mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-07-18 20:24:20 +01:00
Merge branch 'master' into providermanager-cleanup
This commit is contained in:
@@ -8,7 +8,7 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
@@ -655,8 +655,6 @@ namespace MediaBrowser.Providers.Manager
|
||||
};
|
||||
temp.Item.Path = item.Path;
|
||||
|
||||
var userDataList = new List<UserItemData>();
|
||||
|
||||
// If replacing all metadata, run internet providers first
|
||||
if (options.ReplaceAllMetadata)
|
||||
{
|
||||
@@ -670,7 +668,7 @@ namespace MediaBrowser.Providers.Manager
|
||||
|
||||
var hasLocalMetadata = false;
|
||||
|
||||
foreach (var provider in providers.OfType<ILocalMetadataProvider<TItemType>>().ToList())
|
||||
foreach (var provider in providers.OfType<ILocalMetadataProvider<TItemType>>())
|
||||
{
|
||||
var providerName = provider.GetType().Name;
|
||||
Logger.LogDebug("Running {Provider} for {Item}", providerName, logName);
|
||||
@@ -687,6 +685,11 @@ namespace MediaBrowser.Providers.Manager
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!options.IsReplacingImage(remoteImage.Type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await ProviderManager.SaveImage(item, remoteImage.Url, remoteImage.Type, null, cancellationToken).ConfigureAwait(false);
|
||||
refreshResult.UpdateType |= ItemUpdateType.ImageUpdate;
|
||||
}
|
||||
@@ -701,11 +704,6 @@ namespace MediaBrowser.Providers.Manager
|
||||
refreshResult.UpdateType |= ItemUpdateType.ImageUpdate;
|
||||
}
|
||||
|
||||
if (localItem.UserDataList != null)
|
||||
{
|
||||
userDataList.AddRange(localItem.UserDataList);
|
||||
}
|
||||
|
||||
MergeData(localItem, temp, Array.Empty<MetadataField>(), !options.ReplaceAllMetadata, true);
|
||||
refreshResult.UpdateType |= ItemUpdateType.MetadataImport;
|
||||
|
||||
@@ -764,15 +762,11 @@ namespace MediaBrowser.Providers.Manager
|
||||
}
|
||||
}
|
||||
|
||||
// var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0;
|
||||
|
||||
foreach (var provider in customProviders.Where(i => i is not IPreRefreshProvider))
|
||||
{
|
||||
await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// ImportUserData(item, userDataList, cancellationToken);
|
||||
|
||||
return refreshResult;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Priority_Queue;
|
||||
@@ -176,6 +177,12 @@ namespace MediaBrowser.Providers.Manager
|
||||
throw new HttpRequestException("Invalid image received.", null, HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
// some iptv/epg providers don't correctly report media type, extract from url if no extension found
|
||||
if (string.IsNullOrWhiteSpace(MimeTypes.ToExtension(contentType)))
|
||||
{
|
||||
contentType = MimeTypes.GetMimeType(url);
|
||||
}
|
||||
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
await SaveImage(
|
||||
item,
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="OptimizedPriorityQueue" Version="5.0.0" />
|
||||
<PackageReference Include="PlaylistsNET" Version="1.1.3" />
|
||||
<PackageReference Include="TMDbLib" Version="1.9.1" />
|
||||
<PackageReference Include="OptimizedPriorityQueue" Version="5.1.0" />
|
||||
<PackageReference Include="PlaylistsNET" Version="1.2.1" />
|
||||
<PackageReference Include="TMDbLib" Version="1.9.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -43,7 +43,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Providers.MediaInfo
|
||||
{
|
||||
@@ -15,16 +16,19 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AudioResolver"/> class for external audio file processing.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="localizationManager">The localization manager.</param>
|
||||
/// <param name="mediaEncoder">The media encoder.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="namingOptions">The <see cref="NamingOptions"/> object containing FileExtensions, MediaDefaultFlags, MediaForcedFlags and MediaFlagDelimiters.</param>
|
||||
public AudioResolver(
|
||||
ILogger<AudioResolver> logger,
|
||||
ILocalizationManager localizationManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
IFileSystem fileSystem,
|
||||
NamingOptions namingOptions)
|
||||
: base(
|
||||
logger,
|
||||
localizationManager,
|
||||
mediaEncoder,
|
||||
fileSystem,
|
||||
|
||||
@@ -47,7 +47,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
|
||||
|
||||
public FFProbeProvider(
|
||||
ILogger<FFProbeProvider> logger,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
IItemRepository itemRepo,
|
||||
@@ -59,11 +58,12 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
IChapterManager chapterManager,
|
||||
ILibraryManager libraryManager,
|
||||
IFileSystem fileSystem,
|
||||
ILoggerFactory loggerFactory,
|
||||
NamingOptions namingOptions)
|
||||
{
|
||||
_logger = logger;
|
||||
_audioResolver = new AudioResolver(localization, mediaEncoder, fileSystem, namingOptions);
|
||||
_subtitleResolver = new SubtitleResolver(localization, mediaEncoder, fileSystem, namingOptions);
|
||||
_logger = loggerFactory.CreateLogger<FFProbeProvider>();
|
||||
_audioResolver = new AudioResolver(loggerFactory.CreateLogger<AudioResolver>(), localization, mediaEncoder, fileSystem, namingOptions);
|
||||
_subtitleResolver = new SubtitleResolver(loggerFactory.CreateLogger<SubtitleResolver>(), localization, mediaEncoder, fileSystem, namingOptions);
|
||||
_videoProber = new FFProbeVideoInfo(
|
||||
_logger,
|
||||
mediaSourceManager,
|
||||
|
||||
@@ -173,16 +173,30 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
IReadOnlyList<MediaAttachment> mediaAttachments;
|
||||
ChapterInfo[] chapters;
|
||||
|
||||
mediaStreams = new List<MediaStream>();
|
||||
|
||||
// Add external streams before adding the streams from the file to preserve stream IDs on remote videos
|
||||
await AddExternalSubtitlesAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var startIndex = mediaStreams.Count == 0 ? 0 : (mediaStreams.Max(i => i.Index) + 1);
|
||||
|
||||
if (mediaInfo != null)
|
||||
{
|
||||
mediaStreams = mediaInfo.MediaStreams.ToList();
|
||||
foreach (var mediaStream in mediaInfo.MediaStreams)
|
||||
{
|
||||
mediaStream.Index = startIndex++;
|
||||
mediaStreams.Add(mediaStream);
|
||||
}
|
||||
|
||||
mediaAttachments = mediaInfo.MediaAttachments;
|
||||
|
||||
video.TotalBitrate = mediaInfo.Bitrate;
|
||||
// video.FormatName = (mediaInfo.Container ?? string.Empty)
|
||||
// .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// For dvd's this may not always be accurate, so don't set the runtime if the item already has one
|
||||
// For DVDs this may not always be accurate, so don't set the runtime if the item already has one
|
||||
var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0;
|
||||
|
||||
if (needToSetRuntime)
|
||||
@@ -213,15 +227,20 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
}
|
||||
else
|
||||
{
|
||||
mediaStreams = new List<MediaStream>();
|
||||
var currentMediaStreams = video.GetMediaStreams();
|
||||
foreach (var mediaStream in currentMediaStreams)
|
||||
{
|
||||
if (!mediaStream.IsExternal)
|
||||
{
|
||||
mediaStream.Index = startIndex++;
|
||||
mediaStreams.Add(mediaStream);
|
||||
}
|
||||
}
|
||||
|
||||
mediaAttachments = Array.Empty<MediaAttachment>();
|
||||
chapters = Array.Empty<ChapterInfo>();
|
||||
}
|
||||
|
||||
await AddExternalSubtitlesAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var libraryOptions = _libraryManager.GetLibraryOptions(video);
|
||||
|
||||
if (mediaInfo != null)
|
||||
@@ -254,7 +273,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
|
||||
|
||||
_itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);
|
||||
_itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
|
||||
|
||||
if (mediaAttachments.Any())
|
||||
{
|
||||
_itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
|
||||
}
|
||||
|
||||
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
|
||||
options.MetadataRefreshMode == MetadataRefreshMode.Default)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -16,6 +15,7 @@ using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Providers.MediaInfo
|
||||
{
|
||||
@@ -34,6 +34,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
/// </summary>
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
@@ -49,18 +50,21 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MediaInfoResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="localizationManager">The localization manager.</param>
|
||||
/// <param name="mediaEncoder">The media encoder.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="namingOptions">The <see cref="NamingOptions"/> object containing FileExtensions, MediaDefaultFlags, MediaForcedFlags and MediaFlagDelimiters.</param>
|
||||
/// <param name="type">The <see cref="DlnaProfileType"/> of the parsed file.</param>
|
||||
protected MediaInfoResolver(
|
||||
ILogger logger,
|
||||
ILocalizationManager localizationManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
IFileSystem fileSystem,
|
||||
NamingOptions namingOptions,
|
||||
DlnaProfileType type)
|
||||
{
|
||||
_logger = logger;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_fileSystem = fileSystem;
|
||||
_namingOptions = namingOptions;
|
||||
@@ -102,25 +106,43 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
{
|
||||
if (!pathInfo.Path.AsSpan().EndsWith(".strm", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var mediaInfo = await GetMediaInfo(pathInfo.Path, _type, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (mediaInfo.MediaStreams.Count == 1)
|
||||
try
|
||||
{
|
||||
MediaStream mediaStream = mediaInfo.MediaStreams[0];
|
||||
mediaStream.Index = startIndex++;
|
||||
mediaStream.IsDefault = pathInfo.IsDefault || mediaStream.IsDefault;
|
||||
mediaStream.IsForced = pathInfo.IsForced || mediaStream.IsForced;
|
||||
var mediaInfo = await GetMediaInfo(pathInfo.Path, _type, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
mediaStreams.Add(MergeMetadata(mediaStream, pathInfo));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (MediaStream mediaStream in mediaInfo.MediaStreams)
|
||||
if (mediaInfo.MediaStreams.Count == 1)
|
||||
{
|
||||
mediaStream.Index = startIndex++;
|
||||
MediaStream mediaStream = mediaInfo.MediaStreams[0];
|
||||
|
||||
mediaStreams.Add(MergeMetadata(mediaStream, pathInfo));
|
||||
if ((mediaStream.Type == MediaStreamType.Audio && _type == DlnaProfileType.Audio)
|
||||
|| (mediaStream.Type == MediaStreamType.Subtitle && _type == DlnaProfileType.Subtitle))
|
||||
{
|
||||
mediaStream.Index = startIndex++;
|
||||
mediaStream.IsDefault = pathInfo.IsDefault || mediaStream.IsDefault;
|
||||
mediaStream.IsForced = pathInfo.IsForced || mediaStream.IsForced;
|
||||
|
||||
mediaStreams.Add(MergeMetadata(mediaStream, pathInfo));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (MediaStream mediaStream in mediaInfo.MediaStreams)
|
||||
{
|
||||
if ((mediaStream.Type == MediaStreamType.Audio && _type == DlnaProfileType.Audio)
|
||||
|| (mediaStream.Type == MediaStreamType.Subtitle && _type == DlnaProfileType.Subtitle))
|
||||
{
|
||||
mediaStream.Index = startIndex++;
|
||||
|
||||
mediaStreams.Add(MergeMetadata(mediaStream, pathInfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting external streams from {Path}", pathInfo.Path);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,6 +175,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
}
|
||||
|
||||
var files = directoryService.GetFilePaths(folder, clearCache).ToList();
|
||||
files.Remove(video.Path);
|
||||
var internalMetadataPath = video.GetInternalMetadataPath();
|
||||
if (_fileSystem.DirectoryExists(internalMetadataPath))
|
||||
{
|
||||
@@ -222,13 +245,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
mediaStream.Title = string.IsNullOrEmpty(mediaStream.Title) ? (string.IsNullOrEmpty(pathInfo.Title) ? null : pathInfo.Title) : mediaStream.Title;
|
||||
mediaStream.Language = string.IsNullOrEmpty(mediaStream.Language) ? (string.IsNullOrEmpty(pathInfo.Language) ? null : pathInfo.Language) : mediaStream.Language;
|
||||
|
||||
mediaStream.Type = _type switch
|
||||
{
|
||||
DlnaProfileType.Audio => MediaStreamType.Audio,
|
||||
DlnaProfileType.Subtitle => MediaStreamType.Subtitle,
|
||||
_ => mediaStream.Type
|
||||
};
|
||||
|
||||
return mediaStream;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Providers.MediaInfo
|
||||
{
|
||||
@@ -15,16 +16,19 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SubtitleResolver"/> class for external subtitle file processing.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="localizationManager">The localization manager.</param>
|
||||
/// <param name="mediaEncoder">The media encoder.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="namingOptions">The <see cref="NamingOptions"/> object containing FileExtensions, MediaDefaultFlags, MediaForcedFlags and MediaFlagDelimiters.</param>
|
||||
public SubtitleResolver(
|
||||
ILogger<SubtitleResolver> logger,
|
||||
ILocalizationManager localizationManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
IFileSystem fileSystem,
|
||||
NamingOptions namingOptions)
|
||||
: base(
|
||||
logger,
|
||||
localizationManager,
|
||||
mediaEncoder,
|
||||
fileSystem,
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Music
|
||||
/// <summary>
|
||||
/// The Jellyfin user-agent is unrestricted but source IP must not exceed
|
||||
/// one request per second, therefore we rate limit to avoid throttling.
|
||||
/// Be prudent, use a value slightly above the minimun required.
|
||||
/// Be prudent, use a value slightly above the minimum required.
|
||||
/// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting.
|
||||
/// </summary>
|
||||
private readonly long _musicBrainzQueryIntervalMs;
|
||||
@@ -465,7 +465,8 @@ namespace MediaBrowser.Providers.Music
|
||||
ValidationType = ValidationType.None,
|
||||
CheckCharacters = false,
|
||||
IgnoreProcessingInstructions = true,
|
||||
IgnoreComments = true
|
||||
IgnoreComments = true,
|
||||
Async = true
|
||||
};
|
||||
|
||||
using var reader = XmlReader.Create(oReader, settings);
|
||||
|
||||
@@ -13,7 +13,7 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using MediaBrowser.Model.Plugins;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.StudioImages
|
||||
namespace MediaBrowser.Providers.Plugins.StudioImages.Configuration
|
||||
{
|
||||
public class PluginConfiguration : BasePluginConfiguration
|
||||
{
|
||||
@@ -12,12 +12,19 @@ namespace MediaBrowser.Providers.Plugins.StudioImages
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_repository))
|
||||
{
|
||||
_repository = Plugin.DefaultServer;
|
||||
}
|
||||
|
||||
return _repository;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_repository = value.TrimEnd('/');
|
||||
_repository = string.IsNullOrEmpty(value)
|
||||
? Plugin.DefaultServer
|
||||
: value.TrimEnd('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<div class="content-primary">
|
||||
<form class="configForm">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="repository" required label="Repository" />
|
||||
<div class="fieldDescription">This can be any Jellyfin-compatible artwork repository.</div>
|
||||
<input is="emby-input" type="text" id="repository" label="Repository" />
|
||||
<div class="fieldDescription">This can be any Jellyfin-compatible artwork repository. Leave blank to use default repository.</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
@@ -44,7 +44,7 @@
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
|
||||
config.RepositoryUrl = document.querySelector('#server').value;
|
||||
config.RepositoryUrl = document.querySelector('#repository').value;
|
||||
|
||||
ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Providers.Plugins.StudioImages.Configuration;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.StudioImages
|
||||
{
|
||||
|
||||
@@ -18,29 +18,24 @@ using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Plugins.StudioImages;
|
||||
|
||||
namespace MediaBrowser.Providers.Studios
|
||||
namespace MediaBrowser.Providers.Plugins.StudioImages
|
||||
{
|
||||
public class StudiosImageProvider : IRemoteImageProvider
|
||||
{
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly string repositoryUrl;
|
||||
|
||||
public StudiosImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory, IFileSystem fileSystem)
|
||||
{
|
||||
_config = config;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_fileSystem = fileSystem;
|
||||
repositoryUrl = Plugin.Instance.Configuration.RepositoryUrl;
|
||||
}
|
||||
|
||||
public string Name => "Artwork Repository";
|
||||
|
||||
public int Order => 0;
|
||||
|
||||
public bool Supports(BaseItem item)
|
||||
{
|
||||
return item is Studio;
|
||||
@@ -98,12 +93,12 @@ namespace MediaBrowser.Providers.Studios
|
||||
|
||||
private string GetUrl(string image, string filename)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}/images/{1}/{2}.jpg", repositoryUrl, image, filename);
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}/images/{1}/{2}.jpg", GetRepositoryUrl(), image, filename);
|
||||
}
|
||||
|
||||
private Task<string> EnsureThumbsList(string file, CancellationToken cancellationToken)
|
||||
{
|
||||
string url = string.Format(CultureInfo.InvariantCulture, "{0}/thumbs.txt", repositoryUrl);
|
||||
string url = string.Format(CultureInfo.InvariantCulture, "{0}/thumbs.txt", GetRepositoryUrl());
|
||||
|
||||
return EnsureList(url, file, _fileSystem, cancellationToken);
|
||||
}
|
||||
@@ -169,5 +164,8 @@ namespace MediaBrowser.Providers.Studios
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetRepositoryUrl()
|
||||
=> Plugin.Instance.Configuration.RepositoryUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
|
||||
public static bool IsTrailerType(Video video)
|
||||
{
|
||||
return video.Site.Equals("youtube", StringComparison.OrdinalIgnoreCase)
|
||||
&& (!video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase)
|
||||
|| !video.Type.Equals("teaser", StringComparison.OrdinalIgnoreCase));
|
||||
&& (video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase)
|
||||
|| video.Type.Equals("teaser", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
@@ -40,6 +41,7 @@ namespace MediaBrowser.Providers.TV
|
||||
{
|
||||
await base.AfterMetadataRefresh(item, refreshOptions, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
RemoveObsoleteEpisodes(item);
|
||||
RemoveObsoleteSeasons(item);
|
||||
await FillInMissingSeasonsAsync(item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
@@ -121,6 +123,61 @@ namespace MediaBrowser.Providers.TV
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveObsoleteEpisodes(Series series)
|
||||
{
|
||||
var episodes = series.GetEpisodes(null, new DtoOptions()).OfType<Episode>().ToList();
|
||||
var numberOfEpisodes = episodes.Count;
|
||||
// TODO: O(n^2), but can it be done faster without overcomplicating it?
|
||||
for (var i = 0; i < numberOfEpisodes; i++)
|
||||
{
|
||||
var currentEpisode = episodes[i];
|
||||
// The outer loop only examines virtual episodes
|
||||
if (!currentEpisode.IsVirtualItem)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Virtual episodes without an episode number are practically orphaned and should be deleted
|
||||
if (!currentEpisode.IndexNumber.HasValue)
|
||||
{
|
||||
DeleteEpisode(currentEpisode);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var j = i + 1; j < numberOfEpisodes; j++)
|
||||
{
|
||||
var comparisonEpisode = episodes[j];
|
||||
// The inner loop is only for "physical" episodes
|
||||
if (comparisonEpisode.IsVirtualItem
|
||||
|| currentEpisode.ParentIndexNumber != comparisonEpisode.ParentIndexNumber
|
||||
|| !comparisonEpisode.ContainsEpisodeNumber(currentEpisode.IndexNumber.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DeleteEpisode(currentEpisode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteEpisode(Episode episode)
|
||||
{
|
||||
Logger.LogInformation(
|
||||
"Removing virtual episode S{SeasonNumber}E{EpisodeNumber} in series {SeriesName}",
|
||||
episode.ParentIndexNumber,
|
||||
episode.IndexNumber,
|
||||
episode.SeriesName);
|
||||
|
||||
LibraryManager.DeleteItem(
|
||||
episode,
|
||||
new DeleteOptions
|
||||
{
|
||||
DeleteFileLocation = true
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates seasons for all episodes that aren't in a season folder.
|
||||
/// If no season number can be determined, a dummy season will be created.
|
||||
|
||||
Reference in New Issue
Block a user