Compare commits

..

6 commits

Author SHA1 Message Date
8e11008d58 Updated Migrations
Updated Migrations to handle change of Int to Long (SQLite still uses
integer field, but better safe if using something other then SQLite)
Changed Primary Key for Video from VideoId to PlaylistItemId.
2025-07-19 12:47:24 -05:00
6970b792eb Updated Video.cs
Removed videoId as Key, and use playlistItemId as Key, as this will be
unique to each playlist, and will not conflict with videos being on more
then one playlist.
2025-07-19 12:46:14 -05:00
aa2b694d70 Updated Git Ignore
Added SQLite tracking files to ignore.
2025-07-19 12:44:56 -05:00
a61362e9b5 Updated Playlist, SearchHistory and Video
Changed all ints to longs for field datatypes dealing with Unix
timestamp.
2025-07-19 12:42:26 -05:00
2e27078443 Updated SearchHistoryEndpoints.cs
Fixed wrong Model for SearchHistory, was using Regular History class,
instead of SearchHistory class.
2025-07-19 12:40:00 -05:00
15d949d7e1 Updated DataContext.cs
Added Navigation AutoInclude() for Profile subscriptions, and Playlist
videos.
2025-07-19 12:38:36 -05:00
11 changed files with 592 additions and 24 deletions

2
.gitignore vendored
View file

@ -5,3 +5,5 @@ obj/
riderModule.iml riderModule.iml
/_ReSharper.Caches/ /_ReSharper.Caches/
FreeTubeSync.db FreeTubeSync.db
FreeTubeSync.db-wal
FreeTubeSync.db-shm

View file

@ -25,6 +25,20 @@ public class DataContext : DbContext
.Property(s => s.ValueJson) .Property(s => s.ValueJson)
.HasColumnName("Value") .HasColumnName("Value")
.IsRequired(); .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();
} }
public DbSet<History> Histories { get; set; } public DbSet<History> Histories { get; set; }

View file

@ -8,13 +8,13 @@ public static class SearchHistoryEndpoint
{ {
var group = app.MapGroup("searchHistory"); var group = app.MapGroup("searchHistory");
group.MapGet("/", async (IRepository<History> repository, CancellationToken ct) => group.MapGet("/", async (IRepository<SearchHistory> repository, CancellationToken ct) =>
{ {
var result = await repository.GetAllAsync(ct); var result = await repository.GetAllAsync(ct);
return Results.Ok(result); return Results.Ok(result);
}); });
group.MapPost("/", async (IRepository<History> repository, CancellationToken ct, History history) => group.MapPost("/", async (IRepository<SearchHistory> repository, CancellationToken ct, SearchHistory history) =>
{ {
var result = await repository.GetByIdAsync(history._id, ct); var result = await repository.GetByIdAsync(history._id, ct);
if (result != null) if (result != null)
@ -24,7 +24,7 @@ public static class SearchHistoryEndpoint
return Results.Ok(); return Results.Ok();
}); });
group.MapDelete("/{id}", async (IRepository<History> repository, CancellationToken ct, string id) => group.MapDelete("/{id}", async (IRepository<SearchHistory> repository, CancellationToken ct, string id) =>
{ {
var result = await repository.GetByIdAsync(id, ct); var result = await repository.GetByIdAsync(id, ct);
if (result == null) return Results.NotFound(); if (result == null) return Results.NotFound();

View file

@ -0,0 +1,247 @@
// <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("20250719172349_UpdateIntToLong")]
partial class UpdateIntToLong
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.7");
modelBuilder.Entity("FreeTubeSync.Model.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.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", (string)null);
});
modelBuilder.Entity("FreeTubeSync.Model.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", (string)null);
});
modelBuilder.Entity("FreeTubeSync.Model.SearchHistory", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<long>("lastUpdatedAt")
.HasColumnType("INTEGER");
b.HasKey("_id");
b.ToTable("SearchHistories");
});
modelBuilder.Entity("FreeTubeSync.Model.Setting", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<string>("ValueJson")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("Value");
b.HasKey("_id");
b.ToTable("Settings", (string)null);
});
modelBuilder.Entity("FreeTubeSync.Model.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.Video", b =>
{
b.Property<string>("videoId")
.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<string>("playlistItemId")
.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.HasKey("videoId");
b.HasIndex("Playlist_id");
b.ToTable("Videos");
});
modelBuilder.Entity("FreeTubeSync.Model.Subscription", b =>
{
b.HasOne("FreeTubeSync.Model.Profile", null)
.WithMany("subscriptions")
.HasForeignKey("Profile_id");
});
modelBuilder.Entity("FreeTubeSync.Model.Video", b =>
{
b.HasOne("FreeTubeSync.Model.Playlist", null)
.WithMany("videos")
.HasForeignKey("Playlist_id");
});
modelBuilder.Entity("FreeTubeSync.Model.Playlist", b =>
{
b.Navigation("videos");
});
modelBuilder.Entity("FreeTubeSync.Model.Profile", b =>
{
b.Navigation("subscriptions");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace FreeTubeSync.Migrations
{
/// <inheritdoc />
public partial class UpdateIntToLong : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View file

@ -0,0 +1,247 @@
// <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("20250719172939_ChangePrimaryKeyToPlaylistItemId")]
partial class ChangePrimaryKeyToPlaylistItemId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.7");
modelBuilder.Entity("FreeTubeSync.Model.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.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", (string)null);
});
modelBuilder.Entity("FreeTubeSync.Model.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", (string)null);
});
modelBuilder.Entity("FreeTubeSync.Model.SearchHistory", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<long>("lastUpdatedAt")
.HasColumnType("INTEGER");
b.HasKey("_id");
b.ToTable("SearchHistories");
});
modelBuilder.Entity("FreeTubeSync.Model.Setting", b =>
{
b.Property<string>("_id")
.HasColumnType("TEXT");
b.Property<string>("ValueJson")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("Value");
b.HasKey("_id");
b.ToTable("Settings", (string)null);
});
modelBuilder.Entity("FreeTubeSync.Model.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.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.Subscription", b =>
{
b.HasOne("FreeTubeSync.Model.Profile", null)
.WithMany("subscriptions")
.HasForeignKey("Profile_id");
});
modelBuilder.Entity("FreeTubeSync.Model.Video", b =>
{
b.HasOne("FreeTubeSync.Model.Playlist", null)
.WithMany("videos")
.HasForeignKey("Playlist_id");
});
modelBuilder.Entity("FreeTubeSync.Model.Playlist", b =>
{
b.Navigation("videos");
});
modelBuilder.Entity("FreeTubeSync.Model.Profile", b =>
{
b.Navigation("subscriptions");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace FreeTubeSync.Migrations
{
/// <inheritdoc />
public partial class ChangePrimaryKeyToPlaylistItemId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_Videos",
table: "Videos");
migrationBuilder.AddPrimaryKey(
name: "PK_Videos",
table: "Videos",
column: "playlistItemId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_Videos",
table: "Videos");
migrationBuilder.AddPrimaryKey(
name: "PK_Videos",
table: "Videos",
column: "videoId");
}
}
}

View file

@ -1,5 +1,4 @@
// <auto-generated /> // <auto-generated />
using FreeTubeSync.Database; using FreeTubeSync.Database;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
@ -81,10 +80,10 @@ namespace FreeTubeSync.Migrations
b.Property<string>("_id") b.Property<string>("_id")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int>("createdAt") b.Property<long>("createdAt")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("lastUpdatedAt") b.Property<long>("lastUpdatedAt")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("playlistName") b.Property<string>("playlistName")
@ -96,7 +95,7 @@ namespace FreeTubeSync.Migrations
b.HasKey("_id"); b.HasKey("_id");
b.ToTable("Playlists"); b.ToTable("Playlists", (string)null);
}); });
modelBuilder.Entity("FreeTubeSync.Model.Profile", b => modelBuilder.Entity("FreeTubeSync.Model.Profile", b =>
@ -118,7 +117,7 @@ namespace FreeTubeSync.Migrations
b.HasKey("_id"); b.HasKey("_id");
b.ToTable("Profiles"); b.ToTable("Profiles", (string)null);
}); });
modelBuilder.Entity("FreeTubeSync.Model.SearchHistory", b => modelBuilder.Entity("FreeTubeSync.Model.SearchHistory", b =>
@ -126,7 +125,7 @@ namespace FreeTubeSync.Migrations
b.Property<string>("_id") b.Property<string>("_id")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int>("lastUpdatedAt") b.Property<long>("lastUpdatedAt")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.HasKey("_id"); b.HasKey("_id");
@ -173,7 +172,7 @@ namespace FreeTubeSync.Migrations
modelBuilder.Entity("FreeTubeSync.Model.Video", b => modelBuilder.Entity("FreeTubeSync.Model.Video", b =>
{ {
b.Property<string>("videoId") b.Property<string>("playlistItemId")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("Playlist_id") b.Property<string>("Playlist_id")
@ -191,14 +190,10 @@ namespace FreeTubeSync.Migrations
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("playlistItemId") b.Property<long>("pubished")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("pubished")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("timeAdded") b.Property<long>("timeAdded")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("title") b.Property<string>("title")
@ -209,7 +204,11 @@ namespace FreeTubeSync.Migrations
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.HasKey("videoId"); b.Property<string>("videoId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("playlistItemId");
b.HasIndex("Playlist_id"); b.HasIndex("Playlist_id");

View file

@ -11,8 +11,8 @@ public class Playlist
public string playlistName { get; set; } = string.Empty; public string playlistName { get; set; } = string.Empty;
public bool @protected { get; set; } public bool @protected { get; set; }
public List<Video> videos { get; set; } = []; public List<Video> videos { get; set; } = [];
public int createdAt { get; set; } public long createdAt { get; set; }
public int lastUpdatedAt { get; set; } public long lastUpdatedAt { get; set; }
public void Update(Playlist other) public void Update(Playlist other)
{ {

View file

@ -8,7 +8,7 @@ public class SearchHistory
{ {
[Key] [Key]
public string _id { get; set; } = string.Empty; public string _id { get; set; } = string.Empty;
public int lastUpdatedAt { get; set; } public long lastUpdatedAt { get; set; }
public void Update(SearchHistory other) public void Update(SearchHistory other)
{ {

View file

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