- Lesson.cs and LessonMedia.cs model classes - LessonStore.cs with CRUD operations - Microsoft.Data.Sqlite package reference - Default DB path: ~/.local/share/jellyfin-lessons/lessons.db Closes #4
223 lines
7.8 KiB
C#
223 lines
7.8 KiB
C#
using Microsoft.Data.Sqlite;
|
|
using Jellyfin.Plugin.Lessons.Data.Models;
|
|
|
|
namespace Jellyfin.Plugin.Lessons.Data;
|
|
|
|
public class LessonStore
|
|
{
|
|
private readonly string _connectionString;
|
|
|
|
public LessonStore(string dbPath)
|
|
{
|
|
if (dbPath.StartsWith("~"))
|
|
dbPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + dbPath[1..];
|
|
|
|
var dir = Path.GetDirectoryName(dbPath);
|
|
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
|
Directory.CreateDirectory(dir);
|
|
|
|
_connectionString = $"Data Source={dbPath}";
|
|
|
|
CreateTables();
|
|
}
|
|
|
|
private void CreateTables()
|
|
{
|
|
using var conn = new SqliteConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
using var cmd = conn.CreateCommand();
|
|
cmd.CommandText = @"
|
|
CREATE TABLE IF NOT EXISTS lessons (
|
|
id TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
grade INTEGER NOT NULL,
|
|
subject TEXT NOT NULL,
|
|
lesson_date TEXT NOT NULL,
|
|
description TEXT,
|
|
source_path TEXT NOT NULL,
|
|
compiled_html TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS lesson_media (
|
|
lesson_id TEXT NOT NULL,
|
|
jellyfin_item_id TEXT NOT NULL,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
PRIMARY KEY (lesson_id, jellyfin_item_id),
|
|
FOREIGN KEY (lesson_id) REFERENCES lessons(id) ON DELETE CASCADE
|
|
);";
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
|
|
public List<Lesson> GetAllLessons()
|
|
{
|
|
var lessons = new List<Lesson>();
|
|
|
|
using var conn = new SqliteConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
using var cmd = conn.CreateCommand();
|
|
cmd.CommandText = "SELECT * FROM lessons ORDER BY lesson_date DESC, subject, title";
|
|
|
|
using var reader = cmd.ExecuteReader();
|
|
while (reader.Read())
|
|
lessons.Add(ReadLesson(reader));
|
|
|
|
return lessons;
|
|
}
|
|
|
|
public Lesson? GetLessonById(string id)
|
|
{
|
|
using var conn = new SqliteConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
using var cmd = conn.CreateCommand();
|
|
cmd.CommandText = "SELECT * FROM lessons WHERE id = @id";
|
|
cmd.Parameters.AddWithValue("@id", id);
|
|
|
|
using var reader = cmd.ExecuteReader();
|
|
if (!reader.Read())
|
|
return null;
|
|
|
|
var lesson = ReadLesson(reader);
|
|
lesson.MediaItems = GetMediaForLesson(lesson.Id);
|
|
return lesson;
|
|
}
|
|
|
|
public List<Lesson> GetLessonsByGrade(int grade)
|
|
{
|
|
var lessons = new List<Lesson>();
|
|
|
|
using var conn = new SqliteConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
using var cmd = conn.CreateCommand();
|
|
cmd.CommandText = "SELECT * FROM lessons WHERE grade = @grade ORDER BY lesson_date DESC, subject, title";
|
|
cmd.Parameters.AddWithValue("@grade", grade);
|
|
|
|
using var reader = cmd.ExecuteReader();
|
|
while (reader.Read())
|
|
lessons.Add(ReadLesson(reader));
|
|
|
|
return lessons;
|
|
}
|
|
|
|
public void UpsertLesson(Lesson lesson, List<LessonMedia> mediaItems)
|
|
{
|
|
using var conn = new SqliteConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
using var tx = conn.BeginTransaction();
|
|
|
|
var now = DateTime.UtcNow.ToString("o");
|
|
|
|
if (string.IsNullOrEmpty(lesson.Id))
|
|
lesson.Id = Guid.NewGuid().ToString();
|
|
|
|
lesson.CreatedAt = string.IsNullOrEmpty(lesson.CreatedAt) ? now : lesson.CreatedAt;
|
|
lesson.UpdatedAt = now;
|
|
|
|
using (var cmd = conn.CreateCommand())
|
|
{
|
|
cmd.CommandText = @"
|
|
INSERT INTO lessons (id, title, grade, subject, lesson_date, description, source_path, compiled_html, created_at, updated_at)
|
|
VALUES (@id, @title, @grade, @subject, @lesson_date, @description, @source_path, @compiled_html, @created_at, @updated_at)
|
|
ON CONFLICT(id) DO UPDATE SET
|
|
title = excluded.title,
|
|
grade = excluded.grade,
|
|
subject = excluded.subject,
|
|
lesson_date = excluded.lesson_date,
|
|
description = excluded.description,
|
|
source_path = excluded.source_path,
|
|
compiled_html = excluded.compiled_html,
|
|
updated_at = excluded.updated_at";
|
|
cmd.Parameters.AddWithValue("@id", lesson.Id);
|
|
cmd.Parameters.AddWithValue("@title", lesson.Title);
|
|
cmd.Parameters.AddWithValue("@grade", lesson.Grade);
|
|
cmd.Parameters.AddWithValue("@subject", lesson.Subject);
|
|
cmd.Parameters.AddWithValue("@lesson_date", lesson.LessonDate);
|
|
cmd.Parameters.AddWithValue("@description", (object?)lesson.Description ?? DBNull.Value);
|
|
cmd.Parameters.AddWithValue("@source_path", lesson.SourcePath);
|
|
cmd.Parameters.AddWithValue("@compiled_html", (object?)lesson.CompiledHtml ?? DBNull.Value);
|
|
cmd.Parameters.AddWithValue("@created_at", lesson.CreatedAt);
|
|
cmd.Parameters.AddWithValue("@updated_at", lesson.UpdatedAt);
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
|
|
// Replace media items
|
|
using (var delCmd = conn.CreateCommand())
|
|
{
|
|
delCmd.CommandText = "DELETE FROM lesson_media WHERE lesson_id = @lesson_id";
|
|
delCmd.Parameters.AddWithValue("@lesson_id", lesson.Id);
|
|
delCmd.ExecuteNonQuery();
|
|
}
|
|
|
|
foreach (var m in mediaItems)
|
|
{
|
|
using var insCmd = conn.CreateCommand();
|
|
insCmd.CommandText = @"
|
|
INSERT INTO lesson_media (lesson_id, jellyfin_item_id, sort_order)
|
|
VALUES (@lesson_id, @jellyfin_item_id, @sort_order)";
|
|
insCmd.Parameters.AddWithValue("@lesson_id", lesson.Id);
|
|
insCmd.Parameters.AddWithValue("@jellyfin_item_id", m.JellyfinItemId);
|
|
insCmd.Parameters.AddWithValue("@sort_order", m.SortOrder);
|
|
insCmd.ExecuteNonQuery();
|
|
}
|
|
|
|
tx.Commit();
|
|
}
|
|
|
|
public void DeleteLesson(string id)
|
|
{
|
|
using var conn = new SqliteConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
using var cmd = conn.CreateCommand();
|
|
cmd.CommandText = "DELETE FROM lessons WHERE id = @id";
|
|
cmd.Parameters.AddWithValue("@id", id);
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
|
|
private List<LessonMedia> GetMediaForLesson(string lessonId)
|
|
{
|
|
var items = new List<LessonMedia>();
|
|
|
|
using var conn = new SqliteConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
using var cmd = conn.CreateCommand();
|
|
cmd.CommandText = "SELECT * FROM lesson_media WHERE lesson_id = @id ORDER BY sort_order";
|
|
cmd.Parameters.AddWithValue("@id", lessonId);
|
|
|
|
using var reader = cmd.ExecuteReader();
|
|
while (reader.Read())
|
|
{
|
|
items.Add(new LessonMedia
|
|
{
|
|
LessonId = reader.GetString(0),
|
|
JellyfinItemId = reader.GetString(1),
|
|
SortOrder = reader.GetInt32(2)
|
|
});
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
private static Lesson ReadLesson(SqliteDataReader reader)
|
|
{
|
|
return new Lesson
|
|
{
|
|
Id = reader.GetString(0),
|
|
Title = reader.GetString(1),
|
|
Grade = reader.GetInt32(2),
|
|
Subject = reader.GetString(3),
|
|
LessonDate = reader.GetString(4),
|
|
Description = reader.IsDBNull(5) ? null : reader.GetString(5),
|
|
SourcePath = reader.GetString(6),
|
|
CompiledHtml = reader.IsDBNull(7) ? null : reader.GetString(7),
|
|
CreatedAt = reader.GetString(8),
|
|
UpdatedAt = reader.GetString(9)
|
|
};
|
|
}
|
|
}
|