Oborozuki.Fumi.Abstractions 0.1.0-dev.2

This is a prerelease version of Oborozuki.Fumi.Abstractions.
dotnet add package Oborozuki.Fumi.Abstractions --version 0.1.0-dev.2
                    
NuGet\Install-Package Oborozuki.Fumi.Abstractions -Version 0.1.0-dev.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Oborozuki.Fumi.Abstractions" Version="0.1.0-dev.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Oborozuki.Fumi.Abstractions" Version="0.1.0-dev.2" />
                    
Directory.Packages.props
<PackageReference Include="Oborozuki.Fumi.Abstractions" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Oborozuki.Fumi.Abstractions --version 0.1.0-dev.2
                    
#r "nuget: Oborozuki.Fumi.Abstractions, 0.1.0-dev.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Oborozuki.Fumi.Abstractions@0.1.0-dev.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Oborozuki.Fumi.Abstractions&version=0.1.0-dev.2&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Oborozuki.Fumi.Abstractions&version=0.1.0-dev.2&prerelease
                    
Install as a Cake Tool

Oborozuki.Fumi.Abstractions

The contracts a Shoko Server plugin uses to write to Fumi's audit log and to send notifications through its hub, with actions the recipients can take. It carries none of the plugin that stores and delivers them: that is Fumi itself, which the server loads beside your plugin.

Getting the plugin

Fumi is installed into Shoko from its release manifest: in the Web UI, go to Settings → Plugin Management → Repositories, add a repository with this URL, then find Fumi under Browse.

https://cdn.oborozuki.moe/oborozuki/fumi/metadata/manifest.json

Using Fumi from your plugin

Name Fumi as a plugin dependency, so the server loads it before your plugin, and reference the contracts, published as Oborozuki.Fumi.Abstractions (the assembly and namespaces are Fumi.Abstractions). How you reference them depends on whether your plugin needs Fumi or only does more with it.

When your plugin needs Fumi

Make Fumi a required dependency and exclude the runtime assets: Fumi is always there and supplies the assembly.

<PluginDependency Include="4257a8df-7f8b-42f5-9950-581652f73629" Version=">=0.1.0" />
<PackageReference Include="Oborozuki.Fumi.Abstractions" Version="0.1.0-dev.2" ExcludeAssets="runtime" />

Then take IAuditLog and INotificationHub from the service container like any other service:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Fumi.Abstractions.Audit;
using Fumi.Abstractions.Notifications;
using Shoko.Abstractions.User;

namespace Shoko.Plugin.WatchParty;

public class WatchPartyService(IAuditLog audit, INotificationHub notifications)
{
    public async Task Invite(IUser host, IEnumerable<int> guests)
    {
        await audit.Write(new()
        {
            Source = "Watch Party",
            Category = "parties",
            Action = "party.started",
            Summary = $"{host.Username} started a watch party.",
            ActorUserID = host.LocalID,
        });

        await notifications.Send(new()
        {
            Source = "Watch Party",
            Title = $"Join the watch party started by {host.Username}?",
            Audience = NotificationAudience.ForUsers(guests),
            ResponseMode = ActionResponseMode.Multiple,
            Actions =
            [
                new() { Name = "join", Label = "Join", Timeout = TimeSpan.FromMinutes(30), Callback = (answer, token) => AddGuest(answer.User, token) },
                new() { Name = "decline", Label = "Decline", Timeout = TimeSpan.FromMinutes(30), Callback = (_, _) => Task.CompletedTask },
            ],
        });
    }

    private Task AddGuest(IUser guest, CancellationToken token)
        => Task.CompletedTask; // Seat the guest in the party.
}

When Fumi adds to your plugin

Make Fumi an optional dependency and ship the assembly with your plugin. While Fumi is installed the server hands your plugin Fumi's copy (a dependency's own assemblies come before your plugin's files), so the contracts are one type for both; without Fumi your own copy loads, the types resolve, and the services are not registered.

<PluginDependency Include="4257a8df-7f8b-42f5-9950-581652f73629" Version=">=0.1.0" Optional="true" />
<PackageReference Include="Oborozuki.Fumi.Abstractions" Version="0.1.0-dev.2" />

Take the services with a default of null: the container passes the default for a parameter it cannot resolve, while a parameter that is only marked nullable, with no default, fails to resolve. Your plugin does its work either way, and records and tells more when Fumi is there:

using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Fumi.Abstractions.Audit;
using Fumi.Abstractions.Notifications;
using Microsoft.Extensions.Logging;

namespace Shoko.Plugin.SidecarCleaner;

public class SidecarCleaner(
    ILogger<SidecarCleaner> logger,
    IAuditLog? audit = null,
    INotificationHub? notifications = null)
{
    public async Task Clean(string videoPath, CancellationToken token)
    {
        var removed = new List<string>();
        foreach (var sidecar in FindSidecars(videoPath))
        {
            File.Delete(sidecar);
            removed.Add(Path.GetFileName(sidecar));
        }

        // Always: the server log.
        logger.LogInformation("Removed {Count} sidecar file(s) for {Video}.", removed.Count, videoPath);

        // With Fumi: a lasting, searchable record of what was removed.
        if (audit is not null && removed.Count > 0)
            await audit.Write(new()
            {
                Source = "Sidecar Cleaner",
                Category = "files",
                Action = "sidecars.removed",
                Summary = $"Removed {removed.Count} sidecar file(s) next to {Path.GetFileName(videoPath)}.",
            }, token);

        // With Fumi: tell the administrators when a lot went at once.
        if (notifications is not null && removed.Count >= 20)
            await notifications.Send(new()
            {
                Source = "Sidecar Cleaner",
                Title = $"Removed {removed.Count} sidecar files",
                Audience = NotificationAudience.Administrators,
            }, token);
    }

    private static IEnumerable<string> FindSidecars(string videoPath)
        => []; // .nfo, .srt, -thumb.jpg … next to the video.
}

Good to know

  • ActorUserID names the actor, and ActorDevice the device of the key they used when you know it; leave both out and Fumi takes the actor the server has for the calling request, hub call or queued job.
  • Writes and sends made before the server has started wait for it.
  • IAuditLog.EntryWritten, INotificationHub.Sent and INotificationHub.ActionTaken report what happened, and INotificationHub.TakeAction answers for a recipient, for a plugin with a surface of its own.

License

MIT.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-dev.2 0 10/30/2026