Compare commits

..

3 commits

Author SHA1 Message Date
1e1d059642 Merge pull request 'feat: scaffold Nix flake with dotnet-sdk_9, typst, and nodejs' (#10) from issue-1-flake-scaffold into main 2026-05-15 21:26:20 -07:00
Scott Scoble
4e12bc5b41 fix: populate deps.json, add Jellyfin package refs, rename csproj to Jellyfin.Plugin.Lessons.csproj
- Generated correct deps.json with nuget-to-json for all NuGet dependencies
- Added Jellyfin.Controller and Jellyfin.Model package references with ExcludeAssets=runtime
- Renamed csproj to Jellyfin.Plugin.Lessons.csproj (updated flake.nix projectFile)
2026-05-15 21:25:13 -07:00
Scott Scoble
d94b71d0a1 feat: scaffold Nix flake with dotnet-sdk_9, typst, and nodejs
- devShells.default provides dotnetCorePackages.sdk_9_0, typst, nodejs
- packages.default uses buildDotnetModule with net9.0 target via callPackage
- Stub plugin/ project with minimal .csproj
- .gitignore with .NET/Nix build artifacts
2026-05-15 21:18:15 -07:00
7 changed files with 281 additions and 173 deletions

14
.gitignore vendored Normal file
View file

@ -0,0 +1,14 @@
# .NET build artifacts
bin/
obj/
*.dll
# Nix
.result/
result
# IDE
.vs/
.vscode/
*.user
*.suo

152
deps.json Normal file
View file

@ -0,0 +1,152 @@
[
{
"pname": "Diacritics",
"version": "3.3.29",
"hash": "sha256-sIbdJ3yMthnmJHly3WheUdYjtwPakcczTJx9ycxtgrY="
},
{
"pname": "ICU4N",
"version": "60.1.0-alpha.356",
"hash": "sha256-1QyOgO7pNMeoEgBtl6o8IG4o91wD2hFUgRI0jM0ltxY="
},
{
"pname": "ICU4N.Transliterator",
"version": "60.1.0-alpha.356",
"hash": "sha256-RLNwQNVqNz8Omfb/mC/rzQWVq8c7uCnNdG2qi4xJmds="
},
{
"pname": "J2N",
"version": "2.0.0",
"hash": "sha256-YvtIWErlm2O49hg3lIRm5Ha8/owkQkfMudzuldC3EhA="
},
{
"pname": "Jellyfin.Common",
"version": "10.10.7",
"hash": "sha256-9EIigrDheob4vRP+UBAoIPHH4fyz6Cl27GUpelEGpBg="
},
{
"pname": "Jellyfin.Controller",
"version": "10.10.7",
"hash": "sha256-/obWAuxWpSn+NlMES+fjyrf1g+qbxmSYQUBvgXQYltQ="
},
{
"pname": "Jellyfin.Data",
"version": "10.10.7",
"hash": "sha256-lRQjg/HkFAtCN0woL1gX6j5dMfVLP/fzQTKjFTcRth4="
},
{
"pname": "Jellyfin.Extensions",
"version": "10.10.7",
"hash": "sha256-AOGJ2IoT2v+LnlEqnb2O4FnXapcQiH0V9ny8GUysNHg="
},
{
"pname": "Jellyfin.Model",
"version": "10.10.7",
"hash": "sha256-ubsClGTLq/aFnMiBCaHVUX2b88beuL07Yn5VXNbTCjQ="
},
{
"pname": "Jellyfin.Naming",
"version": "10.10.7",
"hash": "sha256-/jlIS0X4FuBo6Ac1cjLGzgdJ3vR6VotBspxpNrqfMzo="
},
{
"pname": "Microsoft.Extensions.Caching.Abstractions",
"version": "2.0.0",
"hash": "sha256-Eg1MES40kzkGW9tZmjaKtbWI00Kbv7fLJQmjrigjxqk="
},
{
"pname": "Microsoft.Extensions.Caching.Memory",
"version": "2.0.0",
"hash": "sha256-1fnNvp62KrviVwYlqVl1CbdaZVpCDah9eCZeNDGDbWM="
},
{
"pname": "Microsoft.Extensions.Configuration.Abstractions",
"version": "8.0.0",
"hash": "sha256-4eBpDkf7MJozTZnOwQvwcfgRKQGcNXe0K/kF+h5Rl8o="
},
{
"pname": "Microsoft.Extensions.Configuration.Binder",
"version": "8.0.2",
"hash": "sha256-aGB0VuoC34YadAEqrwoaXLc5qla55pswDV2xLSmR7SE="
},
{
"pname": "Microsoft.Extensions.DependencyInjection",
"version": "8.0.1",
"hash": "sha256-O9g0jWS+jfGoT3yqKwZYJGL+jGSIeSbwmvomKDC3hTU="
},
{
"pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
"version": "2.0.0",
"hash": "sha256-H1rEnq/veRWvmp8qmUsrQkQIcVlKilUNzmmKsxJ0md8="
},
{
"pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
"version": "8.0.0",
"hash": "sha256-75KzEGWjbRELczJpCiJub+ltNUMMbz5A/1KQU+5dgP8="
},
{
"pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
"version": "8.0.2",
"hash": "sha256-UfLfEQAkXxDaVPC7foE/J3FVEXd31Pu6uQIhTic3JgY="
},
{
"pname": "Microsoft.Extensions.Logging",
"version": "8.0.1",
"hash": "sha256-vkfVw4tQEg86Xg18v6QO0Qb4Ysz0Njx57d1XcNuj6IU="
},
{
"pname": "Microsoft.Extensions.Logging.Abstractions",
"version": "8.0.2",
"hash": "sha256-cHpe8X2BgYa5DzulZfq24rg8O2K5Lmq2OiLhoyAVgJc="
},
{
"pname": "Microsoft.Extensions.Options",
"version": "2.0.0",
"hash": "sha256-EMvaXxGzueI8lT97bYJQr0kAj1IK0pjnAcWN82hTnzw="
},
{
"pname": "Microsoft.Extensions.Options",
"version": "8.0.2",
"hash": "sha256-AjcldddddtN/9aH9pg7ClEZycWtFHLi9IPe1GGhNQys="
},
{
"pname": "Microsoft.Extensions.Primitives",
"version": "2.0.0",
"hash": "sha256-q44LtMvyNEKSvgERvA+BrasKapP92Sc91QR4u2TJ9/Y="
},
{
"pname": "Microsoft.Extensions.Primitives",
"version": "8.0.0",
"hash": "sha256-FU8qj3DR8bDdc1c+WeGZx/PCZeqqndweZM9epcpXjSo="
},
{
"pname": "Microsoft.NETCore.Platforms",
"version": "1.1.0",
"hash": "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM="
},
{
"pname": "Microsoft.NETCore.Targets",
"version": "1.1.0",
"hash": "sha256-0AqQ2gMS8iNlYkrD+BxtIg7cXMnr9xZHtKAuN4bjfaQ="
},
{
"pname": "System.Globalization",
"version": "4.3.0",
"hash": "sha256-caL0pRmFSEsaoeZeWN5BTQtGrAtaQPwFi8YOZPZG5rI="
},
{
"pname": "System.Runtime",
"version": "4.3.0",
"hash": "sha256-51813WXpBIsuA6fUtE5XaRQjcWdQ2/lmEokJt97u0Rg="
},
{
"pname": "System.Text.Json",
"version": "8.0.5",
"hash": "sha256-yKxo54w5odWT6nPruUVsaX53oPRe+gKzGvLnnxtwP68="
},
{
"pname": "System.Threading.Tasks.Dataflow",
"version": "8.0.1",
"hash": "sha256-hgCfF91BDd/eOtLEd5jhjzgJdvwmVv4/b42fXRr3nvo="
}
]

27
flake.lock generated Normal file
View file

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1778443072,
"narHash": "sha256-zi7/fsqM/kFdNuED//4WOCUtezGtKKqRNORjMvfwjnA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "da5ad661ba4e5ef59ba743f0d112cbc30e474f32",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

72
flake.nix Normal file
View file

@ -0,0 +1,72 @@
{
description = "Jellyfin Lessons — a Jellyfin plugin";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs =
{ self, nixpkgs }:
let
forAllSystems =
fn: nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed fn;
in
{
devShells = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
default = pkgs.mkShell {
packages = [
pkgs.dotnetCorePackages.sdk_9_0
pkgs.typst
pkgs.nodejs
];
shellHook = ''
echo "jellyfin-lessons dev shell"
echo " dotnet: $(dotnet --version)"
echo " typst: $(typst --version)"
echo " node: $(node --version)"
'';
};
}
);
packages = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
default = pkgs.callPackage (
{
buildDotnetModule,
dotnetCorePackages,
lib,
}:
buildDotnetModule {
pname = "jellyfin-lessons";
version = "0.1.0";
src = self;
projectFile = "plugin/Jellyfin.Plugin.Lessons.csproj";
nugetDeps = ./deps.json;
dotnet-sdk = dotnetCorePackages.sdk_9_0;
dotnet-runtime = dotnetCorePackages.aspnetcore_9_0;
meta = {
description = "Jellyfin Lessons plugin";
license = lib.licenses.mit;
platforms = lib.platforms.all;
};
}
) { };
}
);
};
}

View file

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<RootNamespace>JellyfinLessons</RootNamespace>
<AssemblyName>jellyfin-lessons</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Jellyfin.Controller" Version="10.10.*">
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="Jellyfin.Model" Version="10.10.*">
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View file

@ -1,83 +0,0 @@
// example-lesson.typ — Complete example demonstrating the lesson template
// ============================================================
// Frontmatter:
// title: God's Creation: The Water Cycle
// grade: 3
// subject: Science
// date: 2026-01-15
// media: jellyfin://uuid-water-cycle, jellyfin://uuid-cloud-formation
// description: Learn how God designed water to move through His creation —
// evaporation, condensation, and precipitation working together to sustain life.
#import "lesson.typ": lesson-template, media-ref
#show: doc => lesson-template(
doc,
title: "God's Creation: The Water Cycle",
grade: "3",
subject: "Science",
date: "2026-01-15",
media: (
("uuid-water-cycle", "The Water Cycle Explained"),
("uuid-cloud-formation", "Cloud Formation and Weather"),
),
description: "Learn how God designed water to move through His creation — evaporation, condensation, and precipitation working together to sustain life on Earth.",
)
= What Is the Water Cycle?
Water is one of God's most amazing creations! The water cycle is how water moves
all around our planet. It never runs out — it just keeps going around and around.
There are four main steps: evaporation, condensation, precipitation, and collection.
== Evaporation
When the sun shines on oceans, lakes, and rivers, the water gets warm and turns
into a gas called *water vapor*. This gas rises up into the sky. You can't see it,
but it's there! Evaporation happens every day, especially on warm, sunny days.
== Condensation
When the water vapor goes high up where it's cold, it turns back into tiny water
droplets. These droplets come together to make clouds. Have you ever looked at
clouds and seen shapes? Those shapes are made of millions of tiny water droplets
sticking together!
== Precipitation
When the clouds get too heavy with water droplets, the water falls back down to
Earth. This is called *precipitation*. It can be:
- Rain — liquid water falling from the sky
- Snow — frozen crystals that fall in cold weather
- Sleet — tiny ice pellets that bounce when they land
- Hail — hard balls of ice that form in big storms
God sends precipitation to water the plants and fill the rivers, lakes, and oceans.
== Collection
The water that falls onto the ground collects in rivers, lakes, and oceans. Some
of it soaks into the ground and becomes *groundwater* — water stored underground
that plants can reach with their roots. Then the sun warms the water, and the
whole cycle starts all over again!
= Why Is the Water Cycle Important?
God designed the water cycle to do important work:
- Give plants the water they need to grow
- Provide fresh water for animals and people to drink
- Keep our rivers and lakes full of water
- Help control the temperature of our whole planet
- Clean the water naturally as it moves through the cycle
*"He causes the vapors to ascend from the ends of the earth; He makes lightning
for the rain; He brings the wind out of His treasuries."* — Psalm 135:7
= Think About It
1. What happens to water when the sun shines on a lake?
2. Why do clouds form high up in the sky?
3. What are the four types of precipitation?
4. How does the water cycle show God's care for His creation?

View file

@ -1,90 +0,0 @@
// lesson.typ — Typst template for homeschool lessons
// ============================================================
// This template produces clean, structured lesson documents.
// Designed to compile to HTML for rendering inside Jellyfin.
//
// Frontmatter convention (documented as comments at top of each lesson):
// // title: The Lesson Title
// // grade: 3
// // subject: Science
// // date: 2026-01-15
// // media: jellyfin://uuid-1, jellyfin://uuid-2
// // description: Short description of the lesson
//
// Usage:
// #import "lesson.typ": lesson-template, media-ref
// #show: doc => lesson-template(
// doc,
// title: "The Water Cycle",
// grade: "3",
// subject: "Science",
// date: "2026-01-15",
// media: (),
// description: "Learn about evaporation and condensation.",
// )
// Render a single Jellyfin media reference.
// Produces a <code data-lang="jellyfin-media"> element in HTML output
// that the Jellyfin plugin can parse and link to the player.
#let media-ref(id, title) = {
raw(lang: "jellyfin-media", id + "|" + title)
}
// Render a list of media references as a formatted section.
#let media-section(media-items) = {
if media-items.len() > 0 {
heading(level: 2)[Media]
for item in media-items {
list.item(media-ref(item.at(0), item.at(1)))
}
}
}
// The main lesson template.
// - body: The lesson content (passed via show rule)
// - title: Lesson title (string)
// - grade: Grade level (string, e.g. "3")
// - subject: Subject name (string)
// - date: Lesson date in YYYY-MM-DD format
// - media: Array of (id, title) pairs for linked Jellyfin media
// - description: Short description of the lesson
#let lesson-template(
body,
title: "Untitled Lesson",
grade: none,
subject: none,
date: none,
media: (),
description: none,
) = {
// Document metadata for PDF output
set document(title: title)
// Title — prominent heading
heading(level: 1, title)
// Metadata line: Grade X — Subject — Date
let meta-parts = ()
if grade != none { meta-parts.push([Grade ] + grade) }
if subject != none { meta-parts.push(subject) }
if date != none { meta-parts.push(date) }
if meta-parts.len() > 0 {
let sep = [ — ]
let meta-text = meta-parts.first()
for i in range(1, meta-parts.len()) {
meta-text += sep + meta-parts.at(i)
}
par(text(style: "italic", size: 0.9em, meta-text))
}
// Description paragraph
if description != none {
par([*Description:* ] + description)
}
// Lesson body content (renders user-authored sections, paragraphs, lists, etc.)
body
// Media references section — listed with identifiable jellyfin-media codes
media-section(media)
}