< Summary - Jellyfin

Information
Class: Jellyfin.Server.Implementations.Item.ChapterRepository
Assembly: Jellyfin.Server.Implementations
File(s): /srv/git/jellyfin/Jellyfin.Server.Implementations/Item/ChapterRepository.cs
Line coverage
24%
Covered lines: 15
Uncovered lines: 46
Coverable lines: 61
Total lines: 127
Line coverage: 24.5%
Branch coverage
0%
Covered branches: 0
Total branches: 6
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 2/13/2026 - 12:11:21 AM Line coverage: 5.3% (3/56) Branch coverage: 0% (0/6) Total lines: 1284/19/2026 - 12:14:27 AM Line coverage: 4.9% (3/61) Branch coverage: 0% (0/6) Total lines: 1285/4/2026 - 12:15:16 AM Line coverage: 22.9% (14/61) Branch coverage: 0% (0/6) Total lines: 1285/18/2026 - 12:15:49 AM Line coverage: 24.5% (15/61) Branch coverage: 0% (0/6) Total lines: 127 2/13/2026 - 12:11:21 AM Line coverage: 5.3% (3/56) Branch coverage: 0% (0/6) Total lines: 1284/19/2026 - 12:14:27 AM Line coverage: 4.9% (3/61) Branch coverage: 0% (0/6) Total lines: 1285/4/2026 - 12:15:16 AM Line coverage: 22.9% (14/61) Branch coverage: 0% (0/6) Total lines: 1285/18/2026 - 12:15:49 AM Line coverage: 24.5% (15/61) Branch coverage: 0% (0/6) Total lines: 127

Coverage delta

Coverage delta 18 -18

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetChapter(...)0%620%
GetChapters(...)100%11100%
SaveChapters(...)0%620%
DeleteChaptersAsync()100%210%
Map(...)100%210%
Map(...)0%620%

File(s)

/srv/git/jellyfin/Jellyfin.Server.Implementations/Item/ChapterRepository.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Threading;
 5using System.Threading.Tasks;
 6using Jellyfin.Database.Implementations;
 7using Jellyfin.Database.Implementations.Entities;
 8using MediaBrowser.Controller.Drawing;
 9using MediaBrowser.Controller.Persistence;
 10using MediaBrowser.Model.Entities;
 11using Microsoft.EntityFrameworkCore;
 12
 13namespace Jellyfin.Server.Implementations.Item;
 14
 15/// <summary>
 16/// The Chapter manager.
 17/// </summary>
 18public class ChapterRepository : IChapterRepository
 19{
 20    private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
 21    private readonly IImageProcessor _imageProcessor;
 22
 23    /// <summary>
 24    /// Initializes a new instance of the <see cref="ChapterRepository"/> class.
 25    /// </summary>
 26    /// <param name="dbProvider">The EFCore provider.</param>
 27    /// <param name="imageProcessor">The Image Processor.</param>
 28    public ChapterRepository(IDbContextFactory<JellyfinDbContext> dbProvider, IImageProcessor imageProcessor)
 29    {
 2130        _dbProvider = dbProvider;
 2131        _imageProcessor = imageProcessor;
 2132    }
 33
 34    /// <inheritdoc />
 35    public ChapterInfo? GetChapter(Guid baseItemId, int index)
 36    {
 037        using var context = _dbProvider.CreateDbContext();
 038        var chapter = context.Chapters.AsNoTracking()
 039            .Select(e => new
 040            {
 041                chapter = e,
 042                baseItemPath = e.Item.Path
 043            })
 044            .FirstOrDefault(e => e.chapter.ItemId.Equals(baseItemId) && e.chapter.ChapterIndex == index);
 045        if (chapter is not null)
 46        {
 047            return Map(chapter.chapter, chapter.baseItemPath!);
 48        }
 49
 050        return null;
 051    }
 52
 53    /// <inheritdoc />
 54    public IReadOnlyList<ChapterInfo> GetChapters(Guid baseItemId)
 55    {
 656        using var context = _dbProvider.CreateDbContext();
 657        return context.Chapters.AsNoTracking().Where(e => e.ItemId.Equals(baseItemId))
 658            .OrderBy(e => e.StartPositionTicks)
 659            .Select(e => new
 660            {
 661                chapter = e,
 662                baseItemPath = e.Item.Path
 663            })
 664            .AsEnumerable()
 665            .Select(e => Map(e.chapter, e.baseItemPath!))
 666            .ToArray();
 667    }
 68
 69    /// <inheritdoc />
 70    public void SaveChapters(Guid itemId, IReadOnlyList<ChapterInfo> chapters)
 71    {
 072        using var context = _dbProvider.CreateDbContext();
 073        using var transaction = context.Database.BeginTransaction();
 074        context.Chapters.Where(e => e.ItemId.Equals(itemId)).ExecuteDelete();
 075        for (var i = 0; i < chapters.Count; i++)
 76        {
 077            var chapter = chapters[i];
 078            context.Chapters.Add(Map(chapter, i, itemId));
 79        }
 80
 081        context.SaveChanges();
 082        transaction.Commit();
 083    }
 84
 85    /// <inheritdoc />
 86    public async Task DeleteChaptersAsync(Guid itemId, CancellationToken cancellationToken)
 87    {
 088        var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
 089        await using (dbContext.ConfigureAwait(false))
 90        {
 091            await dbContext.Chapters.Where(c => c.ItemId.Equals(itemId)).ExecuteDeleteAsync(cancellationToken).Configure
 092            await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
 93        }
 094    }
 95
 96    private Chapter Map(ChapterInfo chapterInfo, int index, Guid itemId)
 97    {
 098        return new Chapter()
 099        {
 0100            ChapterIndex = index,
 0101            StartPositionTicks = chapterInfo.StartPositionTicks,
 0102            ImageDateModified = chapterInfo.ImageDateModified,
 0103            ImagePath = chapterInfo.ImagePath,
 0104            ItemId = itemId,
 0105            Name = chapterInfo.Name,
 0106            Item = null!
 0107        };
 108    }
 109
 110    private ChapterInfo Map(Chapter chapterInfo, string baseItemPath)
 111    {
 0112        var chapterEntity = new ChapterInfo()
 0113        {
 0114            StartPositionTicks = chapterInfo.StartPositionTicks,
 0115            ImageDateModified = chapterInfo.ImageDateModified.GetValueOrDefault(),
 0116            ImagePath = chapterInfo.ImagePath,
 0117            Name = chapterInfo.Name,
 0118        };
 119
 0120        if (!string.IsNullOrEmpty(chapterInfo.ImagePath))
 121        {
 0122            chapterEntity.ImageTag = _imageProcessor.GetImageCacheTag(baseItemPath, chapterEntity.ImageDateModified);
 123        }
 124
 0125        return chapterEntity;
 126    }
 127}