makes file system indexer work and create local entries for albums and images

This commit is contained in:
Philipp Häfelfinger 2023-08-30 23:35:20 +02:00
parent 0a25ce2cc0
commit 7e528cd25d
15 changed files with 728 additions and 20 deletions

View File

@ -1,11 +1,15 @@
using System.Threading.Channels;
using System.Security.Cryptography;
using System.Threading.Channels;
using Microsoft.EntityFrameworkCore;
using NLog;
using PiwigoDirectorySync.Persistence;
namespace PiwigoDirectorySync.Commands.Scan;
public class FileIndexer
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
private readonly IList<string> _failedFiles = new List<string>();
private readonly Channel<string> _fileQueue;
private readonly int _piwigoServerId;
@ -17,20 +21,119 @@ public class FileIndexer
}
public int TotalFilesScanned { get; private set; }
public IReadOnlyCollection<string> FailedFiles => _failedFiles.AsReadOnly();
public async Task StartProcessingAsync(CancellationToken ct)
{
await foreach (var filePath in _fileQueue.Reader.ReadAllAsync(ct))
await using var db = new PersistenceContext();
var piwigoServer = await db.PiwigoServers.GetByIdAsync(_piwigoServerId, ct);
await foreach (var fullFilePath in _fileQueue.Reader.ReadAllAsync(ct))
{
try
{
Logger.Info($"Indexing file {filePath}");
if (ct.IsCancellationRequested)
{
Logger.Warn("Indexing cancelled");
break;
}
Logger.Info($"Indexing file {fullFilePath}");
var fileInfo = new FileInfo(fullFilePath);
if (!fileInfo.Exists)
{
Logger.Warn($"File {fullFilePath} not found");
_failedFiles.Add(fullFilePath);
continue;
}
var relativePath = Path.GetRelativePath(piwigoServer.RootDirectory, fullFilePath);
var album = await GetOrAddAlbumAsync(db, piwigoServer, fileInfo.Directory!, ct);
var image = await GetOrAddImageAsync(db, album, relativePath, ct);
if (image.LastChange != fileInfo.LastWriteTimeUtc)
{
image.UploadRequired = true;
image.Md5Sum = await CalculateMd5SumAsync(fullFilePath, ct);
}
image.DeleteRequired = false;
image.LastChange = fileInfo.LastWriteTimeUtc;
await db.SaveChangesAsync(ct);
TotalFilesScanned++;
}
catch (Exception ex)
{
Logger.Error(ex, $"could not delete file {filePath}");
_failedFiles.Add(fullFilePath);
Logger.Error(ex, $"could not delete file {fullFilePath}");
}
}
}
private static async Task<ImageEntity> GetOrAddImageAsync(PersistenceContext db, AlbumEntity album, string relativePath, CancellationToken ct)
{
var imageEntity = await db.PiwigoImages.Where(i => i.AlbumId == album.Id && i.FilePath == relativePath).FirstOrDefaultAsync(ct);
if (imageEntity is null)
{
imageEntity = new ImageEntity
{
AlbumId = album.Id,
Album = album,
FilePath = relativePath,
UploadRequired = true,
DeleteRequired = false
};
db.PiwigoImages.Add(imageEntity);
}
return imageEntity;
}
private async Task<AlbumEntity> GetOrAddAlbumAsync(PersistenceContext db, ServerEntity server, DirectoryInfo directory, CancellationToken ct)
{
var albumPath = Path.GetRelativePath(server.RootDirectory, directory.FullName);
var album = await db.PiwigoAlbums.FindByServerAndPathAsync(server.Id, albumPath, ct);
if (album != null)
{
return album;
}
AlbumEntity? parentAlbum;
if (string.Equals(new DirectoryInfo(server.RootDirectory).FullName, directory.Parent!.FullName))
{
parentAlbum = null;
}
else
{
parentAlbum = await GetOrAddAlbumAsync(db, server, directory.Parent!, ct);
}
album = new AlbumEntity
{
ServerId = server.Id,
Server = server,
Name = directory.Name,
Path = albumPath,
ParentId = parentAlbum?.Id,
Parent = parentAlbum
};
db.PiwigoAlbums.Add(album);
await db.SaveChangesAsync(ct);
return album;
}
private static async Task<string> CalculateMd5SumAsync(string imageFileFullPath, CancellationToken stoppingToken)
{
using var md5 = MD5.Create();
await using var stream = File.OpenRead(imageFileFullPath);
var hash = await md5.ComputeHashAsync(stream, stoppingToken);
return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
}
}

View File

@ -11,6 +11,8 @@ public class ScanCommand : AsyncCommand<ScanSettings>
public override async Task<int> ExecuteAsync(CommandContext context, ScanSettings settings)
{
//TODO: check files for deletion -> files in db but no longer exist
Logger.Info("Starting scanner and remover");
var stopWatch = Stopwatch.StartNew();

View File

@ -0,0 +1,152 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using PiwigoDirectorySync.Persistence;
#nullable disable
namespace PiwigoDirectorySync.Migrations
{
[DbContext(typeof(PersistenceContext))]
[Migration("20230830212128_Update_path_handling")]
partial class Update_path_handling
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.10");
modelBuilder.Entity("PiwigoDirectorySync.Persistence.AlbumEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("ParentId")
.HasColumnType("INTEGER");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("PiwigoAlbumId")
.HasColumnType("INTEGER");
b.Property<int>("ServerId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ParentId");
b.HasIndex("PiwigoAlbumId");
b.HasIndex("ServerId");
b.ToTable("PiwigoAlbums");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ImageEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AlbumId")
.HasColumnType("INTEGER");
b.Property<bool>("DeleteRequired")
.HasColumnType("INTEGER");
b.Property<string>("FilePath")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Md5Sum")
.HasColumnType("TEXT");
b.Property<int?>("ServerImageId")
.HasColumnType("INTEGER");
b.Property<bool>("UploadRequired")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("AlbumId");
b.ToTable("PiwigoImages");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ServerEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RootDirectory")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("PiwigoServers");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.AlbumEntity", b =>
{
b.HasOne("PiwigoDirectorySync.Persistence.AlbumEntity", "Parent")
.WithMany()
.HasForeignKey("ParentId");
b.HasOne("PiwigoDirectorySync.Persistence.ServerEntity", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Parent");
b.Navigation("Server");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ImageEntity", b =>
{
b.HasOne("PiwigoDirectorySync.Persistence.AlbumEntity", "Album")
.WithMany()
.HasForeignKey("AlbumId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Album");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PiwigoDirectorySync.Migrations
{
/// <inheritdoc />
public partial class Update_path_handling : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "DirectoryName",
table: "PiwigoAlbums",
newName: "Path");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Path",
table: "PiwigoAlbums",
newName: "DirectoryName");
}
}
}

View File

@ -0,0 +1,154 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using PiwigoDirectorySync.Persistence;
#nullable disable
namespace PiwigoDirectorySync.Migrations
{
[DbContext(typeof(PersistenceContext))]
[Migration("20230830212412_Update_indices_and_rename_piwigoServerAlbumId")]
partial class Update_indices_and_rename_piwigoServerAlbumId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.10");
modelBuilder.Entity("PiwigoDirectorySync.Persistence.AlbumEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("ParentId")
.HasColumnType("INTEGER");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("ServerAlbumId")
.HasColumnType("INTEGER");
b.Property<int>("ServerId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ParentId");
b.HasIndex("ServerAlbumId");
b.HasIndex("ServerId");
b.ToTable("PiwigoAlbums");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ImageEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AlbumId")
.HasColumnType("INTEGER");
b.Property<bool>("DeleteRequired")
.HasColumnType("INTEGER");
b.Property<string>("FilePath")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Md5Sum")
.HasColumnType("TEXT");
b.Property<int?>("ServerImageId")
.HasColumnType("INTEGER");
b.Property<bool>("UploadRequired")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ServerImageId");
b.HasIndex("AlbumId", "FilePath");
b.ToTable("PiwigoImages");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ServerEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RootDirectory")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("PiwigoServers");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.AlbumEntity", b =>
{
b.HasOne("PiwigoDirectorySync.Persistence.AlbumEntity", "Parent")
.WithMany()
.HasForeignKey("ParentId");
b.HasOne("PiwigoDirectorySync.Persistence.ServerEntity", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Parent");
b.Navigation("Server");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ImageEntity", b =>
{
b.HasOne("PiwigoDirectorySync.Persistence.AlbumEntity", "Album")
.WithMany()
.HasForeignKey("AlbumId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Album");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,65 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PiwigoDirectorySync.Migrations
{
/// <inheritdoc />
public partial class Update_indices_and_rename_piwigoServerAlbumId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_PiwigoImages_AlbumId",
table: "PiwigoImages");
migrationBuilder.RenameColumn(
name: "PiwigoAlbumId",
table: "PiwigoAlbums",
newName: "ServerAlbumId");
migrationBuilder.RenameIndex(
name: "IX_PiwigoAlbums_PiwigoAlbumId",
table: "PiwigoAlbums",
newName: "IX_PiwigoAlbums_ServerAlbumId");
migrationBuilder.CreateIndex(
name: "IX_PiwigoImages_AlbumId_FilePath",
table: "PiwigoImages",
columns: new[] { "AlbumId", "FilePath" });
migrationBuilder.CreateIndex(
name: "IX_PiwigoImages_ServerImageId",
table: "PiwigoImages",
column: "ServerImageId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_PiwigoImages_AlbumId_FilePath",
table: "PiwigoImages");
migrationBuilder.DropIndex(
name: "IX_PiwigoImages_ServerImageId",
table: "PiwigoImages");
migrationBuilder.RenameColumn(
name: "ServerAlbumId",
table: "PiwigoAlbums",
newName: "PiwigoAlbumId");
migrationBuilder.RenameIndex(
name: "IX_PiwigoAlbums_ServerAlbumId",
table: "PiwigoAlbums",
newName: "IX_PiwigoAlbums_PiwigoAlbumId");
migrationBuilder.CreateIndex(
name: "IX_PiwigoImages_AlbumId",
table: "PiwigoImages",
column: "AlbumId");
}
}
}

View File

@ -0,0 +1,158 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using PiwigoDirectorySync.Persistence;
#nullable disable
namespace PiwigoDirectorySync.Migrations
{
[DbContext(typeof(PersistenceContext))]
[Migration("20230830213341_Adds_directory_name_to_Album")]
partial class Adds_directory_name_to_Album
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.10");
modelBuilder.Entity("PiwigoDirectorySync.Persistence.AlbumEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("ParentId")
.HasColumnType("INTEGER");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("ServerAlbumId")
.HasColumnType("INTEGER");
b.Property<int>("ServerId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ParentId");
b.HasIndex("ServerAlbumId");
b.HasIndex("ServerId");
b.ToTable("PiwigoAlbums");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ImageEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AlbumId")
.HasColumnType("INTEGER");
b.Property<bool>("DeleteRequired")
.HasColumnType("INTEGER");
b.Property<string>("FilePath")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Md5Sum")
.HasColumnType("TEXT");
b.Property<int?>("ServerImageId")
.HasColumnType("INTEGER");
b.Property<bool>("UploadRequired")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ServerImageId");
b.HasIndex("AlbumId", "FilePath");
b.ToTable("PiwigoImages");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ServerEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RootDirectory")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("PiwigoServers");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.AlbumEntity", b =>
{
b.HasOne("PiwigoDirectorySync.Persistence.AlbumEntity", "Parent")
.WithMany()
.HasForeignKey("ParentId");
b.HasOne("PiwigoDirectorySync.Persistence.ServerEntity", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Parent");
b.Navigation("Server");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ImageEntity", b =>
{
b.HasOne("PiwigoDirectorySync.Persistence.AlbumEntity", "Album")
.WithMany()
.HasForeignKey("AlbumId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Album");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PiwigoDirectorySync.Migrations
{
/// <inheritdoc />
public partial class Adds_directory_name_to_Album : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Name",
table: "PiwigoAlbums",
type: "TEXT",
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Name",
table: "PiwigoAlbums");
}
}
}

View File

@ -23,14 +23,18 @@ namespace PiwigoDirectorySync.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("DirectoryName")
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("ParentId")
.HasColumnType("INTEGER");
b.Property<int?>("PiwigoAlbumId")
b.Property<string>("Path")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("ServerAlbumId")
.HasColumnType("INTEGER");
b.Property<int>("ServerId")
@ -40,7 +44,7 @@ namespace PiwigoDirectorySync.Migrations
b.HasIndex("ParentId");
b.HasIndex("PiwigoAlbumId");
b.HasIndex("ServerAlbumId");
b.HasIndex("ServerId");
@ -77,12 +81,14 @@ namespace PiwigoDirectorySync.Migrations
b.HasKey("Id");
b.HasIndex("AlbumId");
b.HasIndex("ServerImageId");
b.HasIndex("AlbumId", "FilePath");
b.ToTable("PiwigoImages");
});
modelBuilder.Entity("PiwigoDirectorySync.Persistence.PiwigoServerEntity", b =>
modelBuilder.Entity("PiwigoDirectorySync.Persistence.ServerEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -122,7 +128,7 @@ namespace PiwigoDirectorySync.Migrations
.WithMany()
.HasForeignKey("ParentId");
b.HasOne("PiwigoDirectorySync.Persistence.PiwigoServerEntity", "Server")
b.HasOne("PiwigoDirectorySync.Persistence.ServerEntity", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade)

View File

@ -5,22 +5,21 @@ using Microsoft.EntityFrameworkCore;
namespace PiwigoDirectorySync.Persistence;
[Index(nameof(ParentId))]
[Index(nameof(PiwigoAlbumId))]
[Index(nameof(ServerAlbumId))]
public class AlbumEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int? PiwigoAlbumId { get; set; }
public required string DirectoryName { get; set; }
public int? ServerAlbumId { get; set; }
public required string Name { get; set; }
public required string Path { get; set; }
public int? ParentId { get; set; }
public AlbumEntity? Parent { get; set; }
public required int ServerId { get; set; }
public PiwigoServerEntity Server { get; set; } = null!;
public ServerEntity Server { get; set; } = null!;
public string FullPath =>
Parent is not null ? $"{Parent.FullPath}{Path.DirectorySeparatorChar}{DirectoryName}" : $"{Server.RootDirectory}{Path.DirectorySeparatorChar}{DirectoryName}";
}

View File

@ -4,8 +4,13 @@ namespace PiwigoDirectorySync.Persistence;
public static class ExtensionMethods
{
public static Task<PiwigoServerEntity> GetByIdAsync(this DbSet<PiwigoServerEntity> dbSet, int serverId, CancellationToken ct)
public static Task<ServerEntity> GetByIdAsync(this DbSet<ServerEntity> dbSet, int serverId, CancellationToken ct)
{
return dbSet.Where(a => a.Id == serverId).FirstAsync(ct);
}
public static async Task<AlbumEntity?> FindByServerAndPathAsync(this DbSet<AlbumEntity> dbSet, int serverId, string relativePath, CancellationToken ct)
{
return await dbSet.Where(a => a.ServerId == serverId && a.Path == relativePath).FirstOrDefaultAsync(ct);
}
}

View File

@ -4,7 +4,8 @@ using Microsoft.EntityFrameworkCore;
namespace PiwigoDirectorySync.Persistence;
[Index(nameof(AlbumId))]
[Index(nameof(AlbumId), nameof(FilePath))]
[Index(nameof(ServerImageId))]
public class ImageEntity
{
[Key]

View File

@ -4,7 +4,7 @@ namespace PiwigoDirectorySync.Persistence;
public class PersistenceContext : DbContext
{
public DbSet<PiwigoServerEntity> PiwigoServers { get; set; } = null!;
public DbSet<ServerEntity> PiwigoServers { get; set; } = null!;
public DbSet<AlbumEntity> PiwigoAlbums { get; set; } = null!;
public DbSet<ImageEntity> PiwigoImages { get; set; } = null!;

View File

@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore;
namespace PiwigoDirectorySync.Persistence;
[Index(nameof(Name), IsUnique = true)]
public class PiwigoServerEntity
public class ServerEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

View File

@ -6,6 +6,12 @@
"commandLineArgs": "scan 1",
"environmentVariables": {
}
},
"PiwigoDirectorySyncPng": {
"commandName": "Project",
"commandLineArgs": "scan 2",
"environmentVariables": {
}
}
}
}