Files
claude 9e33a343ab
Some checks failed
release / build-release (push) Has been cancelled
Floatplane channel plugin for Jellyfin 10.10.x: browse, HLS proxy, cookie session, CI release
- IChannel: creators -> sub-channels -> video posts (public listing API, search mapped)
- IRequiresMediaInfoCallback -> signed proxy URLs; stream controller rewrites HLS so the
  AES-128 key is fetched server-side with the session cookie, segments stay on the CDN
- delivery/info cached per token lifetime and throttled (~2/min)
- config page: paste sails.sid + validate, extra creators, quality, search test
- scripts/package.sh -> plugin folder + zip + md5 + manifest.json; Gitea release workflow
- research docs, HAR analysis (HAR itself gitignored), install-pi5.sh

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-25 09:42:27 +01:00

167 lines
4.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Jellyfin.Plugin.Floatplane.Api.Models;
/// <summary>
/// A blog post (the unit shown in Floatplane's feed). List endpoints return posts
/// without attachment details; GET /api/v3/content/post expands them.
/// </summary>
public class PostModel : IHasId
{
public string Id { get; set; } = string.Empty;
[JsonIgnore]
public bool IsReferenceOnly { get; set; }
public string? Guid { get; set; }
public string Title { get; set; } = string.Empty;
/// <summary>HTML body.</summary>
public string? Text { get; set; }
public string? TextMarkdown { get; set; }
public string? Type { get; set; }
public List<string> Tags { get; set; } = new();
public List<string> AttachmentOrder { get; set; } = new();
public DateTimeOffset? ReleaseDate { get; set; }
public int Likes { get; set; }
public int Dislikes { get; set; }
public int Score { get; set; }
public int Comments { get; set; }
public bool IsAccessible { get; set; } = true;
public bool WasReleasedSilently { get; set; }
public PostMetadata? Metadata { get; set; }
public ImageModel? Thumbnail { get; set; }
public CreatorModel? Creator { get; set; }
public ChannelModel? Channel { get; set; }
/// <summary>Full objects from /content/post; ids (or missing) from list endpoints.</summary>
public List<VideoModel>? VideoAttachments { get; set; }
[JsonIgnore]
public bool HasVideo => Metadata?.HasVideo ?? (VideoAttachments is { Count: > 0 });
[JsonIgnore]
public double DurationSeconds => Metadata?.VideoDuration ?? 0;
}
public class PostMetadata
{
public bool HasVideo { get; set; }
public int VideoCount { get; set; }
public double VideoDuration { get; set; }
public bool HasAudio { get; set; }
public int AudioCount { get; set; }
public double AudioDuration { get; set; }
public bool HasPicture { get; set; }
public int PictureCount { get; set; }
public bool HasGallery { get; set; }
public int GalleryCount { get; set; }
public bool IsFeatured { get; set; }
public double DisplayDuration { get; set; }
}
/// <summary>A video attachment (GET /api/v3/content/video or expanded in /content/post).</summary>
public class VideoModel : IHasId
{
public string Id { get; set; } = string.Empty;
[JsonIgnore]
public bool IsReferenceOnly { get; set; }
public string? Guid { get; set; }
public string Title { get; set; } = string.Empty;
public string? Type { get; set; }
public string? Description { get; set; }
public double Duration { get; set; }
public string? Creator { get; set; }
public string? PrimaryBlogPost { get; set; }
public bool IsProcessing { get; set; }
public bool IsAccessible { get; set; } = true;
public List<VideoLevel> Levels { get; set; } = new();
public List<TextTrackModel> TextTracks { get; set; } = new();
public ImageModel? Thumbnail { get; set; }
public ImageModel? TimelineSprite { get; set; }
/// <summary>Resume position in seconds (only present when logged in).</summary>
public double? Progress { get; set; }
}
public class VideoLevel
{
/// <summary>"360", "480", "720", "1080", "2160".</summary>
public string Name { get; set; } = string.Empty;
public int Width { get; set; }
public int Height { get; set; }
public string? Label { get; set; }
public int Order { get; set; }
}
public class TextTrackModel
{
public string Id { get; set; } = string.Empty;
/// <summary>S3-presigned URL (expires after 15 minutes).</summary>
public string Src { get; set; } = string.Empty;
public string? Kind { get; set; }
public string? Language { get; set; }
public bool Generated { get; set; }
public bool Processing { get; set; }
}
/// <summary>Entry of GET /api/v3/content/progress (percentage) or POST /content/get/progress.</summary>
public class ProgressModel
{
public string Id { get; set; } = string.Empty;
public double Progress { get; set; }
}