Started the Split

Split Json data models coming from REST Api, from the Database models
storing them in a SQLite database.
Work to re-engineer endpoints to use Database objects, and copy/update
data from the json objects.
More work is needed.
This commit is contained in:
Mario Steele 2025-07-22 17:03:33 -05:00
parent 157d46ee2e
commit 4985dc4179
36 changed files with 684 additions and 323 deletions

View file

@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIEnumerable_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8c1f5b7d48954a608a607b29d36507c2c2ea00_003F47_003F017fc264_003FIEnumerable_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AList_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fe747192abb38e2df82cbdb37e721567726f559914a7b81f8b26ba537de632f4_003FList_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View file

@ -1,4 +1,4 @@
using FreeTubeSync.Model;
using FreeTubeSync.Model.Database;
using Microsoft.EntityFrameworkCore;
namespace FreeTubeSync.Database;
@ -12,25 +12,8 @@ public class DataContext : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Setting>()
.ToTable("Settings")
.HasKey(s => s._id);
modelBuilder.Entity<Setting>()
.Property(s => s.ValueJson)
.HasColumnName("Value")
.IsRequired();
modelBuilder.Entity<Profile>()
.ToTable("Profiles")
.HasKey(s => s._id);
modelBuilder.Entity<Profile>()
.Navigation(e => e.subscriptions).AutoInclude();
modelBuilder.Entity<Playlist>()
.ToTable("Playlists")
.HasKey(s => s._id);
modelBuilder.Entity<Playlist>()
.Navigation(e => e.videos).AutoInclude();

View file

@ -0,0 +1,36 @@
using System.Collections;
namespace FreeTubeSync.Database;
public static class MapData
{
public static void MapFrom(this object obj1, object obj2)
{
var props = new Dictionary<string, object?>();
var t1Type = obj1.GetType();
var t2Type = obj2.GetType();
var properties = t1Type.GetProperties();
foreach (var property in properties)
{
props[property.Name] = property.GetValue(obj1);
}
properties = t2Type.GetProperties();
foreach (var property in properties)
{
if (props[property.Name] is not IEnumerable)
property.SetValue(obj2, props[property.Name]);
}
}
public static void MapTo<T1, T2>(this IEnumerable<T1> obj1, IList<T2> obj2)
where T1 : class where T2 : class, new()
{
foreach (var obj in obj1)
{
var newObj = new T2();
newObj.MapFrom(obj);
obj2.Add(newObj);
}
}
}

View file

@ -1,4 +1,6 @@
using FreeTubeSync.Model;
using FreeTubeSync.Database;
using FreeTubeSync.Model.Database;
using FreeTubeSync.Model.Json;
namespace FreeTubeSync.EndPoints;
@ -11,17 +13,23 @@ public static class HistoryEndpoint
group.MapGet("/", async (IRepository<History> repository, CancellationToken ct) =>
{
var results = await repository.GetAllAsync(ct);
return Results.Ok(results);
var jsonResults = new List<HistoryJson>();
results.MapTo(jsonResults);
return Results.Ok(jsonResults);
});
group.MapPost("/", async (IRepository<History> repository, CancellationToken ct, History history) =>
group.MapPost("/", async (IRepository<History> repository, CancellationToken ct, HistoryJson historyJson) =>
{
var results = await repository.GetByIdAsync(history._id, ct);
var results = await repository.GetByIdAsync(historyJson._id, ct);
if (results == null)
await repository.AddAsync(history, ct);
{
results = new History();
results.MapFrom(historyJson);
await repository.AddAsync(results, ct);
}
else
{
results.Update(history);
results.MapFrom(historyJson);
await repository.UpdateAsync(results, ct);
}
return Results.Ok();

View file

@ -1,4 +1,6 @@
using FreeTubeSync.Model;
using FreeTubeSync.Database;
using FreeTubeSync.Model.Database;
using FreeTubeSync.Model.Json;
namespace FreeTubeSync.EndPoints;
@ -10,18 +12,53 @@ public static class PlaylistEndpoint
group.MapGet("/", async (IRepository<Playlist> repository, CancellationToken ct) =>
{
var results = await repository.GetAllAsync(ct);
return Results.Ok(results);
var results = (await repository.GetAllAsync(ct)).ToList();
var jsonResults = new List<PlaylistJson>();
results.MapTo(jsonResults);
for (var i = 0; i < jsonResults.Count; i++)
results[i].videos.MapTo(jsonResults[i].videos);
return Results.Ok(jsonResults);
});
group.MapPost("/", async (IRepository<Playlist> repository, CancellationToken ct, Playlist playlist) =>
group.MapPost("/", async (IRepository<Playlist> repository, IRepository<Video> vidRepo, CancellationToken ct, PlaylistJson playlistJson) =>
{
var results = await repository.GetByIdAsync(playlist._id, ct);
var results = await repository.GetByIdAsync(playlistJson._id, ct);
if (results == null)
await repository.AddAsync(playlist, ct);
{
results = new Playlist();
results.MapFrom(playlistJson);
foreach (var video in playlistJson.videos)
{
var vid = new Video();
vid.MapFrom(video);
await vidRepo.AddAsync(vid, ct, false);
}
await repository.AddAsync(results, ct);
}
else
{
results.Update(playlist);
results.MapFrom(playlistJson);
var remove = results.videos.Where(video => playlistJson.videos.All(x => x.playlistItemId != video.playlistItemId)).ToList();
var add = playlistJson.videos.Where(video => results.videos.All(x => x.playlistItemId != video.playlistItemId)).ToList();
var vids = new List<Video>();
foreach (var video in remove)
{
await vidRepo.DeleteAsync(video, ct, false);
}
foreach (var video in add)
{
var vid = new Video();
vid.MapFrom(video);
await vidRepo.AddAsync(vid, ct, false);
vids.Add(vid);
}
results.videos.RemoveAll(x => remove.Contains(x));
results.videos.AddRange(vids);
await repository.UpdateAsync(results, ct);
}

View file

@ -1,4 +1,6 @@
using FreeTubeSync.Model;
using FreeTubeSync.Database;
using FreeTubeSync.Model.Database;
using FreeTubeSync.Model.Json;
namespace FreeTubeSync.EndPoints;
@ -10,18 +12,67 @@ public static class ProfileEndpoint
group.MapGet("/", async (IRepository<Profile> repository, CancellationToken ct) =>
{
var results = await repository.GetAllAsync(ct);
var results = (await repository.GetAllAsync(ct)).ToList();
var jsonResults = new List<ProfileJson>();
results.MapTo(jsonResults);
for (var i = 0; i < jsonResults.Count; i++)
results[i].subscriptions.MapTo(jsonResults[i].subscriptions);
return Results.Ok(results);
});
group.MapPost("/", async (IRepository<Profile> repository, CancellationToken ct, Profile profile) =>
group.MapPost("/", async (IRepository<Profile> repository, IRepository<Subscription> subRepo, CancellationToken ct, ProfileJson profileJson) =>
{
var res = await repository.GetByIdAsync(profile._id, ct);
var res = await repository.GetByIdAsync(profileJson._id, ct);
if (res == null)
await repository.AddAsync(profile, ct);
{
res = new Profile();
res.MapFrom(profileJson);
foreach (var subscription in profileJson.subscriptions)
{
var sub = await subRepo.GetByIdAsync(subscription.id, ct);
if (sub == null)
{
sub = new Subscription();
sub.MapFrom(subscription);
await subRepo.AddAsync(sub, ct, false);
}
res.subscriptions.Add(sub);
}
await repository.AddAsync(res, ct);
}
else
{
res.Update(profile);
res.MapFrom(profileJson);
var notFound = new List<Subscription>();
foreach (var subscription in res.subscriptions)
{
var f = profileJson.subscriptions.FirstOrDefault(s => s.id == subscription.id);
if (f == null)
notFound.Add(subscription);
else
subscription.MapFrom(f);
}
var newSubs = (from subscription in profileJson.subscriptions let f = res.subscriptions.FirstOrDefault(s => s.id == subscription.id) where f == null select subscription).ToList();
foreach (var newSub in newSubs)
{
var sres = await subRepo.GetByIdAsync(newSub.id, ct);
if (sres == null)
{
sres = new Subscription();
sres.MapFrom(newSub);
await subRepo.AddAsync(sres, ct, false);
}
res.subscriptions.Add(sres);
}
foreach (var nfSub in notFound)
{
res.subscriptions.Remove(nfSub);
}
await repository.UpdateAsync(res, ct);
}

View file

@ -1,4 +1,6 @@
using FreeTubeSync.Model;
using FreeTubeSync.Database;
using FreeTubeSync.Model.Database;
using FreeTubeSync.Model.Json;
namespace FreeTubeSync.EndPoints;
@ -11,17 +13,23 @@ public static class SearchHistoryEndpoint
group.MapGet("/", async (IRepository<SearchHistory> repository, CancellationToken ct) =>
{
var result = await repository.GetAllAsync(ct);
return Results.Ok(result);
var jsonResults = new List<SearchHistoryJson>();
result.MapTo(jsonResults);
return Results.Ok(jsonResults);
});
group.MapPost("/", async (IRepository<SearchHistory> repository, CancellationToken ct, SearchHistory history) =>
group.MapPost("/", async (IRepository<SearchHistory> repository, CancellationToken ct, SearchHistoryJson historyJson) =>
{
var result = await repository.GetByIdAsync(history._id, ct);
var result = await repository.GetByIdAsync(historyJson._id, ct);
if (result == null)
await repository.AddAsync(history, ct);
{
result = new SearchHistory();
result.MapFrom(historyJson);
await repository.AddAsync(result, ct);
}
else
{
result.Update(history);
result.MapFrom(historyJson);
await repository.UpdateAsync(result, ct);
}

View file

@ -1,5 +1,6 @@
using FreeTubeSync.Model;
using FreeTubeSync.SpecialResponses;
using FreeTubeSync.Database;
using FreeTubeSync.Model.Database;
using FreeTubeSync.Model.Json;
namespace FreeTubeSync.EndPoints;
@ -12,18 +13,23 @@ public static class SettingEndpoint
group.MapGet("/", async (IRepository<Setting> repository, CancellationToken ct) =>
{
var settings = await repository.GetAllAsync(ct);
var response = settings.MapToResponse();
return Results.Ok(response);
var jsonSettings = new List<SettingJson>();
settings.MapTo(jsonSettings);
return Results.Ok(jsonSettings);
});
group.MapPost("/", async (IRepository<Setting> repository, CancellationToken ct, Setting setting) =>
group.MapPost("/", async (IRepository<Setting> repository, CancellationToken ct, SettingJson settingJson) =>
{
var res = await repository.GetByIdAsync(setting._id, ct);
var res = await repository.GetByIdAsync(settingJson._id, ct);
if (res == null)
await repository.AddAsync(setting, ct);
{
res = new Setting();
res.MapFrom(settingJson);
await repository.AddAsync(res, ct);
}
else
{
res.Update(setting);
res.MapFrom(settingJson);
await repository.UpdateAsync(res, ct);
}
return Results.Ok();

View file

@ -2,9 +2,9 @@ namespace FreeTubeSync;
public interface IRepository<TEntity> where TEntity : class
{
Task AddAsync(TEntity entity, CancellationToken ct);
Task UpdateAsync(TEntity entity, CancellationToken ct);
Task DeleteAsync(TEntity entity, CancellationToken ct);
Task AddAsync(TEntity entity, CancellationToken ct, bool sync = true);
Task UpdateAsync(TEntity entity, CancellationToken ct, bool sync = true);
Task DeleteAsync(TEntity entity, CancellationToken ct, bool sync = true);
Task<TEntity?> GetByIdAsync(string id, CancellationToken ct);
Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken ct);
}

View file

@ -0,0 +1,246 @@
// <auto-generated />
using FreeTubeSync.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace FreeTubeSync.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20250722190700_SwitchToSplit")]
partial class SwitchToSplit
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.7");
modelBuilder.Entity("FreeTubeSync.Model.Database.History", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<string>("author")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("authorId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("isLive")
.HasColumnType("INTEGER");
b.Property<string>("lastViewedPlaylistItemId")
.HasColumnType("TEXT");
b.Property<string>("lastViewedPlaylistType")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long>("lengthSeconds")
.HasColumnType("INTEGER");
b.Property<long>("published")
.HasColumnType("INTEGER");
b.Property<long>("timeWatched")
.HasColumnType("INTEGER");
b.Property<string>("title")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("type")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("videoId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long>("viewCount")
.HasColumnType("INTEGER");
b.Property<float>("watchProgress")
.HasColumnType("REAL");
b.HasKey("_id");
b.ToTable("Histories");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.Playlist", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<long>("createdAt")
.HasColumnType("INTEGER");
b.Property<long>("lastUpdatedAt")
.HasColumnType("INTEGER");
b.Property<string>("playlistName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("protected")
.HasColumnType("INTEGER");
b.HasKey("_id");
b.ToTable("Playlists");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.Profile", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<string>("bgColor")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("textColor")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("_id");
b.ToTable("Profiles");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.SearchHistory", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<long>("lastUpdatedAt")
.HasColumnType("INTEGER");
b.HasKey("_id");
b.ToTable("SearchHistories");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.Setting", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<string>("value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("_id");
b.ToTable("Settings");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.Subscription", b =>
{
b.Property<string>("id")
.HasColumnType("TEXT");
b.Property<string>("Profile_id")
.HasColumnType("TEXT");
b.Property<string>("name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("thumbnail")
.HasColumnType("TEXT");
b.HasKey("id");
b.HasIndex("Profile_id");
b.ToTable("Subscriptions");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.Video", b =>
{
b.Property<string>("playlistItemId")
.HasColumnType("TEXT");
b.Property<string>("Playlist_id")
.HasColumnType("TEXT");
b.Property<string>("author")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("authorId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("lengthSeconds")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long>("pubished")
.HasColumnType("INTEGER");
b.Property<long>("timeAdded")
.HasColumnType("INTEGER");
b.Property<string>("title")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("type")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("videoId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("playlistItemId");
b.HasIndex("Playlist_id");
b.ToTable("Videos");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.Subscription", b =>
{
b.HasOne("FreeTubeSync.Model.Database.Profile", null)
.WithMany("subscriptions")
.HasForeignKey("Profile_id");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.Video", b =>
{
b.HasOne("FreeTubeSync.Model.Database.Playlist", null)
.WithMany("videos")
.HasForeignKey("Playlist_id");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.Playlist", b =>
{
b.Navigation("videos");
});
modelBuilder.Entity("FreeTubeSync.Model.Database.Profile", b =>
{
b.Navigation("subscriptions");
});
#pragma warning restore 612, 618
}
}
}

View file

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

View file

@ -16,7 +16,7 @@ namespace FreeTubeSync.Migrations
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.7");
modelBuilder.Entity("FreeTubeSync.Model.History", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.History", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
@ -75,7 +75,7 @@ namespace FreeTubeSync.Migrations
b.ToTable("Histories");
});
modelBuilder.Entity("FreeTubeSync.Model.Playlist", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.Playlist", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
@ -95,10 +95,10 @@ namespace FreeTubeSync.Migrations
b.HasKey("_id");
b.ToTable("Playlists", (string)null);
b.ToTable("Playlists");
});
modelBuilder.Entity("FreeTubeSync.Model.Profile", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.Profile", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
@ -117,10 +117,10 @@ namespace FreeTubeSync.Migrations
b.HasKey("_id");
b.ToTable("Profiles", (string)null);
b.ToTable("Profiles");
});
modelBuilder.Entity("FreeTubeSync.Model.SearchHistory", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.SearchHistory", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
@ -133,22 +133,21 @@ namespace FreeTubeSync.Migrations
b.ToTable("SearchHistories");
});
modelBuilder.Entity("FreeTubeSync.Model.Setting", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.Setting", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<string>("ValueJson")
b.Property<string>("value")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("Value");
.HasColumnType("TEXT");
b.HasKey("_id");
b.ToTable("Settings", (string)null);
b.ToTable("Settings");
});
modelBuilder.Entity("FreeTubeSync.Model.Subscription", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.Subscription", b =>
{
b.Property<string>("id")
.HasColumnType("TEXT");
@ -170,7 +169,7 @@ namespace FreeTubeSync.Migrations
b.ToTable("Subscriptions");
});
modelBuilder.Entity("FreeTubeSync.Model.Video", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.Video", b =>
{
b.Property<string>("playlistItemId")
.HasColumnType("TEXT");
@ -215,26 +214,26 @@ namespace FreeTubeSync.Migrations
b.ToTable("Videos");
});
modelBuilder.Entity("FreeTubeSync.Model.Subscription", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.Subscription", b =>
{
b.HasOne("FreeTubeSync.Model.Profile", null)
b.HasOne("FreeTubeSync.Model.Database.Profile", null)
.WithMany("subscriptions")
.HasForeignKey("Profile_id");
});
modelBuilder.Entity("FreeTubeSync.Model.Video", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.Video", b =>
{
b.HasOne("FreeTubeSync.Model.Playlist", null)
b.HasOne("FreeTubeSync.Model.Database.Playlist", null)
.WithMany("videos")
.HasForeignKey("Playlist_id");
});
modelBuilder.Entity("FreeTubeSync.Model.Playlist", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.Playlist", b =>
{
b.Navigation("videos");
});
modelBuilder.Entity("FreeTubeSync.Model.Profile", b =>
modelBuilder.Entity("FreeTubeSync.Model.Database.Profile", b =>
{
b.Navigation("subscriptions");
});

View file

@ -0,0 +1,23 @@
using System.ComponentModel.DataAnnotations;
namespace FreeTubeSync.Model.Database;
public class History
{
[Key]
public string _id { get; set; } = string.Empty;
public string videoId { get; set; } = string.Empty;
public string title { get; set; } = string.Empty;
public string author { get; set; } = string.Empty;
public string authorId { get; set; } = string.Empty;
public long published { get; set; }
public string description { get; set; } = string.Empty;
public long viewCount { get; set; }
public long lengthSeconds { get; set; }
public float watchProgress { get; set; }
public long timeWatched { get; set; }
public bool isLive { get; set; }
public string type { get; set; } = string.Empty;
public string lastViewedPlaylistType { get; set; } = string.Empty;
public string? lastViewedPlaylistItemId { get; set; }
}

View file

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace FreeTubeSync.Model.Database;
public class Playlist
{
[Key]
public string _id { get; set; } = string.Empty;
public string playlistName { get; set; } = string.Empty;
public bool @protected { get; set; }
public List<Video> videos { get; set; } = [];
public long createdAt { get; set; }
public long lastUpdatedAt { get; set; }
}

View file

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace FreeTubeSync.Model.Database;
public class Profile
{
[Key]
public string _id { get; set; } = string.Empty;
public string name { get; set; } = string.Empty;
public string bgColor { get; set; } = string.Empty;
public string textColor { get; set; } = string.Empty;
public List<Subscription> subscriptions { get; set; } = [];
}

View file

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace FreeTubeSync.Model.Database;
public class SearchHistory
{
[Key]
public string _id { get; set; } = string.Empty;
public long lastUpdatedAt { get; set; }
}

View file

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace FreeTubeSync.Model.Database;
public class Setting
{
[Key]
public string _id { get; set; }
public string value { get; set; }
}

View file

@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace FreeTubeSync.Model.Database;
public class Subscription
{
[Key]
public string id { get; set; } = string.Empty;
public string name { get; set; } = string.Empty;
public string? thumbnail { get; set; }
}

View file

@ -0,0 +1,17 @@
using System.ComponentModel.DataAnnotations;
namespace FreeTubeSync.Model.Database;
public class Video
{
public string videoId { get; set; } = string.Empty;
public string title { get; set; } = string.Empty;
public string author { get; set; } = string.Empty;
public string authorId { get; set; } = string.Empty;
public string lengthSeconds { get; set; } = string.Empty;
public long pubished { get; set; }
public long timeAdded { get; set; }
[Key]
public string playlistItemId { get; set; } = string.Empty;
public string type { get; set; } = string.Empty;
}

View file

@ -1,43 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class History
{
[Key]
public string _id { get; set; } = string.Empty;
public string videoId { get; set; } = string.Empty;
public string title { get; set; } = string.Empty;
public string author { get; set; } = string.Empty;
public string authorId { get; set; } = string.Empty;
public long published { get; set; }
public string description { get; set; } = string.Empty;
public long viewCount { get; set; }
public long lengthSeconds { get; set; }
public float watchProgress { get; set; }
public long timeWatched { get; set; }
public bool isLive { get; set; }
public string type { get; set; } = string.Empty;
public string lastViewedPlaylistType { get; set; } = string.Empty;
public string? lastViewedPlaylistItemId { get; set; }
public void Update(History other)
{
if (other.videoId != videoId) videoId = other.videoId;
if (other.title != title) title = other.title;
if (other.author != author) author = other.author;
if (other.authorId != authorId) authorId = other.authorId;
if (other.published != published) published = other.published;
if (other.description != description) description = other.description;
if (other.viewCount != viewCount) viewCount = other.viewCount;
if (other.lengthSeconds != lengthSeconds) lengthSeconds = other.lengthSeconds;
if (!other.watchProgress.Equals(watchProgress)) watchProgress = other.watchProgress;
if (other.timeWatched != timeWatched) timeWatched = other.timeWatched;
if (other.isLive != isLive) isLive = other.isLive;
if (other.type != type) type = other.type;
if (other.lastViewedPlaylistType != lastViewedPlaylistType) lastViewedPlaylistType = other.lastViewedPlaylistType;
if (other.lastViewedPlaylistItemId != lastViewedPlaylistItemId) lastViewedPlaylistItemId = other.lastViewedPlaylistItemId;
}
}

View file

@ -0,0 +1,23 @@
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model.Json;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class HistoryJson
{
public string _id { get; set; } = string.Empty;
public string videoId { get; set; } = string.Empty;
public string title { get; set; } = string.Empty;
public string author { get; set; } = string.Empty;
public string authorId { get; set; } = string.Empty;
public long published { get; set; }
public string description { get; set; } = string.Empty;
public long viewCount { get; set; }
public long lengthSeconds { get; set; }
public float watchProgress { get; set; }
public long timeWatched { get; set; }
public bool isLive { get; set; }
public string type { get; set; } = string.Empty;
public string lastViewedPlaylistType { get; set; } = string.Empty;
public string? lastViewedPlaylistItemId { get; set; }
}

View file

@ -0,0 +1,14 @@
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model.Json;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class PlaylistJson
{
public string _id { get; set; } = string.Empty;
public string playlistName { get; set; } = string.Empty;
public bool @protected { get; set; }
public List<VideoJson> videos { get; set; } = [];
public long createdAt { get; set; }
public long lastUpdatedAt { get; set; }
}

View file

@ -0,0 +1,13 @@
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model.Json;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class ProfileJson
{
public string _id { get; set; } = string.Empty;
public string name { get; set; } = string.Empty;
public string bgColor { get; set; } = string.Empty;
public string textColor { get; set; } = string.Empty;
public List<SubscriptionJson> subscriptions { get; set; } = [];
}

View file

@ -0,0 +1,10 @@
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model.Json;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class SearchHistoryJson
{
public string _id { get; set; } = string.Empty;
public long lastUpdatedAt { get; set; }
}

View file

@ -0,0 +1,10 @@
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model.Json;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class SettingJson
{
public string _id { get; set; } = string.Empty;
public object value { get; set; }
}

View file

@ -0,0 +1,11 @@
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model.Json;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class SubscriptionJson
{
public string id { get; set; } = string.Empty;
public string name { get; set; } = string.Empty;
public string? thumbnail { get; set; }
}

View file

@ -0,0 +1,18 @@
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model.Json;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class VideoJson
{
public string videoId { get; set; } = string.Empty;
public string title { get; set; } = string.Empty;
public string author { get; set; } = string.Empty;
public string authorId { get; set; } = string.Empty;
public string lengthSeconds { get; set; } = string.Empty;
public long pubished { get; set; }
public long timeAdded { get; set; }
public string playlistItemId { get; set; } = string.Empty;
public string type { get; set; } = string.Empty;
}

View file

@ -1,39 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore.Query.Internal;
namespace FreeTubeSync.Model;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class Playlist
{
[Key]
public string _id { get; set; } = string.Empty;
public string playlistName { get; set; } = string.Empty;
public bool @protected { get; set; }
public List<Video> videos { get; set; } = [];
public long createdAt { get; set; }
public long lastUpdatedAt { get; set; }
public void Update(Playlist other)
{
if (other.playlistName != playlistName) playlistName = other.playlistName;
if (other.@protected != @protected) @protected = other.@protected;
var remove = new List<Video>();
foreach (var vid in videos)
{
if (other.videos.Any(x => x.playlistItemId == vid.playlistItemId)) continue;
remove.Add(vid);
}
videos.RemoveAll(x => remove.Contains(x));
remove.Clear();
foreach (var vid in other.videos)
{
if (videos.Any(x => x.playlistItemId == vid.playlistItemId)) continue;
remove.Add(vid);
}
videos.AddRange(remove);
if (other.createdAt != createdAt) createdAt = other.createdAt;
if (other.lastUpdatedAt != lastUpdatedAt) lastUpdatedAt = other.lastUpdatedAt;
}
}

View file

@ -1,36 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class Profile
{
[Key]
public string _id { get; set; } = string.Empty;
public string name { get; set; } = string.Empty;
public string bgColor { get; set; } = string.Empty;
public string textColor { get; set; } = string.Empty;
public List<Subscription> subscriptions { get; set; } = [];
public void Update(Profile other)
{
if (other.name != name) name = other.name;
if (other.bgColor != bgColor) bgColor = other.bgColor;
if (other.textColor != textColor) textColor = other.textColor;
var remove = new List<Subscription>();
foreach (var sub in subscriptions)
{
if (other.subscriptions.Any(x => x.id == sub.id)) continue;
remove.Add(sub);
}
subscriptions.RemoveAll(x => remove.Contains(x));
remove.Clear();
foreach (var sub in other.subscriptions)
{
if (subscriptions.Any(x => x.id == sub.id)) continue;
remove.Add(sub);
}
subscriptions.AddRange(remove);
}
}

View file

@ -1,17 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class SearchHistory
{
[Key]
public string _id { get; set; } = string.Empty;
public long lastUpdatedAt { get; set; }
public void Update(SearchHistory other)
{
if (other.lastUpdatedAt != lastUpdatedAt) lastUpdatedAt = other.lastUpdatedAt;
}
}

View file

@ -1,30 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
namespace FreeTubeSync.Model;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class Setting
{
[Key]
#pragma warning disable CS8618
public string _id { get; set; } = string.Empty;
public string? ValueJson { get; set; }
#pragma warning restore CS8618
[NotMapped]
public object Value
{
#pragma warning disable CS8603
get => string.IsNullOrEmpty(ValueJson) ? null : JsonSerializer.Deserialize<object>(ValueJson);
#pragma warning restore CS8603
set => ValueJson = JsonSerializer.Serialize(value);
}
public void Update(Setting other)
{
Value = other.Value;
}
}

View file

@ -1,19 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace FreeTubeSync.Model;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class Subscription
{
[Key]
public required string id { get; set; }
public required string name { get; set; }
public string? thumbnail { get; set; }
public void Update(Subscription other)
{
name = other.name;
thumbnail = other.thumbnail;
}
}

View file

@ -1,31 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
namespace FreeTubeSync.Model;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class Video
{
public string videoId { get; set; } = string.Empty;
public string title { get; set; } = string.Empty;
public string author { get; set; } = string.Empty;
public string authorId { get; set; } = string.Empty;
public string lengthSeconds { get; set; } = string.Empty;
public long pubished { get; set; }
public long timeAdded { get; set; }
[Key] public string playlistItemId { get; set; } = string.Empty;
public string type { get; set; } = string.Empty;
public void Update(Video other)
{
videoId = other.videoId;
title = other.title;
author = other.author;
authorId = other.authorId;
lengthSeconds = other.lengthSeconds;
pubished = other.pubished;
timeAdded = other.timeAdded;
type = other.type;
}
}

View file

@ -5,22 +5,25 @@ namespace FreeTubeSync;
public class Repository<TEntity>(DataContext dbContext) : IRepository<TEntity> where TEntity : class
{
public async Task AddAsync(TEntity entity, CancellationToken ct)
public async Task AddAsync(TEntity entity, CancellationToken ct, bool sync = true)
{
dbContext.Set<TEntity>().Add(entity);
await dbContext.SaveChangesAsync(ct);
if(sync)
await dbContext.SaveChangesAsync(ct);
}
public async Task UpdateAsync(TEntity entity, CancellationToken ct)
public async Task UpdateAsync(TEntity entity, CancellationToken ct, bool sync = true)
{
dbContext.Set<TEntity>().Update(entity);
await dbContext.SaveChangesAsync(ct);
if (sync)
await dbContext.SaveChangesAsync(ct);
}
public async Task DeleteAsync(TEntity entity, CancellationToken ct)
public async Task DeleteAsync(TEntity entity, CancellationToken ct, bool sync = true)
{
dbContext.Set<TEntity>().Remove(entity);
await dbContext.SaveChangesAsync(ct);
if (sync)
await dbContext.SaveChangesAsync(ct);
}
public async Task<TEntity?> GetByIdAsync(string id, CancellationToken ct)

View file

@ -1,20 +0,0 @@
using FreeTubeSync.Model;
namespace FreeTubeSync.SpecialResponses;
public static class Mappings
{
public static SettingResponse MapToResponse(this Setting setting)
{
return new SettingResponse
{
_id = setting._id,
value = setting.Value
};
}
public static IEnumerable<SettingResponse> MapToResponse(this IEnumerable<Setting> settings)
{
return settings.Select(MapToResponse);
}
}

View file

@ -1,9 +0,0 @@
using FreeTubeSync.Model;
namespace FreeTubeSync.SpecialResponses;
public class SettingResponse
{
public string _id { get; set; }
public object value { get; set; }
}