Browse Source

rework bugsnag integration (#671)

pull/672/head
Jason Dove 4 years ago committed by GitHub
parent
commit
ec1b2502f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      ErsatzTV.Application/ErsatzTV.Application.csproj
  2. 15
      ErsatzTV.Application/Streaming/HlsSessionWorker.cs
  3. 1
      ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj
  4. 3
      ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs
  5. 6
      ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs
  6. 4
      ErsatzTV.Core.Tests/Metadata/LocalStatisticsProviderTests.cs
  7. 3
      ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs
  8. 1
      ErsatzTV.Core/ErsatzTV.Core.csproj
  9. 5
      ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs
  10. 31
      ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs
  11. 7
      ErsatzTV.Core/Metadata/LocalFolderScanner.cs
  12. 10
      ErsatzTV.Core/Metadata/LocalMetadataProvider.cs
  13. 7
      ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs
  14. 9
      ErsatzTV.Core/Metadata/MovieFolderScanner.cs
  15. 11
      ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs
  16. 8
      ErsatzTV.Core/Metadata/OtherVideoFolderScanner.cs
  17. 9
      ErsatzTV.Core/Metadata/SongFolderScanner.cs
  18. 5
      ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs
  19. 14
      ErsatzTV/Services/EmbyService.cs
  20. 15
      ErsatzTV/Services/FFmpegWorkerService.cs
  21. 14
      ErsatzTV/Services/JellyfinService.cs
  22. 14
      ErsatzTV/Services/PlexService.cs
  23. 27
      ErsatzTV/Services/SchedulerService.cs
  24. 14
      ErsatzTV/Services/WorkerService.cs
  25. 1
      ErsatzTV/Startup.cs

1
ErsatzTV.Application/ErsatzTV.Application.csproj

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Bugsnag" Version="3.0.0" />
<PackageReference Include="MediatR" Version="10.0.1" /> <PackageReference Include="MediatR" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.1.46"> <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.1.46">

15
ErsatzTV.Application/Streaming/HlsSessionWorker.cs

@ -1,5 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.Timers; using System.Timers;
using Bugsnag;
using ErsatzTV.Application.Channels; using ErsatzTV.Application.Channels;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
@ -122,6 +123,8 @@ public class HlsSessionWorker : IHlsSessionWorker
bool realtime, bool realtime,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
using IServiceScope scope = _serviceScopeFactory.CreateScope();
try try
{ {
if (!realtime) if (!realtime)
@ -136,7 +139,6 @@ public class HlsSessionWorker : IHlsSessionWorker
channelNumber); channelNumber);
} }
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>(); IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
long ptsOffset = await GetPtsOffset(mediator, channelNumber, cancellationToken); long ptsOffset = await GetPtsOffset(mediator, channelNumber, cancellationToken);
@ -200,6 +202,17 @@ public class HlsSessionWorker : IHlsSessionWorker
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Error transcoding channel {Channel}", channelNumber); _logger.LogError(ex, "Error transcoding channel {Channel}", channelNumber);
try
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
catch (Exception)
{
// do nothing
}
return false; return false;
} }
finally finally

1
ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Bugsnag" Version="3.0.0" />
<PackageReference Include="FluentAssertions" Version="6.5.1" /> <PackageReference Include="FluentAssertions" Version="6.5.1" />
<PackageReference Include="LanguageExt.Core" Version="4.0.3" /> <PackageReference Include="LanguageExt.Core" Version="4.0.3" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />

3
ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs

@ -1,6 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Bugsnag;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler; using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.FFmpeg; using ErsatzTV.Core.FFmpeg;
@ -248,6 +249,7 @@ public class TranscodingTests
new FakeStreamSelector(), new FakeStreamSelector(),
imageCache.Object, imageCache.Object,
new Mock<ITempFilePool>().Object, new Mock<ITempFilePool>().Object,
new Mock<IClient>().Object,
LoggerFactory.CreateLogger<FFmpegProcessService>()); LoggerFactory.CreateLogger<FFmpegProcessService>());
var service = new FFmpegLibraryProcessService( var service = new FFmpegLibraryProcessService(
@ -276,6 +278,7 @@ public class TranscodingTests
var localStatisticsProvider = new LocalStatisticsProvider( var localStatisticsProvider = new LocalStatisticsProvider(
metadataRepository.Object, metadataRepository.Object,
new LocalFileSystem(LoggerFactory.CreateLogger<LocalFileSystem>()), new LocalFileSystem(LoggerFactory.CreateLogger<LocalFileSystem>()),
new Mock<IClient>().Object,
LoggerFactory.CreateLogger<LocalStatisticsProvider>()); LoggerFactory.CreateLogger<LocalStatisticsProvider>());
await localStatisticsProvider.RefreshStatistics( await localStatisticsProvider.RefreshStatistics(

6
ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs

@ -1,6 +1,8 @@
using ErsatzTV.Core.Domain; using Bugsnag;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Metadata; using ErsatzTV.Core.Metadata;
using FluentAssertions; using FluentAssertions;
using Moq;
using NUnit.Framework; using NUnit.Framework;
namespace ErsatzTV.Core.Tests.Metadata; namespace ErsatzTV.Core.Tests.Metadata;
@ -9,7 +11,7 @@ namespace ErsatzTV.Core.Tests.Metadata;
public class FallbackMetadataProviderTests public class FallbackMetadataProviderTests
{ {
[SetUp] [SetUp]
public void SetUp() => _fallbackMetadataProvider = new FallbackMetadataProvider(); public void SetUp() => _fallbackMetadataProvider = new FallbackMetadataProvider(new Mock<IClient>().Object);
private FallbackMetadataProvider _fallbackMetadataProvider; private FallbackMetadataProvider _fallbackMetadataProvider;

4
ErsatzTV.Core.Tests/Metadata/LocalStatisticsProviderTests.cs

@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain; using Bugsnag;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Metadata; using ErsatzTV.Core.Metadata;
@ -20,6 +21,7 @@ public class LocalStatisticsProviderTests
var provider = new LocalStatisticsProvider( var provider = new LocalStatisticsProvider(
new Mock<IMetadataRepository>().Object, new Mock<IMetadataRepository>().Object,
new Mock<ILocalFileSystem>().Object, new Mock<ILocalFileSystem>().Object,
new Mock<IClient>().Object,
new Mock<ILogger<LocalStatisticsProvider>>().Object); new Mock<ILogger<LocalStatisticsProvider>>().Object);
var input = new LocalStatisticsProvider.FFprobe( var input = new LocalStatisticsProvider.FFprobe(

3
ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs

@ -1,4 +1,5 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Bugsnag;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Images; using ErsatzTV.Core.Interfaces.Images;
@ -587,6 +588,7 @@ public class MovieFolderScannerTests
new Mock<IMediator>().Object, new Mock<IMediator>().Object,
null, null,
new Mock<ITempFilePool>().Object, new Mock<ITempFilePool>().Object,
new Mock<IClient>().Object,
new Mock<ILogger<MovieFolderScanner>>().Object new Mock<ILogger<MovieFolderScanner>>().Object
); );
@ -605,6 +607,7 @@ public class MovieFolderScannerTests
new Mock<IMediator>().Object, new Mock<IMediator>().Object,
null, null,
new Mock<ITempFilePool>().Object, new Mock<ITempFilePool>().Object,
new Mock<IClient>().Object,
new Mock<ILogger<MovieFolderScanner>>().Object new Mock<ILogger<MovieFolderScanner>>().Object
); );
} }

1
ErsatzTV.Core/ErsatzTV.Core.csproj

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Bugsnag" Version="3.0.0" />
<PackageReference Include="Flurl" Version="3.0.4" /> <PackageReference Include="Flurl" Version="3.0.4" />
<PackageReference Include="LanguageExt.Core" Version="4.0.3" /> <PackageReference Include="LanguageExt.Core" Version="4.0.3" />
<PackageReference Include="MediatR" Version="10.0.1" /> <PackageReference Include="MediatR" Version="10.0.1" />

5
ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

@ -1,4 +1,5 @@
using System.Diagnostics; using System.Diagnostics;
using Bugsnag;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler; using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
@ -13,6 +14,7 @@ public class FFmpegProcessService : IFFmpegProcessService
private readonly IFFmpegStreamSelector _ffmpegStreamSelector; private readonly IFFmpegStreamSelector _ffmpegStreamSelector;
private readonly IImageCache _imageCache; private readonly IImageCache _imageCache;
private readonly ITempFilePool _tempFilePool; private readonly ITempFilePool _tempFilePool;
private readonly IClient _client;
private readonly ILogger<FFmpegProcessService> _logger; private readonly ILogger<FFmpegProcessService> _logger;
private readonly FFmpegPlaybackSettingsCalculator _playbackSettingsCalculator; private readonly FFmpegPlaybackSettingsCalculator _playbackSettingsCalculator;
@ -21,12 +23,14 @@ public class FFmpegProcessService : IFFmpegProcessService
IFFmpegStreamSelector ffmpegStreamSelector, IFFmpegStreamSelector ffmpegStreamSelector,
IImageCache imageCache, IImageCache imageCache,
ITempFilePool tempFilePool, ITempFilePool tempFilePool,
IClient client,
ILogger<FFmpegProcessService> logger) ILogger<FFmpegProcessService> logger)
{ {
_playbackSettingsCalculator = ffmpegPlaybackSettingsService; _playbackSettingsCalculator = ffmpegPlaybackSettingsService;
_ffmpegStreamSelector = ffmpegStreamSelector; _ffmpegStreamSelector = ffmpegStreamSelector;
_imageCache = imageCache; _imageCache = imageCache;
_tempFilePool = tempFilePool; _tempFilePool = tempFilePool;
_client = client;
_logger = logger; _logger = logger;
} }
@ -408,6 +412,7 @@ public class FFmpegProcessService : IFFmpegProcessService
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Error generating song image"); _logger.LogWarning(ex, "Error generating song image");
_client.Notify(ex);
return Left(BaseError.New(ex.Message)); return Left(BaseError.New(ex.Message));
} }
} }

31
ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs

@ -1,4 +1,5 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Bugsnag;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Metadata;
@ -6,6 +7,13 @@ namespace ErsatzTV.Core.Metadata;
public class FallbackMetadataProvider : IFallbackMetadataProvider public class FallbackMetadataProvider : IFallbackMetadataProvider
{ {
private readonly IClient _client;
public FallbackMetadataProvider(IClient client)
{
_client = client;
}
public ShowMetadata GetFallbackMetadataForShow(string showFolder) public ShowMetadata GetFallbackMetadataForShow(string showFolder)
{ {
string fileName = Path.GetFileName(showFolder); string fileName = Path.GetFileName(showFolder);
@ -142,7 +150,7 @@ public class FallbackMetadataProvider : IFallbackMetadataProvider
return title; return title;
} }
private static List<EpisodeMetadata> GetEpisodeMetadata(string fileName, EpisodeMetadata baseMetadata) private List<EpisodeMetadata> GetEpisodeMetadata(string fileName, EpisodeMetadata baseMetadata)
{ {
var result = new List<EpisodeMetadata>(); var result = new List<EpisodeMetadata>();
@ -186,9 +194,9 @@ public class FallbackMetadataProvider : IFallbackMetadataProvider
return result; return result;
} }
} }
catch (Exception) catch (Exception ex)
{ {
// ignored _client.Notify(ex);
} }
return result; return result;
@ -208,9 +216,9 @@ public class FallbackMetadataProvider : IFallbackMetadataProvider
metadata.DateUpdated = DateTime.UtcNow; metadata.DateUpdated = DateTime.UtcNow;
} }
} }
catch (Exception) catch (Exception ex)
{ {
// ignored _client.Notify(ex);
} }
return metadata; return metadata;
@ -232,8 +240,9 @@ public class FallbackMetadataProvider : IFallbackMetadataProvider
return metadata; return metadata;
} }
catch (Exception) catch (Exception ex)
{ {
_client.Notify(ex);
return None; return None;
} }
} }
@ -269,8 +278,9 @@ public class FallbackMetadataProvider : IFallbackMetadataProvider
return metadata; return metadata;
} }
catch (Exception) catch (Exception ex)
{ {
_client.Notify(ex);
return None; return None;
} }
} }
@ -306,8 +316,9 @@ public class FallbackMetadataProvider : IFallbackMetadataProvider
return metadata; return metadata;
} }
catch (Exception) catch (Exception ex)
{ {
_client.Notify(ex);
return None; return None;
} }
} }
@ -326,9 +337,9 @@ public class FallbackMetadataProvider : IFallbackMetadataProvider
metadata.DateUpdated = DateTime.UtcNow; metadata.DateUpdated = DateTime.UtcNow;
} }
} }
catch (Exception) catch (Exception ex)
{ {
// ignored _client.Notify(ex);
} }
return metadata; return metadata;

7
ErsatzTV.Core/Metadata/LocalFolderScanner.cs

@ -1,4 +1,5 @@
using System.Diagnostics; using System.Diagnostics;
using Bugsnag;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.FFmpeg; using ErsatzTV.Core.FFmpeg;
@ -46,6 +47,7 @@ public abstract class LocalFolderScanner
private readonly IImageCache _imageCache; private readonly IImageCache _imageCache;
private readonly IFFmpegProcessServiceFactory _ffmpegProcessServiceFactory; private readonly IFFmpegProcessServiceFactory _ffmpegProcessServiceFactory;
private readonly ITempFilePool _tempFilePool; private readonly ITempFilePool _tempFilePool;
private readonly IClient _client;
private readonly ILocalFileSystem _localFileSystem; private readonly ILocalFileSystem _localFileSystem;
private readonly ILocalStatisticsProvider _localStatisticsProvider; private readonly ILocalStatisticsProvider _localStatisticsProvider;
@ -61,6 +63,7 @@ public abstract class LocalFolderScanner
IImageCache imageCache, IImageCache imageCache,
IFFmpegProcessServiceFactory ffmpegProcessServiceFactory, IFFmpegProcessServiceFactory ffmpegProcessServiceFactory,
ITempFilePool tempFilePool, ITempFilePool tempFilePool,
IClient client,
ILogger logger) ILogger logger)
{ {
_localFileSystem = localFileSystem; _localFileSystem = localFileSystem;
@ -70,6 +73,7 @@ public abstract class LocalFolderScanner
_imageCache = imageCache; _imageCache = imageCache;
_ffmpegProcessServiceFactory = ffmpegProcessServiceFactory; _ffmpegProcessServiceFactory = ffmpegProcessServiceFactory;
_tempFilePool = tempFilePool; _tempFilePool = tempFilePool;
_client = client;
_logger = logger; _logger = logger;
} }
@ -109,6 +113,7 @@ public abstract class LocalFolderScanner
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.Message); return BaseError.New(ex.Message);
} }
} }
@ -258,6 +263,7 @@ public abstract class LocalFolderScanner
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Error refreshing artwork"); _logger.LogWarning(ex, "Error refreshing artwork");
_client.Notify(ex);
} }
} }
@ -283,6 +289,7 @@ public abstract class LocalFolderScanner
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }

10
ErsatzTV.Core/Metadata/LocalMetadataProvider.cs

@ -1,4 +1,5 @@
using System.Xml.Serialization; using System.Xml.Serialization;
using Bugsnag;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Metadata;
@ -18,6 +19,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
private readonly IArtistRepository _artistRepository; private readonly IArtistRepository _artistRepository;
private readonly IEpisodeNfoReader _episodeNfoReader; private readonly IEpisodeNfoReader _episodeNfoReader;
private readonly ILocalStatisticsProvider _localStatisticsProvider; private readonly ILocalStatisticsProvider _localStatisticsProvider;
private readonly IClient _client;
private readonly IFallbackMetadataProvider _fallbackMetadataProvider; private readonly IFallbackMetadataProvider _fallbackMetadataProvider;
private readonly ILocalFileSystem _localFileSystem; private readonly ILocalFileSystem _localFileSystem;
private readonly ILogger<LocalMetadataProvider> _logger; private readonly ILogger<LocalMetadataProvider> _logger;
@ -41,6 +43,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
ILocalFileSystem localFileSystem, ILocalFileSystem localFileSystem,
IEpisodeNfoReader episodeNfoReader, IEpisodeNfoReader episodeNfoReader,
ILocalStatisticsProvider localStatisticsProvider, ILocalStatisticsProvider localStatisticsProvider,
IClient client,
ILogger<LocalMetadataProvider> logger) ILogger<LocalMetadataProvider> logger)
{ {
_metadataRepository = metadataRepository; _metadataRepository = metadataRepository;
@ -54,6 +57,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
_localFileSystem = localFileSystem; _localFileSystem = localFileSystem;
_episodeNfoReader = episodeNfoReader; _episodeNfoReader = episodeNfoReader;
_localStatisticsProvider = localStatisticsProvider; _localStatisticsProvider = localStatisticsProvider;
_client = client;
_logger = logger; _logger = logger;
} }
@ -189,6 +193,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogInformation(ex, "Failed to read music video nfo metadata from {Path}", nfoFileName); _logger.LogInformation(ex, "Failed to read music video nfo metadata from {Path}", nfoFileName);
_client.Notify(ex);
return None; return None;
} }
} }
@ -274,6 +279,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogInformation(ex, "Failed to read embedded song metadata from {Path}", path); _logger.LogInformation(ex, "Failed to read embedded song metadata from {Path}", path);
_client.Notify(ex);
return None; return None;
} }
} }
@ -839,6 +845,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogInformation(ex, "Failed to read TV show nfo metadata from {Path}", nfoFileName); _logger.LogInformation(ex, "Failed to read TV show nfo metadata from {Path}", nfoFileName);
_client.Notify(ex);
return None; return None;
} }
} }
@ -867,6 +874,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogInformation(ex, "Failed to read artist nfo metadata from {Path}", nfoFileName); _logger.LogInformation(ex, "Failed to read artist nfo metadata from {Path}", nfoFileName);
_client.Notify(ex);
return None; return None;
} }
} }
@ -914,6 +922,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogInformation(ex, "Failed to read TV episode nfo metadata from {Path}", nfoFileName); _logger.LogInformation(ex, "Failed to read TV episode nfo metadata from {Path}", nfoFileName);
_client.Notify(ex);
return _fallbackMetadataProvider.GetFallbackMetadata(episode); return _fallbackMetadataProvider.GetFallbackMetadata(episode);
} }
} }
@ -958,6 +967,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogInformation(ex, "Failed to read Movie nfo metadata from {Path}", nfoFileName); _logger.LogInformation(ex, "Failed to read Movie nfo metadata from {Path}", nfoFileName);
_client.Notify(ex);
return _fallbackMetadataProvider.GetFallbackMetadata(movie); return _fallbackMetadataProvider.GetFallbackMetadata(movie);
} }
} }

7
ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs

@ -1,6 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using Bugsnag;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Metadata;
@ -13,16 +14,19 @@ namespace ErsatzTV.Core.Metadata;
public class LocalStatisticsProvider : ILocalStatisticsProvider public class LocalStatisticsProvider : ILocalStatisticsProvider
{ {
private readonly ILocalFileSystem _localFileSystem; private readonly ILocalFileSystem _localFileSystem;
private readonly IClient _client;
private readonly ILogger<LocalStatisticsProvider> _logger; private readonly ILogger<LocalStatisticsProvider> _logger;
private readonly IMetadataRepository _metadataRepository; private readonly IMetadataRepository _metadataRepository;
public LocalStatisticsProvider( public LocalStatisticsProvider(
IMetadataRepository metadataRepository, IMetadataRepository metadataRepository,
ILocalFileSystem localFileSystem, ILocalFileSystem localFileSystem,
IClient client,
ILogger<LocalStatisticsProvider> logger) ILogger<LocalStatisticsProvider> logger)
{ {
_metadataRepository = metadataRepository; _metadataRepository = metadataRepository;
_localFileSystem = localFileSystem; _localFileSystem = localFileSystem;
_client = client;
_logger = logger; _logger = logger;
} }
@ -36,6 +40,7 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Failed to refresh statistics for media item {Id}", mediaItem.Id); _logger.LogWarning(ex, "Failed to refresh statistics for media item {Id}", mediaItem.Id);
_client.Notify(ex);
return BaseError.New(ex.Message); return BaseError.New(ex.Message);
} }
} }
@ -60,6 +65,7 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Failed to refresh statistics for media item {Id}", mediaItem.Id); _logger.LogWarning(ex, "Failed to refresh statistics for media item {Id}", mediaItem.Id);
_client.Notify(ex);
return BaseError.New(ex.Message); return BaseError.New(ex.Message);
} }
} }
@ -114,6 +120,7 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Failed to get format tags for media item {Id}", mediaItem.Id); _logger.LogWarning(ex, "Failed to get format tags for media item {Id}", mediaItem.Id);
_client.Notify(ex);
return BaseError.New(ex.Message); return BaseError.New(ex.Message);
} }
} }

9
ErsatzTV.Core/Metadata/MovieFolderScanner.cs

@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain; using Bugsnag;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg; using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Images; using ErsatzTV.Core.Interfaces.Images;
@ -18,6 +19,7 @@ public class MovieFolderScanner : LocalFolderScanner, IMovieFolderScanner
private readonly ILocalMetadataProvider _localMetadataProvider; private readonly ILocalMetadataProvider _localMetadataProvider;
private readonly ILogger<MovieFolderScanner> _logger; private readonly ILogger<MovieFolderScanner> _logger;
private readonly IMediator _mediator; private readonly IMediator _mediator;
private readonly IClient _client;
private readonly IMovieRepository _movieRepository; private readonly IMovieRepository _movieRepository;
private readonly ISearchIndex _searchIndex; private readonly ISearchIndex _searchIndex;
private readonly ISearchRepository _searchRepository; private readonly ISearchRepository _searchRepository;
@ -36,6 +38,7 @@ public class MovieFolderScanner : LocalFolderScanner, IMovieFolderScanner
IMediator mediator, IMediator mediator,
IFFmpegProcessServiceFactory ffmpegProcessServiceFactory, IFFmpegProcessServiceFactory ffmpegProcessServiceFactory,
ITempFilePool tempFilePool, ITempFilePool tempFilePool,
IClient client,
ILogger<MovieFolderScanner> logger) ILogger<MovieFolderScanner> logger)
: base( : base(
localFileSystem, localFileSystem,
@ -45,6 +48,7 @@ public class MovieFolderScanner : LocalFolderScanner, IMovieFolderScanner
imageCache, imageCache,
ffmpegProcessServiceFactory, ffmpegProcessServiceFactory,
tempFilePool, tempFilePool,
client,
logger) logger)
{ {
_localFileSystem = localFileSystem; _localFileSystem = localFileSystem;
@ -54,6 +58,7 @@ public class MovieFolderScanner : LocalFolderScanner, IMovieFolderScanner
_searchRepository = searchRepository; _searchRepository = searchRepository;
_libraryRepository = libraryRepository; _libraryRepository = libraryRepository;
_mediator = mediator; _mediator = mediator;
_client = client;
_logger = logger; _logger = logger;
} }
@ -216,6 +221,7 @@ public class MovieFolderScanner : LocalFolderScanner, IMovieFolderScanner
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }
@ -238,6 +244,7 @@ public class MovieFolderScanner : LocalFolderScanner, IMovieFolderScanner
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }

11
ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs

@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain; using Bugsnag;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg; using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Images; using ErsatzTV.Core.Interfaces.Images;
@ -18,6 +19,7 @@ public class MusicVideoFolderScanner : LocalFolderScanner, IMusicVideoFolderScan
private readonly ILocalMetadataProvider _localMetadataProvider; private readonly ILocalMetadataProvider _localMetadataProvider;
private readonly ILogger<MusicVideoFolderScanner> _logger; private readonly ILogger<MusicVideoFolderScanner> _logger;
private readonly IMediator _mediator; private readonly IMediator _mediator;
private readonly IClient _client;
private readonly IMusicVideoRepository _musicVideoRepository; private readonly IMusicVideoRepository _musicVideoRepository;
private readonly ISearchIndex _searchIndex; private readonly ISearchIndex _searchIndex;
private readonly ISearchRepository _searchRepository; private readonly ISearchRepository _searchRepository;
@ -37,6 +39,7 @@ public class MusicVideoFolderScanner : LocalFolderScanner, IMusicVideoFolderScan
IMediator mediator, IMediator mediator,
IFFmpegProcessServiceFactory ffmpegProcessServiceFactory, IFFmpegProcessServiceFactory ffmpegProcessServiceFactory,
ITempFilePool tempFilePool, ITempFilePool tempFilePool,
IClient client,
ILogger<MusicVideoFolderScanner> logger) : base( ILogger<MusicVideoFolderScanner> logger) : base(
localFileSystem, localFileSystem,
localStatisticsProvider, localStatisticsProvider,
@ -45,6 +48,7 @@ public class MusicVideoFolderScanner : LocalFolderScanner, IMusicVideoFolderScan
imageCache, imageCache,
ffmpegProcessServiceFactory, ffmpegProcessServiceFactory,
tempFilePool, tempFilePool,
client,
logger) logger)
{ {
_localFileSystem = localFileSystem; _localFileSystem = localFileSystem;
@ -55,6 +59,7 @@ public class MusicVideoFolderScanner : LocalFolderScanner, IMusicVideoFolderScan
_musicVideoRepository = musicVideoRepository; _musicVideoRepository = musicVideoRepository;
_libraryRepository = libraryRepository; _libraryRepository = libraryRepository;
_mediator = mediator; _mediator = mediator;
_client = client;
_logger = logger; _logger = logger;
} }
@ -197,6 +202,7 @@ public class MusicVideoFolderScanner : LocalFolderScanner, IMusicVideoFolderScan
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }
@ -220,6 +226,7 @@ public class MusicVideoFolderScanner : LocalFolderScanner, IMusicVideoFolderScan
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }
@ -335,6 +342,7 @@ public class MusicVideoFolderScanner : LocalFolderScanner, IMusicVideoFolderScan
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }
@ -384,6 +392,7 @@ public class MusicVideoFolderScanner : LocalFolderScanner, IMusicVideoFolderScan
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }

8
ErsatzTV.Core/Metadata/OtherVideoFolderScanner.cs

@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain; using Bugsnag;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg; using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Images; using ErsatzTV.Core.Interfaces.Images;
@ -19,6 +20,7 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
private readonly ISearchRepository _searchRepository; private readonly ISearchRepository _searchRepository;
private readonly IOtherVideoRepository _otherVideoRepository; private readonly IOtherVideoRepository _otherVideoRepository;
private readonly ILibraryRepository _libraryRepository; private readonly ILibraryRepository _libraryRepository;
private readonly IClient _client;
private readonly ILogger<OtherVideoFolderScanner> _logger; private readonly ILogger<OtherVideoFolderScanner> _logger;
public OtherVideoFolderScanner( public OtherVideoFolderScanner(
@ -35,6 +37,7 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
IMediaItemRepository mediaItemRepository, IMediaItemRepository mediaItemRepository,
IFFmpegProcessServiceFactory ffmpegProcessServiceFactory, IFFmpegProcessServiceFactory ffmpegProcessServiceFactory,
ITempFilePool tempFilePool, ITempFilePool tempFilePool,
IClient client,
ILogger<OtherVideoFolderScanner> logger) : base( ILogger<OtherVideoFolderScanner> logger) : base(
localFileSystem, localFileSystem,
localStatisticsProvider, localStatisticsProvider,
@ -43,6 +46,7 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
imageCache, imageCache,
ffmpegProcessServiceFactory, ffmpegProcessServiceFactory,
tempFilePool, tempFilePool,
client,
logger) logger)
{ {
_localFileSystem = localFileSystem; _localFileSystem = localFileSystem;
@ -52,6 +56,7 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
_searchRepository = searchRepository; _searchRepository = searchRepository;
_otherVideoRepository = otherVideoRepository; _otherVideoRepository = otherVideoRepository;
_libraryRepository = libraryRepository; _libraryRepository = libraryRepository;
_client = client;
_logger = logger; _logger = logger;
} }
@ -191,6 +196,7 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }

9
ErsatzTV.Core/Metadata/SongFolderScanner.cs

@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain; using Bugsnag;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.FFmpeg; using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
@ -20,6 +21,7 @@ public class SongFolderScanner : LocalFolderScanner, ISongFolderScanner
private readonly ISearchRepository _searchRepository; private readonly ISearchRepository _searchRepository;
private readonly ISongRepository _songRepository; private readonly ISongRepository _songRepository;
private readonly ILibraryRepository _libraryRepository; private readonly ILibraryRepository _libraryRepository;
private readonly IClient _client;
private readonly ILogger<SongFolderScanner> _logger; private readonly ILogger<SongFolderScanner> _logger;
public SongFolderScanner( public SongFolderScanner(
@ -36,6 +38,7 @@ public class SongFolderScanner : LocalFolderScanner, ISongFolderScanner
IMediaItemRepository mediaItemRepository, IMediaItemRepository mediaItemRepository,
IFFmpegProcessServiceFactory ffmpegProcessServiceFactory, IFFmpegProcessServiceFactory ffmpegProcessServiceFactory,
ITempFilePool tempFilePool, ITempFilePool tempFilePool,
IClient client,
ILogger<SongFolderScanner> logger) : base( ILogger<SongFolderScanner> logger) : base(
localFileSystem, localFileSystem,
localStatisticsProvider, localStatisticsProvider,
@ -44,6 +47,7 @@ public class SongFolderScanner : LocalFolderScanner, ISongFolderScanner
imageCache, imageCache,
ffmpegProcessServiceFactory, ffmpegProcessServiceFactory,
tempFilePool, tempFilePool,
client,
logger) logger)
{ {
_localFileSystem = localFileSystem; _localFileSystem = localFileSystem;
@ -53,6 +57,7 @@ public class SongFolderScanner : LocalFolderScanner, ISongFolderScanner
_searchRepository = searchRepository; _searchRepository = searchRepository;
_songRepository = songRepository; _songRepository = songRepository;
_libraryRepository = libraryRepository; _libraryRepository = libraryRepository;
_client = client;
_logger = logger; _logger = logger;
} }
@ -200,6 +205,7 @@ public class SongFolderScanner : LocalFolderScanner, ISongFolderScanner
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }
@ -236,6 +242,7 @@ public class SongFolderScanner : LocalFolderScanner, ISongFolderScanner
} }
catch (Exception ex) catch (Exception ex)
{ {
_client.Notify(ex);
return BaseError.New(ex.ToString()); return BaseError.New(ex.ToString());
} }
} }

5
ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs

@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain; using Bugsnag;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg; using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Images; using ErsatzTV.Core.Interfaces.Images;
@ -36,6 +37,7 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
IMediator mediator, IMediator mediator,
IFFmpegProcessServiceFactory ffmpegProcessServiceFactory, IFFmpegProcessServiceFactory ffmpegProcessServiceFactory,
ITempFilePool tempFilePool, ITempFilePool tempFilePool,
IClient client,
ILogger<TelevisionFolderScanner> logger) : base( ILogger<TelevisionFolderScanner> logger) : base(
localFileSystem, localFileSystem,
localStatisticsProvider, localStatisticsProvider,
@ -44,6 +46,7 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
imageCache, imageCache,
ffmpegProcessServiceFactory, ffmpegProcessServiceFactory,
tempFilePool, tempFilePool,
client,
logger) logger)
{ {
_localFileSystem = localFileSystem; _localFileSystem = localFileSystem;

14
ErsatzTV/Services/EmbyService.cs

@ -1,4 +1,5 @@
using System.Threading.Channels; using System.Threading.Channels;
using Bugsnag;
using ErsatzTV.Application; using ErsatzTV.Application;
using ErsatzTV.Application.Emby; using ErsatzTV.Application.Emby;
using ErsatzTV.Core; using ErsatzTV.Core;
@ -65,6 +66,19 @@ public class EmbyService : BackgroundService
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Failed to process Emby background service request"); _logger.LogWarning(ex, "Failed to process Emby background service request");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
}
catch (Exception)
{
// do nothing
}
} }
} }
} }

15
ErsatzTV/Services/FFmpegWorkerService.cs

@ -1,4 +1,5 @@
using System.Threading.Channels; using System.Threading.Channels;
using Bugsnag;
using ErsatzTV.Application; using ErsatzTV.Application;
using ErsatzTV.Application.Streaming; using ErsatzTV.Application.Streaming;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
@ -30,10 +31,10 @@ public class FFmpegWorkerService : BackgroundService
await foreach (IFFmpegWorkerRequest request in _channel.ReadAllAsync(cancellationToken)) await foreach (IFFmpegWorkerRequest request in _channel.ReadAllAsync(cancellationToken))
{ {
using IServiceScope scope = _serviceScopeFactory.CreateScope();
try try
{ {
using IServiceScope scope = _serviceScopeFactory.CreateScope();
// IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
switch (request) switch (request)
{ {
@ -49,6 +50,16 @@ public class FFmpegWorkerService : BackgroundService
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Failed to handle ffmpeg worker request"); _logger.LogWarning(ex, "Failed to handle ffmpeg worker request");
try
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
catch (Exception)
{
// do nothing
}
} }
} }
} }

14
ErsatzTV/Services/JellyfinService.cs

@ -1,4 +1,5 @@
using System.Threading.Channels; using System.Threading.Channels;
using Bugsnag;
using ErsatzTV.Application; using ErsatzTV.Application;
using ErsatzTV.Application.Jellyfin; using ErsatzTV.Application.Jellyfin;
using ErsatzTV.Core; using ErsatzTV.Core;
@ -65,6 +66,19 @@ public class JellyfinService : BackgroundService
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Failed to process Jellyfin background service request"); _logger.LogWarning(ex, "Failed to process Jellyfin background service request");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
}
catch (Exception)
{
// do nothing
}
} }
} }
} }

14
ErsatzTV/Services/PlexService.cs

@ -1,4 +1,5 @@
using System.Threading.Channels; using System.Threading.Channels;
using Bugsnag;
using ErsatzTV.Application; using ErsatzTV.Application;
using ErsatzTV.Application.Plex; using ErsatzTV.Application.Plex;
using ErsatzTV.Core; using ErsatzTV.Core;
@ -65,6 +66,19 @@ public class PlexService : BackgroundService
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Failed to process plex background service request"); _logger.LogWarning(ex, "Failed to process plex background service request");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
}
catch (Exception)
{
// do nothing
}
} }
} }
} }

27
ErsatzTV/Services/SchedulerService.cs

@ -1,4 +1,5 @@
using System.Threading.Channels; using System.Threading.Channels;
using Bugsnag;
using ErsatzTV.Application; using ErsatzTV.Application;
using ErsatzTV.Application.Maintenance; using ErsatzTV.Application.Maintenance;
using ErsatzTV.Application.MediaCollections; using ErsatzTV.Application.MediaCollections;
@ -83,6 +84,19 @@ public class SchedulerService : BackgroundService
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Error during scheduler run"); _logger.LogWarning(ex, "Error during scheduler run");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
}
catch (Exception)
{
// do nothing
}
} }
} }
@ -110,6 +124,19 @@ public class SchedulerService : BackgroundService
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Error during scheduler run"); _logger.LogWarning(ex, "Error during scheduler run");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
}
catch (Exception)
{
// do nothing
}
} }
} }

14
ErsatzTV/Services/WorkerService.cs

@ -1,4 +1,5 @@
using System.Threading.Channels; using System.Threading.Channels;
using Bugsnag;
using ErsatzTV.Application; using ErsatzTV.Application;
using ErsatzTV.Application.Maintenance; using ErsatzTV.Application.Maintenance;
using ErsatzTV.Application.MediaCollections; using ErsatzTV.Application.MediaCollections;
@ -32,9 +33,10 @@ public class WorkerService : BackgroundService
await foreach (IBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken)) await foreach (IBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
{ {
using IServiceScope scope = _serviceScopeFactory.CreateScope();
try try
{ {
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>(); IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
switch (request) switch (request)
@ -84,6 +86,16 @@ public class WorkerService : BackgroundService
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Failed to process background service request"); _logger.LogWarning(ex, "Failed to process background service request");
try
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
catch (Exception)
{
// do nothing
}
} }
} }
} }

1
ErsatzTV/Startup.cs

@ -85,6 +85,7 @@ public class Startup
configuration.AppVersion = Assembly.GetEntryAssembly() configuration.AppVersion = Assembly.GetEntryAssembly()
?.GetCustomAttribute<AssemblyInformationalVersionAttribute>() ?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion ?? "unknown"; ?.InformationalVersion ?? "unknown";
configuration.AutoNotify = false;
configuration.NotifyReleaseStages = new[] { "public", "develop" }; configuration.NotifyReleaseStages = new[] { "public", "develop" };

Loading…
Cancel
Save