Plugin cannot change Episode metadata like ParentIndexNumber with some files #6994

Closed
opened 2026-02-07 04:22:37 +03:00 by OVERLORD · 1 comment
Owner

Originally created by @wsgh0202 on GitHub (May 8, 2025).

Description of the bug

The following issue involves the plugin not working properly, I debugged the code on both sides and think the problem is on Jellyfin's side

I'm using the jellyfin-plugin-bangumi plugin to grab the metadata for the animation, but it always recognizes the special episode content (ParentIndexNumber should be 0) as the main episode content (ParentIndexNumber>0)

The plugin implements the IRemoteMetadataProvider<Episode, EpisodeInfo> interface, and refreshing the metadata during debugging confirms that Item.ParentIndexNumber of MetadataResult<Episode> returned by GetMetadata method is 0, but the SeasonNumber of the Episode is always 1.

Plugins can be replaced with your plugin for debugging purposes, e.g. a blank plugin that implements only the IRemoteMetadataProvider<Episode, EpisodeInfo> interface, modifying the parent directory (see Reproduction steps for the directory structure) with a SeasonNumber of 1, modifying the SP subdirectory to 0 or null.

Reproduction steps

For adding Series for the first time:

  1. Install the plugin

  2. Modify the media library, enable the plugin only in any type of metadata downloaders that the plugin has implemented

  3. Add the following directory structure to the media library and create a blank file.

    root@truenas [11:55:49] [/mnt/tank/bt/Anime/Shows/超时空要塞Δ] 
    -> # tree -P "*.mkv" --prune 
    .
    ├── Macross Delta_S01E01 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E01+ [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E02 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E03 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E04 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E05 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E06 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E07 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E08 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E09 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E10 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E11 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E12 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E13 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E14 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E15 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E16 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E17 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E18 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E19 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E20 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E21 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E22 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E23 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E24 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E25 [AV1-10bit Opus].mkv
    ├── Macross Delta_S01E26 [AV1-10bit Opus].mkv
    ├── SP
    │   ├── Macross Delta_SP0.89 [AV1-10bit Opus].mkv
    │   ├── Macross Delta_SP01 [AV1-10bit Opus].mkv
    │   ├── Macross Delta_SP02 [AV1-10bit Opus].mkv
    │   ├── Macross Delta_SP03 [AV1-10bit Opus].mkv
    │   ├── Macross Delta_SP04 [AV1-10bit Opus].mkv
    │   ├── Macross Delta_SP05 [AV1-10bit Opus].mkv
    │   ├── Macross Delta_SP06 [AV1-10bit Opus].mkv
    │   ├── Macross Delta_SP07 [AV1-10bit Opus].mkv
    │   ├── Macross Delta_SP08 [AV1-10bit Opus].mkv
    │   └── Macross Delta_SP09 [AV1-10bit Opus].mkv
    
  4. Scan the library(Refresh mode: Scan for new and updated files)

  5. Enter the Series and see the Season 1 content.

For repeated debugging:

  1. manually change the Season Number of an Episode in an SP directory(ie,SP\Macross Delta_SP01 [AV1-10bit Opus].mkv) to null or 0 using the Edit Metadata menu.
  2. Using the Refresh Metadata menu, select the Refresh mode to Replace all metadata.

What is the current bug behavior?

Season 1 has an Episode count of 37 because it incorrectly contains the contents of the subdirectory SP

What is the expected correct behavior?

Season 1 should only contain the 27 Episodes of the main directory

Jellyfin Server version

10.10.0+

Specify commit id

aefb9b2cff

Specify unstable release number

No response

Specify version number

No response

Specify the build version

10.10.7

Environment

- OS: Windows 11
- Linux Kernel: none
- Virtualization: KVM
- Clients: Browser
- Browser: Microsoft Edge 136
- FFmpeg Version: 7.0.2-Jellyfin
- Playback Method:
- Hardware Acceleration:
- GPU Model:
- Plugins: [jellyfin-plugin-bangumi 1.7.1](https://github.com/kookxiang/jellyfin-plugin-bangumi/tree/1.7.1)
- Reverse Proxy:
- Base URL:
- Networking:
- Storage: local

Jellyfin logs

[16:16:09] [INF] [1] Main: Jellyfin version: 10.10.7
[16:16:09] [INF] [1] Main: Environment Variables: ["[JELLYFIN_LOG_DIR, E:\\tmp\\jellyfin_10.10.7-amd64\\data\\log]"]
[16:16:09] [INF] [1] Main: Arguments: ["E:\\tmp\\jellyfin_10.10.7-amd64\\jellyfin\\jellyfin.dll", "-d", "E:\\tmp\\jellyfin_10.10.7-amd64\\data"]
[16:16:09] [INF] [1] Main: Operating system: Microsoft Windows 10.0.26100
[16:16:09] [INF] [1] Main: Architecture: X64
[16:16:09] [INF] [1] Main: 64-Bit Process: True
[16:16:09] [INF] [1] Main: User Interactive: True
[16:16:09] [INF] [1] Main: Processor count: 8
[16:16:09] [INF] [1] Main: Program data path: E:\tmp\jellyfin_10.10.7-amd64\data
[16:16:09] [INF] [1] Main: Log directory path: E:\tmp\jellyfin_10.10.7-amd64\data\log
[16:16:09] [INF] [1] Main: Config directory path: E:\tmp\jellyfin_10.10.7-amd64\data\config
[16:16:09] [INF] [1] Main: Cache path: E:\tmp\jellyfin_10.10.7-amd64\data\cache
[16:16:09] [INF] [1] Main: Temp directory path: C:\Users\you\AppData\Local\Temp\jellyfin
[16:16:09] [INF] [1] Main: Web resources path: E:\tmp\jellyfin_10.10.7-amd64\jellyfin\jellyfin-web
[16:16:09] [INF] [1] Main: Application directory: E:\tmp\jellyfin_10.10.7-amd64\jellyfin\
[16:16:09] [INF] [1] Emby.Server.Implementations.AppBase.BaseConfigurationManager: Setting cache path: E:\tmp\jellyfin_10.10.7-amd64\data\cache
[16:16:09] [INF] [1] Emby.Server.Implementations.ApplicationHost: Loading assemblies
[16:16:09] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded assembly AnitomySharp, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null from E:\tmp\jellyfin_10.10.7-amd64\data\plugins\Bangumi_1.7.1.0\AnitomySharp.dll
[16:16:09] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded assembly Fastenshtein, Version=1.0.10.0, Culture=neutral, PublicKeyToken=null from E:\tmp\jellyfin_10.10.7-amd64\data\plugins\Bangumi_1.7.1.0\Fastenshtein.dll
[16:16:09] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded assembly FuzzySharp, Version=1.0.4.0, Culture=neutral, PublicKeyToken=null from E:\tmp\jellyfin_10.10.7-amd64\data\plugins\Bangumi_1.7.1.0\FuzzySharp.dll
[16:16:09] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded assembly Jellyfin.Plugin.Bangumi, Version=1.7.1.0, Culture=neutral, PublicKeyToken=null from E:\tmp\jellyfin_10.10.7-amd64\data\plugins\Bangumi_1.7.1.0\Jellyfin.Plugin.Bangumi.dll
[16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Defined LAN subnets: ["127.0.0.1/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
[16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Defined LAN exclusions: []
[16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Used LAN subnets: ["127.0.0.1/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
[16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Filtered interface addresses: ["192.168.2.100", "172.16.3.140", "127.0.0.1"]
[16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Bind Addresses ["0.0.0.0"]
[16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Remote IP filter is Allowlist
[16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Filtered subnets: []
[16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: Bangumi 1.7.1.0
[16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: TMDb 10.10.7.0
[16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: Studio Images 10.10.7.0
[16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: OMDb 10.10.7.0
[16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: MusicBrainz 10.10.7.0
[16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: AudioDB 10.10.7.0
[16:16:10] [INF] [1] Main: Kestrel is listening on 0.0.0.0
[16:16:10] [INF] [1] Emby.Server.Implementations.ApplicationHost: Running startup tasks
[16:16:10] [INF] [13] Emby.Server.Implementations.IO.LibraryMonitor: Watching directory E:\tmp\jellyfin_10.10.7-amd64\testmedia\ani
[16:16:11] [INF] [1] Emby.Server.Implementations.ScheduledTasks.TaskManager: Daily trigger for Generate Trickplay Images set to fire at 2025-05-09 03:00:00.000 +08:00, which is 10:43:48.9998873 from now.
[16:16:11] [INF] [1] Emby.Server.Implementations.ScheduledTasks.TaskManager: Daily trigger for Extract Chapter Images set to fire at 2025-05-09 02:00:00.000 +08:00, which is 09:43:48.9965365 from now.
[16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Found ffmpeg version 7.0.2
[16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Available decoders: ["libdav1d", "av1", "av1_cuvid", "av1_qsv", "h264", "h264_qsv", "h264_cuvid", "hevc", "hevc_qsv", "hevc_cuvid", "mpeg2video", "mpeg2_qsv", "mpeg2_cuvid", "mpeg4", "mpeg4_cuvid", "msmpeg4", "vc1_qsv", "vc1_cuvid", "vp8", "libvpx", "vp8_cuvid", "vp8_qsv", "vp9", "libvpx-vp9", "vp9_cuvid", "vp9_qsv", "aac", "ac3", "ac4", "dca", "flac", "mp3", "truehd"]
[16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Available encoders: ["libsvtav1", "av1_nvenc", "av1_qsv", "av1_amf", "libx264", "h264_amf", "h264_nvenc", "h264_qsv", "libx265", "hevc_amf", "hevc_nvenc", "hevc_qsv", "mjpeg_qsv", "aac", "libfdk_aac", "ac3", "alac", "dca", "flac", "libmp3lame", "libopus", "truehd", "libvorbis", "srt"]
[16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Available filters: ["bwdif_cuda", "deinterlace_qsv", "hwupload_cuda", "overlay_opencl", "overlay_qsv", "overlay_cuda", "scale_cuda", "scale_opencl", "scale_qsv", "tonemapx", "tonemap_cuda", "tonemap_opencl", "transpose_cuda", "transpose_opencl", "vpp_qsv", "yadif_cuda", "zscale", "alphasrc"]
[16:16:11] [WRN] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Filter: overlay_vaapi with option Action to take when encountering EOF from secondary input is not available
[16:16:11] [WRN] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Filter: overlay_vulkan with option Action to take when encountering EOF from secondary input is not available
[16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Available hwaccel types: ["cuda", "dxva2", "qsv", "d3d11va", "opencl", "d3d12va"]
[16:16:12] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: FFmpeg: ffmpeg
[16:16:12] [INF] [1] Emby.Server.Implementations.ApplicationHost: ServerId: xxxxxx
[16:16:12] [INF] [1] Emby.Server.Implementations.ApplicationHost: Core startup complete
[16:16:12] [INF] [1] Main: Startup complete 0:00:03.4562871
[16:16:14] [INF] [15] Emby.Server.Implementations.ScheduledTasks.TaskManager: Clean Transcode Directory Completed after 0 minute(s) and 0 seconds
[16:16:14] [INF] [9] Emby.Server.Implementations.ScheduledTasks.TaskManager: Clean up collections and playlists Completed after 0 minute(s) and 0 seconds
[16:16:16] [INF] [16] Emby.Server.Implementations.ScheduledTasks.TaskManager: Update Plugins Completed after 0 minute(s) and 2 seconds
[16:16:44] [INF] [15] Emby.Server.Implementations.HttpServer.WebSocketManager: WS 127.0.0.1 request
[16:16:47] [INF] [19] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Starting ffprobe with args -analyzeduration 200M -probesize 1G -i file:"E:\tmp\jellyfin_10.10.7-amd64\testmedia\ani\超时空要塞Δ\SP\Macross Delta_SP01 [AV1-10bit Opus].mkv" -threads 0 -v warning -print_format json -show_streams -show_chapters -show_format
[16:16:47] [ERR] [19] Jellyfin.Plugin.Bangumi.MetadataService.EpisodeMetadataService: Error in Probe Provider
MediaBrowser.Common.FfmpegException: ffprobe failed - streams and format are both null.
   at MediaBrowser.MediaEncoding.Encoder.MediaEncoder.GetMediaInfoInternal(String inputPath, String primaryPath, MediaProtocol protocol, Boolean extractChapters, String probeSizeArgument, Boolean isAudio, Nullable`1 videoType, CancellationToken cancellationToken)
   at MediaBrowser.MediaEncoding.Encoder.MediaEncoder.GetMediaInfoInternal(String inputPath, String primaryPath, MediaProtocol protocol, Boolean extractChapters, String probeSizeArgument, Boolean isAudio, Nullable`1 videoType, CancellationToken cancellationToken)
   at MediaBrowser.Providers.MediaInfo.FFProbeVideoInfo.ProbeVideo[T](T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
   at MediaBrowser.Providers.Manager.MetadataService`2.RunCustomProvider(ICustomMetadataProvider`1 provider, TItemType item, String logName, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken)
[16:16:47] [INF] [19] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: guess episode number from filename Macross Delta_SP01 [AV1-10bit Opus].mkv because of plugin configuration
[16:16:47] [INF] [19] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: used episode number 01 from anitomy
[16:16:47] [INF] [19] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: fetching episode info using saved id: 650587
[16:16:47] [INF] [20] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: current episode is special episode, skip further checks
[16:16:47] [INF] [20] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: metadata for Macross Delta_SP01 [AV1-10bit Opus].mkv: <Bangumi Episode #650587: みらーじゅ日記/行け!空中騎士団「おしえてワルキューレ 装備編」/みらーじゅ日記>

FFmpeg logs


Client / Browser logs

No response

Relevant screenshots or videos

Library Setting

Image

Season 1 contains the special editions incorrectly

Image

Season Number of special editions should be 0 or null

Image

Additional information

Code Analysis

I actually troubleshot the cause, but I'm new to the project and not sure if it's intentional or not

I'm using the v10.10.7 version of the code with the tag, but the link below is to the master branch, as the relevant part remains unchanged.

  1. before calling IRemoteMetadataProvider<Episode, EpisodeInfo>::GetMetadata method, Jellyfin will call LibraryManager::FillMissingEpisodeNumbersFromPath method to initialize the incoming Episode info.

  2. During initialization, the EpisodePathParser::Parse method uses the built-in regular expression to retrieve the Season Number.

  3. if the Season Number is obtained, it will be used as the ParentIndexNumber of the incoming EpisodeInfo.

  4. in MetadataService<Episode, EpisodeInfo>::ExecuteRemoteProviders method, call IRemoteMetadataProvider<Episode, EpisodeInfo>::GetMetadata method to get metadata

  5. call the MetadataService::MergeData method to merge the metadata

  6. Save the final merged metadata

There are a number of reasons for the problem in the steps above:

  • In step 2, there is a regular expression ([0-9]+)-([0-9]+) that happens to match the AV1-10bit part of the file SP\Macross Delta_SP01 [AV1-10bit Opus].mkv, resulting in SeasonNumber always being 1. EpisodeNumber is always 10

  • In step 5, the replaceData parameter passed into the MergeData method was false because it was passed in as a fixed value

  • The MergeData method always has target.ParentIndexNumber as 1 when merging ParentIndexNumber (target is temp.Item), resulting in the value not being updated

    if (replaceData || !target.ParentIndexNumber.HasValue)
    {
        target.ParentIndexNumber = source.ParentIndexNumber;
    }
    

    As a comparison, although the EpisodeNumber in step 2 is always 10, the temp.Item.IndexNumber does not use this value, the target.IndexNumber is always null, so the merge can be successful, the initialization code for temp is referenced: https://github.com/jellyfin/jellyfin/blob/master/MediaBrowser.Providers/Manager/MetadataService.cs#L743-L751

Trying to fix

I implemented such a class in the plugin and modified the ParentIndexNumber successfully

using System;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
using Microsoft.Extensions.Logging;
using OriginalEpisodeMetadataService = MediaBrowser.Providers.TV.EpisodeMetadataService;

namespace Jellyfin.Plugin.Bangumi.MetadataService
{
    /// <summary>
    /// Service to manage episode metadata.
    /// </summary>
    /// <remarks>
    /// Initializes a new instance of the <see cref="EpisodeMetadataService"/> class.
    /// </remarks>
    /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
    /// <param name="logger">Instance of the <see cref="ILogger{SeasonMetadataService}"/> interface.</param>
    /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
    /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
    public class EpisodeMetadataService(
        IServerConfigurationManager serverConfigurationManager,
        ILogger<EpisodeMetadataService> logger,
        IProviderManager providerManager,
        IFileSystem fileSystem,
        ILibraryManager libraryManager) : OriginalEpisodeMetadataService(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
    {
        /// <inheritdoc />
        protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
        {
            base.MergeData(source, target, lockedFields, replaceData, mergeMetadataSettings);

            var sourceItem = source.Item;
            var targetItem = target.Item;

            if (replaceData || sourceItem.ParentIndexNumber.HasValue)
            {
                targetItem.ParentIndexNumber = sourceItem.ParentIndexNumber;
            }
        }
    }
}

Doubt

I can't find much reference documentation or specification about the development of the plugin, here are what confuse me:

  • Is it reasonable to modify the ParentIndexNumber of Episode in IRemoteMetadataProvider<Episode, EpisodeInfo>, if not, where should I change it?

  • Although the attempt to fix the problem was successful, I doubt this is what a plugin should be doing

    • There are only two packages mentioned in the plugin documentation, however inheriting MetadataService requires MediaBrowser.Providers, which I can only introduce via a project reference, as I can't find the corresponding nuget package

    • MetadataService doesn't work like IRemoteMetadataProvider which can be enabled, disabled and sorted in the webui, as it may change the way other plugins work

Originally created by @wsgh0202 on GitHub (May 8, 2025). ### Description of the bug The following issue involves the plugin not working properly, I debugged the code on both sides and think the problem is on Jellyfin's side I'm using the [jellyfin-plugin-bangumi](https://github.com/kookxiang/jellyfin-plugin-bangumi/tree/1.7.1) plugin to grab the metadata for the animation, but it always recognizes the special episode content (ParentIndexNumber should be 0) as the main episode content (ParentIndexNumber>0) The plugin implements the `IRemoteMetadataProvider<Episode, EpisodeInfo>` interface, and refreshing the metadata during debugging confirms that `Item.ParentIndexNumber` of `MetadataResult<Episode>` returned by `GetMetadata` method is 0, but the SeasonNumber of the Episode is always 1. Plugins can be replaced with your plugin for debugging purposes, e.g. a blank plugin that implements only the `IRemoteMetadataProvider<Episode, EpisodeInfo>` interface, modifying the parent directory (see `Reproduction steps` for the directory structure) with a SeasonNumber of `1`, modifying the SP subdirectory to `0` or `null`. ### Reproduction steps For adding Series for the first time: 1. Install the plugin 2. Modify the media library, enable the plugin only in any type of metadata downloaders that the plugin has implemented 3. Add the following directory structure to the media library and create a blank file. ```text root@truenas [11:55:49] [/mnt/tank/bt/Anime/Shows/超时空要塞Δ] -> # tree -P "*.mkv" --prune . ├── Macross Delta_S01E01 [AV1-10bit Opus].mkv ├── Macross Delta_S01E01+ [AV1-10bit Opus].mkv ├── Macross Delta_S01E02 [AV1-10bit Opus].mkv ├── Macross Delta_S01E03 [AV1-10bit Opus].mkv ├── Macross Delta_S01E04 [AV1-10bit Opus].mkv ├── Macross Delta_S01E05 [AV1-10bit Opus].mkv ├── Macross Delta_S01E06 [AV1-10bit Opus].mkv ├── Macross Delta_S01E07 [AV1-10bit Opus].mkv ├── Macross Delta_S01E08 [AV1-10bit Opus].mkv ├── Macross Delta_S01E09 [AV1-10bit Opus].mkv ├── Macross Delta_S01E10 [AV1-10bit Opus].mkv ├── Macross Delta_S01E11 [AV1-10bit Opus].mkv ├── Macross Delta_S01E12 [AV1-10bit Opus].mkv ├── Macross Delta_S01E13 [AV1-10bit Opus].mkv ├── Macross Delta_S01E14 [AV1-10bit Opus].mkv ├── Macross Delta_S01E15 [AV1-10bit Opus].mkv ├── Macross Delta_S01E16 [AV1-10bit Opus].mkv ├── Macross Delta_S01E17 [AV1-10bit Opus].mkv ├── Macross Delta_S01E18 [AV1-10bit Opus].mkv ├── Macross Delta_S01E19 [AV1-10bit Opus].mkv ├── Macross Delta_S01E20 [AV1-10bit Opus].mkv ├── Macross Delta_S01E21 [AV1-10bit Opus].mkv ├── Macross Delta_S01E22 [AV1-10bit Opus].mkv ├── Macross Delta_S01E23 [AV1-10bit Opus].mkv ├── Macross Delta_S01E24 [AV1-10bit Opus].mkv ├── Macross Delta_S01E25 [AV1-10bit Opus].mkv ├── Macross Delta_S01E26 [AV1-10bit Opus].mkv ├── SP │   ├── Macross Delta_SP0.89 [AV1-10bit Opus].mkv │   ├── Macross Delta_SP01 [AV1-10bit Opus].mkv │   ├── Macross Delta_SP02 [AV1-10bit Opus].mkv │   ├── Macross Delta_SP03 [AV1-10bit Opus].mkv │   ├── Macross Delta_SP04 [AV1-10bit Opus].mkv │   ├── Macross Delta_SP05 [AV1-10bit Opus].mkv │   ├── Macross Delta_SP06 [AV1-10bit Opus].mkv │   ├── Macross Delta_SP07 [AV1-10bit Opus].mkv │   ├── Macross Delta_SP08 [AV1-10bit Opus].mkv │   └── Macross Delta_SP09 [AV1-10bit Opus].mkv ``` 5. Scan the library(Refresh mode: Scan for new and updated files) 6. Enter the Series and see the Season 1 content. For repeated debugging: 1. manually change the Season Number of an Episode in an SP directory(ie,`SP\Macross Delta_SP01 [AV1-10bit Opus].mkv`) to `null` or `0` using the `Edit Metadata` menu. 2. Using the `Refresh Metadata` menu, select the Refresh mode to `Replace all metadata`. ### What is the current _bug_ behavior? Season 1 has an Episode count of 37 because it incorrectly contains the contents of the subdirectory SP ### What is the expected _correct_ behavior? Season 1 should only contain the 27 Episodes of the main directory ### Jellyfin Server version 10.10.0+ ### Specify commit id aefb9b2cffca3ccb7a95d89af0d9fb2040de6ed3 ### Specify unstable release number _No response_ ### Specify version number _No response_ ### Specify the build version 10.10.7 ### Environment ```markdown - OS: Windows 11 - Linux Kernel: none - Virtualization: KVM - Clients: Browser - Browser: Microsoft Edge 136 - FFmpeg Version: 7.0.2-Jellyfin - Playback Method: - Hardware Acceleration: - GPU Model: - Plugins: [jellyfin-plugin-bangumi 1.7.1](https://github.com/kookxiang/jellyfin-plugin-bangumi/tree/1.7.1) - Reverse Proxy: - Base URL: - Networking: - Storage: local ``` ### Jellyfin logs ```shell [16:16:09] [INF] [1] Main: Jellyfin version: 10.10.7 [16:16:09] [INF] [1] Main: Environment Variables: ["[JELLYFIN_LOG_DIR, E:\\tmp\\jellyfin_10.10.7-amd64\\data\\log]"] [16:16:09] [INF] [1] Main: Arguments: ["E:\\tmp\\jellyfin_10.10.7-amd64\\jellyfin\\jellyfin.dll", "-d", "E:\\tmp\\jellyfin_10.10.7-amd64\\data"] [16:16:09] [INF] [1] Main: Operating system: Microsoft Windows 10.0.26100 [16:16:09] [INF] [1] Main: Architecture: X64 [16:16:09] [INF] [1] Main: 64-Bit Process: True [16:16:09] [INF] [1] Main: User Interactive: True [16:16:09] [INF] [1] Main: Processor count: 8 [16:16:09] [INF] [1] Main: Program data path: E:\tmp\jellyfin_10.10.7-amd64\data [16:16:09] [INF] [1] Main: Log directory path: E:\tmp\jellyfin_10.10.7-amd64\data\log [16:16:09] [INF] [1] Main: Config directory path: E:\tmp\jellyfin_10.10.7-amd64\data\config [16:16:09] [INF] [1] Main: Cache path: E:\tmp\jellyfin_10.10.7-amd64\data\cache [16:16:09] [INF] [1] Main: Temp directory path: C:\Users\you\AppData\Local\Temp\jellyfin [16:16:09] [INF] [1] Main: Web resources path: E:\tmp\jellyfin_10.10.7-amd64\jellyfin\jellyfin-web [16:16:09] [INF] [1] Main: Application directory: E:\tmp\jellyfin_10.10.7-amd64\jellyfin\ [16:16:09] [INF] [1] Emby.Server.Implementations.AppBase.BaseConfigurationManager: Setting cache path: E:\tmp\jellyfin_10.10.7-amd64\data\cache [16:16:09] [INF] [1] Emby.Server.Implementations.ApplicationHost: Loading assemblies [16:16:09] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded assembly AnitomySharp, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null from E:\tmp\jellyfin_10.10.7-amd64\data\plugins\Bangumi_1.7.1.0\AnitomySharp.dll [16:16:09] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded assembly Fastenshtein, Version=1.0.10.0, Culture=neutral, PublicKeyToken=null from E:\tmp\jellyfin_10.10.7-amd64\data\plugins\Bangumi_1.7.1.0\Fastenshtein.dll [16:16:09] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded assembly FuzzySharp, Version=1.0.4.0, Culture=neutral, PublicKeyToken=null from E:\tmp\jellyfin_10.10.7-amd64\data\plugins\Bangumi_1.7.1.0\FuzzySharp.dll [16:16:09] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded assembly Jellyfin.Plugin.Bangumi, Version=1.7.1.0, Culture=neutral, PublicKeyToken=null from E:\tmp\jellyfin_10.10.7-amd64\data\plugins\Bangumi_1.7.1.0\Jellyfin.Plugin.Bangumi.dll [16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Defined LAN subnets: ["127.0.0.1/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] [16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Defined LAN exclusions: [] [16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Used LAN subnets: ["127.0.0.1/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] [16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Filtered interface addresses: ["192.168.2.100", "172.16.3.140", "127.0.0.1"] [16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Bind Addresses ["0.0.0.0"] [16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Remote IP filter is Allowlist [16:16:09] [INF] [1] Jellyfin.Networking.Manager.NetworkManager: Filtered subnets: [] [16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: Bangumi 1.7.1.0 [16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: TMDb 10.10.7.0 [16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: Studio Images 10.10.7.0 [16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: OMDb 10.10.7.0 [16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: MusicBrainz 10.10.7.0 [16:16:10] [INF] [1] Emby.Server.Implementations.Plugins.PluginManager: Loaded plugin: AudioDB 10.10.7.0 [16:16:10] [INF] [1] Main: Kestrel is listening on 0.0.0.0 [16:16:10] [INF] [1] Emby.Server.Implementations.ApplicationHost: Running startup tasks [16:16:10] [INF] [13] Emby.Server.Implementations.IO.LibraryMonitor: Watching directory E:\tmp\jellyfin_10.10.7-amd64\testmedia\ani [16:16:11] [INF] [1] Emby.Server.Implementations.ScheduledTasks.TaskManager: Daily trigger for Generate Trickplay Images set to fire at 2025-05-09 03:00:00.000 +08:00, which is 10:43:48.9998873 from now. [16:16:11] [INF] [1] Emby.Server.Implementations.ScheduledTasks.TaskManager: Daily trigger for Extract Chapter Images set to fire at 2025-05-09 02:00:00.000 +08:00, which is 09:43:48.9965365 from now. [16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Found ffmpeg version 7.0.2 [16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Available decoders: ["libdav1d", "av1", "av1_cuvid", "av1_qsv", "h264", "h264_qsv", "h264_cuvid", "hevc", "hevc_qsv", "hevc_cuvid", "mpeg2video", "mpeg2_qsv", "mpeg2_cuvid", "mpeg4", "mpeg4_cuvid", "msmpeg4", "vc1_qsv", "vc1_cuvid", "vp8", "libvpx", "vp8_cuvid", "vp8_qsv", "vp9", "libvpx-vp9", "vp9_cuvid", "vp9_qsv", "aac", "ac3", "ac4", "dca", "flac", "mp3", "truehd"] [16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Available encoders: ["libsvtav1", "av1_nvenc", "av1_qsv", "av1_amf", "libx264", "h264_amf", "h264_nvenc", "h264_qsv", "libx265", "hevc_amf", "hevc_nvenc", "hevc_qsv", "mjpeg_qsv", "aac", "libfdk_aac", "ac3", "alac", "dca", "flac", "libmp3lame", "libopus", "truehd", "libvorbis", "srt"] [16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Available filters: ["bwdif_cuda", "deinterlace_qsv", "hwupload_cuda", "overlay_opencl", "overlay_qsv", "overlay_cuda", "scale_cuda", "scale_opencl", "scale_qsv", "tonemapx", "tonemap_cuda", "tonemap_opencl", "transpose_cuda", "transpose_opencl", "vpp_qsv", "yadif_cuda", "zscale", "alphasrc"] [16:16:11] [WRN] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Filter: overlay_vaapi with option Action to take when encountering EOF from secondary input is not available [16:16:11] [WRN] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Filter: overlay_vulkan with option Action to take when encountering EOF from secondary input is not available [16:16:11] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Available hwaccel types: ["cuda", "dxva2", "qsv", "d3d11va", "opencl", "d3d12va"] [16:16:12] [INF] [1] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: FFmpeg: ffmpeg [16:16:12] [INF] [1] Emby.Server.Implementations.ApplicationHost: ServerId: xxxxxx [16:16:12] [INF] [1] Emby.Server.Implementations.ApplicationHost: Core startup complete [16:16:12] [INF] [1] Main: Startup complete 0:00:03.4562871 [16:16:14] [INF] [15] Emby.Server.Implementations.ScheduledTasks.TaskManager: Clean Transcode Directory Completed after 0 minute(s) and 0 seconds [16:16:14] [INF] [9] Emby.Server.Implementations.ScheduledTasks.TaskManager: Clean up collections and playlists Completed after 0 minute(s) and 0 seconds [16:16:16] [INF] [16] Emby.Server.Implementations.ScheduledTasks.TaskManager: Update Plugins Completed after 0 minute(s) and 2 seconds [16:16:44] [INF] [15] Emby.Server.Implementations.HttpServer.WebSocketManager: WS 127.0.0.1 request [16:16:47] [INF] [19] MediaBrowser.MediaEncoding.Encoder.MediaEncoder: Starting ffprobe with args -analyzeduration 200M -probesize 1G -i file:"E:\tmp\jellyfin_10.10.7-amd64\testmedia\ani\超时空要塞Δ\SP\Macross Delta_SP01 [AV1-10bit Opus].mkv" -threads 0 -v warning -print_format json -show_streams -show_chapters -show_format [16:16:47] [ERR] [19] Jellyfin.Plugin.Bangumi.MetadataService.EpisodeMetadataService: Error in Probe Provider MediaBrowser.Common.FfmpegException: ffprobe failed - streams and format are both null. at MediaBrowser.MediaEncoding.Encoder.MediaEncoder.GetMediaInfoInternal(String inputPath, String primaryPath, MediaProtocol protocol, Boolean extractChapters, String probeSizeArgument, Boolean isAudio, Nullable`1 videoType, CancellationToken cancellationToken) at MediaBrowser.MediaEncoding.Encoder.MediaEncoder.GetMediaInfoInternal(String inputPath, String primaryPath, MediaProtocol protocol, Boolean extractChapters, String probeSizeArgument, Boolean isAudio, Nullable`1 videoType, CancellationToken cancellationToken) at MediaBrowser.Providers.MediaInfo.FFProbeVideoInfo.ProbeVideo[T](T item, MetadataRefreshOptions options, CancellationToken cancellationToken) at MediaBrowser.Providers.Manager.MetadataService`2.RunCustomProvider(ICustomMetadataProvider`1 provider, TItemType item, String logName, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken) [16:16:47] [INF] [19] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: guess episode number from filename Macross Delta_SP01 [AV1-10bit Opus].mkv because of plugin configuration [16:16:47] [INF] [19] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: used episode number 01 from anitomy [16:16:47] [INF] [19] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: fetching episode info using saved id: 650587 [16:16:47] [INF] [20] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: current episode is special episode, skip further checks [16:16:47] [INF] [20] Jellyfin.Plugin.Bangumi.Providers.EpisodeProvider: metadata for Macross Delta_SP01 [AV1-10bit Opus].mkv: <Bangumi Episode #650587: みらーじゅ日記/行け!空中騎士団「おしえてワルキューレ 装備編」/みらーじゅ日記> ``` ### FFmpeg logs ```shell ``` ### Client / Browser logs _No response_ ### Relevant screenshots or videos Library Setting ![Image](https://github.com/user-attachments/assets/f26c5d4a-e37d-447e-8845-0c874cecf5a3) Season 1 contains the special editions incorrectly ![Image](https://github.com/user-attachments/assets/9955f882-5962-4869-b58d-84b556054940) Season Number of special editions should be 0 or null ![Image](https://github.com/user-attachments/assets/d2cc016b-59b6-43db-ab19-2bd6b1a5cbf4) ### Additional information #### Code Analysis I actually troubleshot the cause, but I'm new to the project and not sure if it's intentional or not I'm using the `v10.10.7` version of the code with the tag, but the link below is to the master branch, as the relevant part remains unchanged. 1. before calling [IRemoteMetadataProvider<Episode, EpisodeInfo>::GetMetadata](https://github.com/jellyfin/jellyfin/blob/master/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs#L31) method, Jellyfin will call [LibraryManager::FillMissingEpisodeNumbersFromPath](https://github.com/jellyfin/jellyfin/blob/master/Emby.Server.Implementations/Library/LibraryManager.cs#L2575) method to initialize the incoming Episode info. 2. During initialization, the [EpisodePathParser::Parse](https://github.com/jellyfin/jellyfin/blob/master/Emby.Naming/TV/EpisodePathParser.cs#L71) method uses the built-in regular expression to retrieve the Season Number. 3. if the Season Number is obtained, it will be used as the ParentIndexNumber of the incoming EpisodeInfo. 4. in [MetadataService<Episode, EpisodeInfo>::ExecuteRemoteProviders](https://github.com/jellyfin/jellyfin/blob/master/MediaBrowser.Providers/Manager/MetadataService.cs#L890) method, call [IRemoteMetadataProvider<Episode, EpisodeInfo>::GetMetadata](https://github.com/jellyfin/jellyfin/blob/master/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs#L31) method to get metadata 5. call the [MetadataService::MergeData](https://github.com/jellyfin/jellyfin/blob/master/MediaBrowser.Providers/Manager/MetadataService.cs#L978) method to merge the metadata 6. Save the final merged metadata There are a number of reasons for the problem in the steps above: - In step 2, there is a regular expression `([0-9]+)-([0-9]+)` that happens to match the `AV1-10bit` part of the file `SP\Macross Delta_SP01 [AV1-10bit Opus].mkv`, resulting in SeasonNumber always being 1. EpisodeNumber is always 10 - In step 5, the `replaceData` parameter passed into the `MergeData` method was `false` because it was passed in as a [fixed value](https://github.com/jellyfin/jellyfin/blob/a7bb3ea21489a3a4c9a3e9f60c9521eaa84bad31/MediaBrowser.Providers/Manager/MetadataService.cs#L828) - The `MergeData` method always has `target.ParentIndexNumber` as 1 when merging `ParentIndexNumber` (`target` is [temp](https://github.com/jellyfin/jellyfin/blob/master/MediaBrowser.Providers/Manager/MetadataService.cs#L912).Item), resulting in the value not being updated ```csharp if (replaceData || !target.ParentIndexNumber.HasValue) { target.ParentIndexNumber = source.ParentIndexNumber; } ``` As a comparison, although the EpisodeNumber in step 2 is always 10, the `temp.Item.IndexNumber` does not use this value, the `target.IndexNumber` is always null, so the merge can be successful, the initialization code for temp is referenced: <https://github.com/jellyfin/jellyfin/blob/master/MediaBrowser.Providers/Manager/MetadataService.cs#L743-L751> #### Trying to fix I implemented such a class in the plugin and modified the ParentIndexNumber successfully ```csharp using System; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Providers.Manager; using Microsoft.Extensions.Logging; using OriginalEpisodeMetadataService = MediaBrowser.Providers.TV.EpisodeMetadataService; namespace Jellyfin.Plugin.Bangumi.MetadataService { /// <summary> /// Service to manage episode metadata. /// </summary> /// <remarks> /// Initializes a new instance of the <see cref="EpisodeMetadataService"/> class. /// </remarks> /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> /// <param name="logger">Instance of the <see cref="ILogger{SeasonMetadataService}"/> interface.</param> /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param> /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> public class EpisodeMetadataService( IServerConfigurationManager serverConfigurationManager, ILogger<EpisodeMetadataService> logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) : OriginalEpisodeMetadataService(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager) { /// <inheritdoc /> protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { base.MergeData(source, target, lockedFields, replaceData, mergeMetadataSettings); var sourceItem = source.Item; var targetItem = target.Item; if (replaceData || sourceItem.ParentIndexNumber.HasValue) { targetItem.ParentIndexNumber = sourceItem.ParentIndexNumber; } } } } ``` #### Doubt I can't find much reference documentation or specification about the development of the plugin, here are what confuse me: - Is it reasonable to modify the ParentIndexNumber of Episode in `IRemoteMetadataProvider<Episode, EpisodeInfo>`, if not, where should I change it? - Although the attempt to fix the problem was successful, I doubt this is what a plugin should be doing - There are only two packages mentioned in the [plugin documentation](https://github.com/jellyfin/jellyfin-plugin-template#1-initialize-your-project), however inheriting `MetadataService` requires `MediaBrowser.Providers`, which I can only introduce via a project reference, as I can't find the corresponding nuget package - `MetadataService` doesn't work like `IRemoteMetadataProvider` which can be enabled, disabled and sorted in the webui, as it may change the way other plugins work
OVERLORD added the bug label 2026-02-07 04:22:37 +03:00
Author
Owner

@wsgh0202 commented on GitHub (May 10, 2025):

For others who encounter the same issue:

    public class EpisodePreRefreshProvider : ICustomMetadataProvider<Episode>, IPreRefreshProvider
    {
        public string Name => throw new NotImplementedException();

        public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken)
        {
            item.ParentIndexNumber = null;

            return Task.FromResult(ItemUpdateType.None);
        }
    }
@wsgh0202 commented on GitHub (May 10, 2025): For others who encounter the same issue: ```csharp public class EpisodePreRefreshProvider : ICustomMetadataProvider<Episode>, IPreRefreshProvider { public string Name => throw new NotImplementedException(); public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken) { item.ParentIndexNumber = null; return Task.FromResult(ItemUpdateType.None); } } ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/jellyfin#6994