Infrastructure upgrades
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.8" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DeepDrftWeb\DeepDrftWeb.csproj" />
|
||||||
|
<ProjectReference Include="..\DeepDrftContent\DeepDrftContent.csproj" />
|
||||||
|
<ProjectReference Include="..\DeepDrftModels\DeepDrftModels.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using DeepDrftWeb.Data;
|
||||||
|
using DeepDrftWeb.Data.Repositories;
|
||||||
|
using DeepDrftContent.FileDatabase.Services;
|
||||||
|
using DeepDrftContent.Processors;
|
||||||
|
using DeepDrftContent.Services;
|
||||||
|
using DeepDrftCli.Services;
|
||||||
|
|
||||||
|
var builder = Host.CreateApplicationBuilder(args);
|
||||||
|
|
||||||
|
// Add configuration
|
||||||
|
var appDirectory = AppContext.BaseDirectory;
|
||||||
|
var configPath = Path.Combine(appDirectory, "appsettings.json");
|
||||||
|
builder.Configuration.AddJsonFile(configPath, optional: false, reloadOnChange: true);
|
||||||
|
|
||||||
|
// Add logging
|
||||||
|
builder.Services.AddLogging(configure => configure.AddConsole());
|
||||||
|
|
||||||
|
// Add database context
|
||||||
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||||
|
if (string.IsNullOrEmpty(connectionString))
|
||||||
|
throw new InvalidOperationException("DefaultConnection not found in configuration");
|
||||||
|
|
||||||
|
builder.Services.AddDbContext<DeepDrftContext>(options =>
|
||||||
|
options.UseSqlite(connectionString));
|
||||||
|
|
||||||
|
// Add FileDatabase
|
||||||
|
builder.Services.AddSingleton<FileDatabase>(provider =>
|
||||||
|
{
|
||||||
|
var logger = provider.GetRequiredService<ILogger<Program>>();
|
||||||
|
var configuration = provider.GetRequiredService<IConfiguration>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var vaultPath = configuration["FileDatabaseSettings:VaultPath"];
|
||||||
|
if (string.IsNullOrEmpty(vaultPath))
|
||||||
|
throw new InvalidOperationException("FileDatabaseSettings:VaultPath not found in configuration");
|
||||||
|
|
||||||
|
var fileDatabase = FileDatabase.FromAsync(vaultPath).GetAwaiter().GetResult();
|
||||||
|
if (fileDatabase == null)
|
||||||
|
{
|
||||||
|
logger.LogError("Failed to initialize FileDatabase");
|
||||||
|
throw new InvalidOperationException("FileDatabase initialization failed");
|
||||||
|
}
|
||||||
|
return fileDatabase;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Error initializing FileDatabase");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add services
|
||||||
|
builder.Services.AddScoped<TrackRepository>();
|
||||||
|
builder.Services.AddScoped<DeepDrftWeb.Services.TrackService>();
|
||||||
|
builder.Services.AddScoped<AudioProcessor>();
|
||||||
|
builder.Services.AddScoped<DeepDrftContent.Services.TrackService>();
|
||||||
|
builder.Services.AddScoped<CliService>();
|
||||||
|
|
||||||
|
// Build and run
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Get the CLI service and run
|
||||||
|
var cliService = app.Services.GetRequiredService<CliService>();
|
||||||
|
await cliService.RunAsync(args);
|
||||||
@@ -0,0 +1,438 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using DeepDrftWeb.Data.Repositories;
|
||||||
|
using DeepDrftContent.Services;
|
||||||
|
using DeepDrftModels.Entities;
|
||||||
|
using NetBlocks.Models;
|
||||||
|
|
||||||
|
namespace DeepDrftCli.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Main CLI service for handling command-line operations
|
||||||
|
/// </summary>
|
||||||
|
public class CliService
|
||||||
|
{
|
||||||
|
private readonly ILogger<CliService> _logger;
|
||||||
|
private readonly TrackRepository _trackRepository;
|
||||||
|
private readonly DeepDrftWeb.Services.TrackService _webTrackService;
|
||||||
|
private readonly DeepDrftContent.Services.TrackService _contentTrackService;
|
||||||
|
|
||||||
|
public CliService(
|
||||||
|
ILogger<CliService> logger,
|
||||||
|
TrackRepository trackRepository,
|
||||||
|
DeepDrftWeb.Services.TrackService webTrackService,
|
||||||
|
DeepDrftContent.Services.TrackService contentTrackService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_trackRepository = trackRepository;
|
||||||
|
_webTrackService = webTrackService;
|
||||||
|
_contentTrackService = contentTrackService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Main entry point for CLI operations
|
||||||
|
/// </summary>
|
||||||
|
public async Task RunAsync(string[] args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (args.Length == 0)
|
||||||
|
{
|
||||||
|
ShowHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = args[0].ToLowerInvariant();
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case "add":
|
||||||
|
await HandleAddCommand(args);
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
await HandleListCommand();
|
||||||
|
break;
|
||||||
|
case "help":
|
||||||
|
case "--help":
|
||||||
|
case "-h":
|
||||||
|
ShowHelp();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine($"Unknown command: {command}");
|
||||||
|
ShowHelp();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "CLI operation failed");
|
||||||
|
Console.WriteLine($"Error: {ex.Message}");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the add command to add a new track
|
||||||
|
/// </summary>
|
||||||
|
private async Task HandleAddCommand(string[] args)
|
||||||
|
{
|
||||||
|
// Check if we have at least the command and file path
|
||||||
|
if (args.Length < 2)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error: WAV file path is required.");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Usage: DeepDrftCli add <wav-file-path> [-i|--interactive] [track-name] [artist] [album] [genre] [release-date]");
|
||||||
|
Console.WriteLine(" DeepDrftCli add <wav-file-path> -i (interactive mode)");
|
||||||
|
Console.WriteLine("Example: DeepDrftCli add \"song.wav\" \"My Song\" \"Artist Name\" \"Album Name\" \"Rock\" \"2024-01-01\"");
|
||||||
|
Console.WriteLine("Example: DeepDrftCli add \"song.wav\" --interactive");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wavFilePath = args[1];
|
||||||
|
|
||||||
|
// Validate that the file path is not a flag
|
||||||
|
if (wavFilePath.StartsWith("-"))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error: WAV file path is required and cannot be a flag.");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Usage: DeepDrftCli add <wav-file-path> [-i|--interactive] [track-name] [artist] [album] [genre] [release-date]");
|
||||||
|
Console.WriteLine(" DeepDrftCli add <wav-file-path> -i (interactive mode)");
|
||||||
|
Console.WriteLine("Example: DeepDrftCli add \"song.wav\" \"My Song\" \"Artist Name\" \"Album Name\" \"Rock\" \"2024-01-01\"");
|
||||||
|
Console.WriteLine("Example: DeepDrftCli add \"song.wav\" --interactive");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isInteractive = args.Contains("-i") || args.Contains("--interactive");
|
||||||
|
|
||||||
|
// Filter out the interactive flags from args for processing
|
||||||
|
var filteredArgs = args.Where(arg => arg != "-i" && arg != "--interactive").ToArray();
|
||||||
|
|
||||||
|
string trackName;
|
||||||
|
string artist;
|
||||||
|
string? album;
|
||||||
|
string? genre;
|
||||||
|
DateOnly? releaseDate = null;
|
||||||
|
|
||||||
|
if (isInteractive)
|
||||||
|
{
|
||||||
|
// Interactive mode - prompt for metadata
|
||||||
|
var metadata = PromptForMetadata(wavFilePath, filteredArgs);
|
||||||
|
trackName = metadata.TrackName;
|
||||||
|
artist = metadata.Artist;
|
||||||
|
album = metadata.Album;
|
||||||
|
genre = metadata.Genre;
|
||||||
|
releaseDate = metadata.ReleaseDate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Traditional command-line mode
|
||||||
|
if (filteredArgs.Length < 4)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Usage: DeepDrftCli add <wav-file-path> <track-name> <artist> [album] [genre] [release-date]");
|
||||||
|
Console.WriteLine(" DeepDrftCli add <wav-file-path> -i (interactive mode)");
|
||||||
|
Console.WriteLine("Example: DeepDrftCli add \"song.wav\" \"My Song\" \"Artist Name\" \"Album Name\" \"Rock\" \"2024-01-01\"");
|
||||||
|
Console.WriteLine("Example: DeepDrftCli add \"song.wav\" --interactive");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trackName = filteredArgs[2];
|
||||||
|
artist = filteredArgs[3];
|
||||||
|
album = filteredArgs.Length > 4 ? filteredArgs[4] : null;
|
||||||
|
genre = filteredArgs.Length > 5 ? filteredArgs[5] : null;
|
||||||
|
|
||||||
|
if (filteredArgs.Length > 6 && DateOnly.TryParse(filteredArgs[6], out var parsedDate))
|
||||||
|
{
|
||||||
|
releaseDate = parsedDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Adding track: {trackName} by {artist}");
|
||||||
|
Console.WriteLine($"Processing WAV file: {wavFilePath}");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Initialize tracks vault if needed
|
||||||
|
await _contentTrackService.InitializeTracksVaultAsync();
|
||||||
|
|
||||||
|
// Add track to FileDatabase and get entity
|
||||||
|
var trackEntity = await _contentTrackService.AddTrackFromWavAsync(
|
||||||
|
wavFilePath, trackName, artist, album, genre, releaseDate);
|
||||||
|
|
||||||
|
if (trackEntity == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to process audio file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add track to SQL database
|
||||||
|
var result = await _webTrackService.Create(trackEntity);
|
||||||
|
if (result.Success && result.Value != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"✓ Track added successfully!");
|
||||||
|
Console.WriteLine($" ID: {result.Value.Id}");
|
||||||
|
Console.WriteLine($" Name: {result.Value.TrackName}");
|
||||||
|
Console.WriteLine($" Artist: {result.Value.Artist}");
|
||||||
|
Console.WriteLine($" Album: {result.Value.Album ?? "N/A"}");
|
||||||
|
Console.WriteLine($" Genre: {result.Value.Genre ?? "N/A"}");
|
||||||
|
Console.WriteLine($" Release Date: {result.Value.ReleaseDate?.ToString() ?? "N/A"}");
|
||||||
|
Console.WriteLine($" Entry Key: {result.Value.EntryKey}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var errorMessage = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||||
|
Console.WriteLine($"Failed to save track to database: {errorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: WAV file not found: {wavFilePath}");
|
||||||
|
}
|
||||||
|
catch (ArgumentException ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error adding track: {ex.Message}");
|
||||||
|
_logger.LogError(ex, "Failed to add track");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the list command to show all tracks
|
||||||
|
/// </summary>
|
||||||
|
private async Task HandleListCommand()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine("Retrieving tracks from database...");
|
||||||
|
|
||||||
|
var result = await _webTrackService.GetAll();
|
||||||
|
if (!result.Success || result.Value == null)
|
||||||
|
{
|
||||||
|
var errorMessage = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||||
|
Console.WriteLine($"Failed to retrieve tracks: {errorMessage}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tracks = result.Value;
|
||||||
|
if (tracks.Count == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No tracks found in database.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"\nFound {tracks.Count} tracks:");
|
||||||
|
Console.WriteLine(new string('-', 80));
|
||||||
|
Console.WriteLine($"{"ID",-5} {"Name",-25} {"Artist",-20} {"Album",-15} {"Genre",-10}");
|
||||||
|
Console.WriteLine(new string('-', 80));
|
||||||
|
|
||||||
|
foreach (var track in tracks)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{track.Id,-5} {TruncateString(track.TrackName, 25),-25} {TruncateString(track.Artist, 20),-20} {TruncateString(track.Album ?? "", 15),-15} {TruncateString(track.Genre ?? "", 10),-10}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error listing tracks: {ex.Message}");
|
||||||
|
_logger.LogError(ex, "Failed to list tracks");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows help information
|
||||||
|
/// </summary>
|
||||||
|
private void ShowHelp()
|
||||||
|
{
|
||||||
|
Console.WriteLine("DeepDrft CLI - Audio Track Management Tool");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Commands:");
|
||||||
|
Console.WriteLine(" add <wav-file> <track-name> <artist> [album] [genre] [release-date]");
|
||||||
|
Console.WriteLine(" - Adds a WAV file to both SQL and FileDatabase");
|
||||||
|
Console.WriteLine(" - Example: DeepDrftCli add \"song.wav\" \"My Song\" \"Artist\" \"Album\" \"Rock\" \"2024-01-01\"");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine(" add <wav-file> -i|--interactive [track-name] [artist] [album] [genre] [release-date]");
|
||||||
|
Console.WriteLine(" - Adds a WAV file with interactive metadata prompts");
|
||||||
|
Console.WriteLine(" - Any provided command-line arguments will be used as defaults");
|
||||||
|
Console.WriteLine(" - Example: DeepDrftCli add \"song.wav\" -i");
|
||||||
|
Console.WriteLine(" - Example: DeepDrftCli add \"song.wav\" --interactive \"My Song\"");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine(" list");
|
||||||
|
Console.WriteLine(" - Lists all tracks in the database");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine(" help");
|
||||||
|
Console.WriteLine(" - Shows this help information");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Interactive Mode Features:");
|
||||||
|
Console.WriteLine(" - Prompts for each metadata field individually");
|
||||||
|
Console.WriteLine(" - Shows file name being processed");
|
||||||
|
Console.WriteLine(" - Supports default values and fallback to command-line args");
|
||||||
|
Console.WriteLine(" - Required fields: Track Name, Artist");
|
||||||
|
Console.WriteLine(" - Optional fields: Album, Genre, Release Date");
|
||||||
|
Console.WriteLine(" - Summary confirmation before proceeding");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Notes:");
|
||||||
|
Console.WriteLine(" - Only WAV files are supported");
|
||||||
|
Console.WriteLine(" - Release date format: YYYY-MM-DD");
|
||||||
|
Console.WriteLine(" - Arguments with spaces should be quoted");
|
||||||
|
Console.WriteLine(" - Use * to indicate required fields in interactive mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Truncates a string to fit within specified length
|
||||||
|
/// </summary>
|
||||||
|
private string TruncateString(string input, int maxLength)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return input.Length <= maxLength ? input : input.Substring(0, maxLength - 3) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prompts user for track metadata interactively
|
||||||
|
/// </summary>
|
||||||
|
private TrackMetadata PromptForMetadata(string wavFilePath, string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("=== Interactive Metadata Entry ===");
|
||||||
|
Console.WriteLine($"Processing file: {Path.GetFileName(wavFilePath)}");
|
||||||
|
Console.WriteLine("Press Enter to use default values or skip optional fields.");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
// Check if any metadata was provided via command line (fallback support)
|
||||||
|
var trackName = args.Length > 2 ? args[2] : null;
|
||||||
|
var artist = args.Length > 3 ? args[3] : null;
|
||||||
|
var album = args.Length > 4 ? args[4] : null;
|
||||||
|
var genre = args.Length > 5 ? args[5] : null;
|
||||||
|
DateOnly? releaseDate = null;
|
||||||
|
if (args.Length > 6 && DateOnly.TryParse(args[6], out var parsedDate))
|
||||||
|
releaseDate = parsedDate;
|
||||||
|
|
||||||
|
// Prompt for track name (required)
|
||||||
|
trackName ??= PromptForInput("Track Name", required: true);
|
||||||
|
|
||||||
|
// Prompt for artist (required)
|
||||||
|
artist ??= PromptForInput("Artist", required: true);
|
||||||
|
|
||||||
|
// Prompt for album (optional)
|
||||||
|
album ??= PromptForInput("Album", defaultValue: album);
|
||||||
|
|
||||||
|
// Prompt for genre (optional)
|
||||||
|
genre ??= PromptForInput("Genre", defaultValue: genre);
|
||||||
|
|
||||||
|
// Prompt for release date (optional)
|
||||||
|
if (releaseDate == null)
|
||||||
|
{
|
||||||
|
var releaseDateInput = PromptForInput("Release Date (YYYY-MM-DD)", defaultValue: releaseDate?.ToString());
|
||||||
|
if (!string.IsNullOrWhiteSpace(releaseDateInput) && DateOnly.TryParse(releaseDateInput, out var newReleaseDate))
|
||||||
|
{
|
||||||
|
releaseDate = newReleaseDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("=== Summary ===");
|
||||||
|
Console.WriteLine($"Track Name: {trackName}");
|
||||||
|
Console.WriteLine($"Artist: {artist}");
|
||||||
|
Console.WriteLine($"Album: {album ?? "N/A"}");
|
||||||
|
Console.WriteLine($"Genre: {genre ?? "N/A"}");
|
||||||
|
Console.WriteLine($"Release Date: {releaseDate?.ToString() ?? "N/A"}");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
if (!ConfirmProceed("Proceed with these details?"))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Operation cancelled.");
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TrackMetadata
|
||||||
|
{
|
||||||
|
TrackName = trackName,
|
||||||
|
Artist = artist,
|
||||||
|
Album = album,
|
||||||
|
Genre = genre,
|
||||||
|
ReleaseDate = releaseDate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prompts user for a single input field
|
||||||
|
/// </summary>
|
||||||
|
private string PromptForInput(string fieldName, bool required = false, string? defaultValue = null)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var prompt = $"{fieldName}";
|
||||||
|
if (!string.IsNullOrWhiteSpace(defaultValue))
|
||||||
|
prompt += $" [{defaultValue}]";
|
||||||
|
if (required)
|
||||||
|
prompt += " *";
|
||||||
|
prompt += ": ";
|
||||||
|
|
||||||
|
Console.Write(prompt);
|
||||||
|
var input = Console.ReadLine();
|
||||||
|
|
||||||
|
// Handle null input (Ctrl+C scenario)
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Operation cancelled.");
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
input = input.Trim();
|
||||||
|
|
||||||
|
// Use default value if input is empty and default exists
|
||||||
|
if (string.IsNullOrWhiteSpace(input) && !string.IsNullOrWhiteSpace(defaultValue))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
// If not required and input is empty, return empty string
|
||||||
|
if (!required && string.IsNullOrWhiteSpace(input))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
// Check if required field is provided
|
||||||
|
if (required && string.IsNullOrWhiteSpace(input))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: {fieldName} is required. Please provide a value.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prompts user for yes/no confirmation
|
||||||
|
/// </summary>
|
||||||
|
private bool ConfirmProceed(string message)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Console.Write($"{message} (y/n): ");
|
||||||
|
var input = Console.ReadKey();
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
switch (input.KeyChar.ToString().ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "y":
|
||||||
|
return true;
|
||||||
|
case "n":
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
Console.WriteLine("Please enter 'y' for yes or 'n' for no.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents track metadata for interactive input
|
||||||
|
/// </summary>
|
||||||
|
private record TrackMetadata
|
||||||
|
{
|
||||||
|
public required string TrackName { get; init; }
|
||||||
|
public required string Artist { get; init; }
|
||||||
|
public string? Album { get; init; }
|
||||||
|
public string? Genre { get; init; }
|
||||||
|
public DateOnly? ReleaseDate { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"System": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "Data Source=F:\\Development\\DeepDrftHome\\Database\\deepdrft.db"
|
||||||
|
},
|
||||||
|
"FileDatabaseSettings": {
|
||||||
|
"VaultPath": "F:\\Development\\DeepDrftHome\\Database\\Vaults"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
public class TrackDto
|
public class TrackDto
|
||||||
{
|
{
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
public string MediaPath { get; set; }
|
public required string EntryKey { get; set; }
|
||||||
public string TrackName { get; set; }
|
public string TrackName { get; set; }
|
||||||
public string Artist { get; set; }
|
public string Artist { get; set; }
|
||||||
public string? Album { get; set; }
|
public string? Album { get; set; }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
public class TrackEntity
|
public class TrackEntity
|
||||||
{
|
{
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
public required string MediaPath { get; set; }
|
public required string EntryKey { get; set; }
|
||||||
public required string TrackName { get; set; }
|
public required string TrackName { get; set; }
|
||||||
public required string Artist { get; set; }
|
public required string Artist { get; set; }
|
||||||
public string? Album { get; set; }
|
public string? Album { get; set; }
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ public class TrackConfiguration : IEntityTypeConfiguration<TrackEntity>
|
|||||||
.HasColumnName("id")
|
.HasColumnName("id")
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
builder.Property(x => x.MediaPath)
|
builder.Property(x => x.EntryKey)
|
||||||
.HasColumnName("media_path")
|
.HasColumnName("entry_key")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(500);
|
.HasMaxLength(100);
|
||||||
|
|
||||||
builder.Property(x => x.TrackName)
|
builder.Property(x => x.TrackName)
|
||||||
.HasColumnName("track_name")
|
.HasColumnName("track_name")
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using DeepDrftWeb.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace DeepDrftWeb.Data.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DeepDrftContext))]
|
|
||||||
[Migration("20250831000622_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>("Genre")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasColumnName("genre");
|
|
||||||
|
|
||||||
b.Property<string>("ImagePath")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasColumnName("image_path");
|
|
||||||
|
|
||||||
b.Property<string>("MediaPath")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasColumnName("media_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.Data.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),
|
|
||||||
media_path = table.Column<string>(type: "TEXT", maxLength: 500, 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.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace DeepDrftWeb.Data.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>("Genre")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasColumnName("genre");
|
|
||||||
|
|
||||||
b.Property<string>("ImagePath")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasColumnName("image_path");
|
|
||||||
|
|
||||||
b.Property<string>("MediaPath")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasColumnName("media_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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -56,7 +56,7 @@ public class TrackRepository
|
|||||||
trackEntity.Artist = track.Artist;
|
trackEntity.Artist = track.Artist;
|
||||||
trackEntity.Genre = track.Genre;
|
trackEntity.Genre = track.Genre;
|
||||||
trackEntity.ImagePath = track.ImagePath;
|
trackEntity.ImagePath = track.ImagePath;
|
||||||
trackEntity.MediaPath = track.MediaPath;
|
trackEntity.EntryKey = track.EntryKey;
|
||||||
trackEntity.ReleaseDate = track.ReleaseDate;
|
trackEntity.ReleaseDate = track.ReleaseDate;
|
||||||
trackEntity.TrackName = track.TrackName;
|
trackEntity.TrackName = track.TrackName;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user