< 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
5%
Covered lines: 3
Uncovered lines: 53
Coverable lines: 56
Total lines: 128
Line coverage: 5.3%
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 7/22/2025 - 12:11:20 AM Line coverage: 5% (3/60) Branch coverage: 0% (0/6) Total lines: 12310/14/2025 - 12:11:23 AM Line coverage: 5.3% (3/56) Branch coverage: 0% (0/6) Total lines: 128 7/22/2025 - 12:11:20 AM Line coverage: 5% (3/60) Branch coverage: 0% (0/6) Total lines: 12310/14/2025 - 12:11:23 AM Line coverage: 5.3% (3/56) Branch coverage: 0% (0/6) Total lines: 128

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetChapter(...)0%620%
GetChapters(...)100%210%
SaveChapters(...)0%620%
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    {
 056        using var context = _dbProvider.CreateDbContext();
 057        return context.Chapters.AsNoTracking().Where(e => e.ItemId.Equals(baseItemId))
 058            .Select(e => new
 059            {
 060                chapter = e,
 061                baseItemPath = e.Item.Path
 062            })
 063            .AsEnumerable()
 064            .Select(e => Map(e.chapter, e.baseItemPath!))
 065            .ToArray();
 066    }
 67
 68    /// <inheritdoc />
 69    public void SaveChapters(Guid itemId, IReadOnlyList<ChapterInfo> chapters)
 70    {
 071        using var context = _dbProvider.CreateDbContext();
 072        using (var transaction = context.Database.BeginTransaction())
 73        {
 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        }
 084    }
 85
 86    /// <inheritdoc />
 87    public async Task DeleteChaptersAsync(Guid itemId, CancellationToken cancellationToken)
 88    {
 89        var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
 90        await using (dbContext.ConfigureAwait(false))
 91        {
 92            await dbContext.Chapters.Where(c => c.ItemId.Equals(itemId)).ExecuteDeleteAsync(cancellationToken).Configure
 93            await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
 94        }
 95    }
 96
 97    private Chapter Map(ChapterInfo chapterInfo, int index, Guid itemId)
 98    {
 099        return new Chapter()
 0100        {
 0101            ChapterIndex = index,
 0102            StartPositionTicks = chapterInfo.StartPositionTicks,
 0103            ImageDateModified = chapterInfo.ImageDateModified,
 0104            ImagePath = chapterInfo.ImagePath,
 0105            ItemId = itemId,
 0106            Name = chapterInfo.Name,
 0107            Item = null!
 0108        };
 109    }
 110
 111    private ChapterInfo Map(Chapter chapterInfo, string baseItemPath)
 112    {
 0113        var chapterEntity = new ChapterInfo()
 0114        {
 0115            StartPositionTicks = chapterInfo.StartPositionTicks,
 0116            ImageDateModified = chapterInfo.ImageDateModified.GetValueOrDefault(),
 0117            ImagePath = chapterInfo.ImagePath,
 0118            Name = chapterInfo.Name,
 0119        };
 120
 0121        if (!string.IsNullOrEmpty(chapterInfo.ImagePath))
 122        {
 0123            chapterEntity.ImageTag = _imageProcessor.GetImageCacheTag(baseItemPath, chapterEntity.ImageDateModified);
 124        }
 125
 0126        return chapterEntity;
 127    }
 128}