From ae1756e4366f0114ca63740e88088b786167cef6 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:31:21 -0500 Subject: [PATCH] fix: update plex movie and other video titles --- CHANGELOG.md | 4 ++++ ErsatzTV.Core/Interfaces/Repositories/IMovieRepository.cs | 2 +- .../Interfaces/Repositories/IOtherVideoRepository.cs | 1 + .../Data/Repositories/MovieRepository.cs | 6 +++--- .../Data/Repositories/OtherVideoRepository.cs | 8 ++++++++ ErsatzTV.Infrastructure/Plex/PlexEtag.cs | 5 +++++ ErsatzTV.Scanner/Core/Plex/PlexMovieLibraryScanner.cs | 5 +++-- .../Core/Plex/PlexOtherVideoLibraryScanner.cs | 8 +++----- 8 files changed, 28 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 366b1e308..b27c598f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Fixed +- Update Plex movie and other video titles when changed from Plex + - The first library scan after updating will act like a deep scan due to adding title to the Plex etag calculation + - Future periodic scans will update titles in ETV automatically (deep scans will not be required to update titles) ## [26.6.0] - 2026-07-09 ### Added diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMovieRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMovieRepository.cs index caa8b041a..52558f816 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMovieRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMovieRepository.cs @@ -20,7 +20,7 @@ public interface IMovieRepository Task AddTag(MovieMetadata metadata, Tag tag); Task AddStudio(MovieMetadata metadata, Studio studio); Task AddActor(MovieMetadata metadata, Actor actor); - Task UpdateSortTitle(MovieMetadata movieMetadata); + Task UpdateTitles(MovieMetadata movieMetadata, string title, string sortTitle); Task AddDirector(MovieMetadata metadata, Director director); Task AddWriter(MovieMetadata metadata, Writer writer); } diff --git a/ErsatzTV.Core/Interfaces/Repositories/IOtherVideoRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IOtherVideoRepository.cs index 819477dfc..91ea35fa9 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IOtherVideoRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IOtherVideoRepository.cs @@ -19,4 +19,5 @@ public interface IOtherVideoRepository Task AddActor(OtherVideoMetadata metadata, Actor actor); Task AddDirector(OtherVideoMetadata metadata, Director director); Task AddWriter(OtherVideoMetadata metadata, Writer writer); + Task UpdateTitles(OtherVideoMetadata metadata, string title, string sortTitle); } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs index a503d8a56..e391b1810 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs @@ -195,12 +195,12 @@ public class MovieRepository : IMovieRepository .Map(result => result > 0); } - public async Task UpdateSortTitle(MovieMetadata movieMetadata) + public async Task UpdateTitles(MovieMetadata movieMetadata, string title, string sortTitle) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.Connection.ExecuteAsync( - @"UPDATE MovieMetadata SET SortTitle = @SortTitle WHERE Id = @Id", - new { movieMetadata.SortTitle, movieMetadata.Id }).Map(result => result > 0); + @"UPDATE MovieMetadata SET Title = @Title, SortTitle = @SortTitle WHERE Id = @Id", + new { Title = title, SortTitle = sortTitle, movieMetadata.Id }).Map(result => result > 0); } public async Task AddDirector(MovieMetadata metadata, Director director) diff --git a/ErsatzTV.Infrastructure/Data/Repositories/OtherVideoRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/OtherVideoRepository.cs index fe5bd474c..0fafef2bb 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/OtherVideoRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/OtherVideoRepository.cs @@ -175,6 +175,14 @@ public class OtherVideoRepository : IOtherVideoRepository new { writer.Name, MetadataId = metadata.Id }).Map(result => result > 0); } + public async Task UpdateTitles(OtherVideoMetadata metadata, string title, string sortTitle) + { + await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.Connection.ExecuteAsync( + "UPDATE OtherVideoMetadata SET Title = @Title, SortTitle = @SortTitle WHERE Id = @Id", + new { Title = title, SortTitle = sortTitle, metadata.Id }).Map(result => result > 0); + } + private async Task>> AddOtherVideo( TvContext dbContext, int libraryPathId, diff --git a/ErsatzTV.Infrastructure/Plex/PlexEtag.cs b/ErsatzTV.Infrastructure/Plex/PlexEtag.cs index 3eae993b2..00061f933 100644 --- a/ErsatzTV.Infrastructure/Plex/PlexEtag.cs +++ b/ErsatzTV.Infrastructure/Plex/PlexEtag.cs @@ -27,6 +27,10 @@ public class PlexEtag // video updated at bw.Write(response.UpdatedAt); + // video title + bw.Write((byte)FieldKey.Title); + bw.Write(response.Title ?? string.Empty); + foreach (PlexMediaResponse media in response.Media) { // media id @@ -330,6 +334,7 @@ public class PlexEtag Thumb = 20, Art = 21, + Title = 22, File = 30, Key = 31, diff --git a/ErsatzTV.Scanner/Core/Plex/PlexMovieLibraryScanner.cs b/ErsatzTV.Scanner/Core/Plex/PlexMovieLibraryScanner.cs index 1474cac11..14f9ef24f 100644 --- a/ErsatzTV.Scanner/Core/Plex/PlexMovieLibraryScanner.cs +++ b/ErsatzTV.Scanner/Core/Plex/PlexMovieLibraryScanner.cs @@ -350,10 +350,11 @@ public class PlexMovieLibraryScanner : result.IsUpdated = true; } - if (fullMetadata.SortTitle != existingMetadata.SortTitle) + if (fullMetadata.SortTitle != existingMetadata.SortTitle || fullMetadata.Title != existingMetadata.Title) { + existingMetadata.Title = fullMetadata.Title; existingMetadata.SortTitle = fullMetadata.SortTitle; - if (await _movieRepository.UpdateSortTitle(existingMetadata)) + if (await _movieRepository.UpdateTitles(existingMetadata, fullMetadata.Title, fullMetadata.SortTitle)) { result.IsUpdated = true; } diff --git a/ErsatzTV.Scanner/Core/Plex/PlexOtherVideoLibraryScanner.cs b/ErsatzTV.Scanner/Core/Plex/PlexOtherVideoLibraryScanner.cs index ffac33c50..a4af33c1b 100644 --- a/ErsatzTV.Scanner/Core/Plex/PlexOtherVideoLibraryScanner.cs +++ b/ErsatzTV.Scanner/Core/Plex/PlexOtherVideoLibraryScanner.cs @@ -352,17 +352,15 @@ public class PlexOtherVideoLibraryScanner : result.IsUpdated = true; } - /* - if (fullMetadata.SortTitle != existingMetadata.SortTitle) + if (fullMetadata.SortTitle != existingMetadata.SortTitle || fullMetadata.Title != existingMetadata.Title) { + existingMetadata.Title = fullMetadata.Title; existingMetadata.SortTitle = fullMetadata.SortTitle; - // Not existing on IOtherVideoRepository - if (await _otherVideoRepository.UpdateSortTitle(existingMetadata)) + if (await _otherVideoRepository.UpdateTitles(existingMetadata, fullMetadata.Title, fullMetadata.SortTitle)) { result.IsUpdated = true; } } - */ bool poster = await UpdateArtworkIfNeeded(existingMetadata, fullMetadata, ArtworkKind.Poster); bool fanArt = await UpdateArtworkIfNeeded(existingMetadata, fullMetadata, ArtworkKind.FanArt);