mirror of https://github.com/jellyfin/jellyfin.git
Merge remote-tracking branch 'upstream/master' into api-stream-return
This commit is contained in:
commit
612e135c8c
|
@ -126,7 +126,6 @@ namespace Emby.Dlna
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
builder.AppendLine("No matching device profile found. The default will need to be used.");
|
builder.AppendLine("No matching device profile found. The default will need to be used.");
|
||||||
builder.AppendFormat(CultureInfo.InvariantCulture, "DeviceDescription:{0}", profile.DeviceDescription ?? string.Empty).AppendLine();
|
|
||||||
builder.AppendFormat(CultureInfo.InvariantCulture, "FriendlyName:{0}", profile.FriendlyName ?? string.Empty).AppendLine();
|
builder.AppendFormat(CultureInfo.InvariantCulture, "FriendlyName:{0}", profile.FriendlyName ?? string.Empty).AppendLine();
|
||||||
builder.AppendFormat(CultureInfo.InvariantCulture, "Manufacturer:{0}", profile.Manufacturer ?? string.Empty).AppendLine();
|
builder.AppendFormat(CultureInfo.InvariantCulture, "Manufacturer:{0}", profile.Manufacturer ?? string.Empty).AppendLine();
|
||||||
builder.AppendFormat(CultureInfo.InvariantCulture, "ManufacturerUrl:{0}", profile.ManufacturerUrl ?? string.Empty).AppendLine();
|
builder.AppendFormat(CultureInfo.InvariantCulture, "ManufacturerUrl:{0}", profile.ManufacturerUrl ?? string.Empty).AppendLine();
|
||||||
|
@ -141,17 +140,9 @@ namespace Emby.Dlna
|
||||||
|
|
||||||
private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
|
private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(profileInfo.DeviceDescription))
|
|
||||||
{
|
|
||||||
if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
|
if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
|
||||||
{
|
{
|
||||||
if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
|
if (deviceInfo.FriendlyName == null || !IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -159,7 +150,7 @@ namespace Emby.Dlna
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
|
if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
|
||||||
{
|
{
|
||||||
if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
|
if (deviceInfo.Manufacturer == null || !IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +158,7 @@ namespace Emby.Dlna
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
|
if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
|
||||||
{
|
{
|
||||||
if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
|
if (deviceInfo.ManufacturerUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -175,7 +166,7 @@ namespace Emby.Dlna
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
|
if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
|
||||||
{
|
{
|
||||||
if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
|
if (deviceInfo.ModelDescription == null || !IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -183,7 +174,7 @@ namespace Emby.Dlna
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profileInfo.ModelName))
|
if (!string.IsNullOrEmpty(profileInfo.ModelName))
|
||||||
{
|
{
|
||||||
if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
|
if (deviceInfo.ModelName == null || !IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -191,7 +182,7 @@ namespace Emby.Dlna
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
|
if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
|
||||||
{
|
{
|
||||||
if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
|
if (deviceInfo.ModelNumber == null || !IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -199,7 +190,7 @@ namespace Emby.Dlna
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
|
if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
|
||||||
{
|
{
|
||||||
if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
|
if (deviceInfo.ModelUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -207,7 +198,7 @@ namespace Emby.Dlna
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
|
if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
|
||||||
{
|
{
|
||||||
if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
|
if (deviceInfo.SerialNumber == null || !IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -216,11 +207,11 @@ namespace Emby.Dlna
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsRegexMatch(string input, string pattern)
|
private bool IsRegexOrSubstringMatch(string input, string pattern)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Regex.IsMatch(input, pattern);
|
return input.Contains(pattern, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||||
}
|
}
|
||||||
catch (ArgumentException ex)
|
catch (ArgumentException ex)
|
||||||
{
|
{
|
||||||
|
@ -511,8 +502,7 @@ namespace Emby.Dlna
|
||||||
|
|
||||||
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
|
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
|
||||||
{
|
{
|
||||||
var profile = GetProfile(headers) ??
|
var profile = GetDefaultProfile();
|
||||||
GetDefaultProfile();
|
|
||||||
|
|
||||||
var serverId = _appHost.SystemId;
|
var serverId = _appHost.SystemId;
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
LoadOptions.PreserveWhitespace);
|
LoadOptions.PreserveWhitespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<HttpResponseMessage> PostSoapDataAsync(
|
private async Task<HttpResponseMessage> PostSoapDataAsync(
|
||||||
string url,
|
string url,
|
||||||
string soapAction,
|
string soapAction,
|
||||||
string postData,
|
string postData,
|
||||||
|
@ -126,7 +126,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
|
|
||||||
options.Content = new StringContent(postData, Encoding.UTF8, MediaTypeNames.Text.Xml);
|
options.Content = new StringContent(postData, Encoding.UTF8, MediaTypeNames.Text.Xml);
|
||||||
|
|
||||||
return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
return await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,8 +238,14 @@ namespace Emby.Server.Implementations
|
||||||
public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager;
|
public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ApplicationHost" /> class.
|
/// Initializes a new instance of the <see cref="ApplicationHost"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
|
||||||
|
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
|
||||||
|
/// <param name="options">Instance of the <see cref="IStartupOptions"/> interface.</param>
|
||||||
|
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
||||||
|
/// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
|
||||||
|
/// <param name="serviceCollection">Instance of the <see cref="IServiceCollection"/> interface.</param>
|
||||||
public ApplicationHost(
|
public ApplicationHost(
|
||||||
IServerApplicationPaths applicationPaths,
|
IServerApplicationPaths applicationPaths,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
|
@ -1137,7 +1143,8 @@ namespace Emby.Server.Implementations
|
||||||
Id = SystemId,
|
Id = SystemId,
|
||||||
OperatingSystem = OperatingSystem.Id.ToString(),
|
OperatingSystem = OperatingSystem.Id.ToString(),
|
||||||
ServerName = FriendlyName,
|
ServerName = FriendlyName,
|
||||||
LocalAddress = localAddress
|
LocalAddress = localAddress,
|
||||||
|
StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1431,10 +1438,6 @@ namespace Emby.Server.Implementations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void EnableLoopback(string appName)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _disposed = false;
|
private bool _disposed = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -468,13 +468,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
|
|
||||||
imageIdString = imageIdString.TrimEnd(',') + "]";
|
imageIdString = imageIdString.TrimEnd(',') + "]";
|
||||||
|
|
||||||
using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs");
|
using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs")
|
||||||
message.Content = new StringContent(imageIdString, Encoding.UTF8, MediaTypeNames.Application.Json);
|
{
|
||||||
|
Content = new StringContent(imageIdString, Encoding.UTF8, MediaTypeNames.Application.Json)
|
||||||
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
|
using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
|
||||||
await using var response = await innerResponse2.Content.ReadAsStreamAsync();
|
await using var response = await innerResponse2.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
return await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ShowImages>>(
|
return await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ShowImages>>(
|
||||||
response).ConfigureAwait(false);
|
response).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"Albums": "專輯",
|
"Albums": "專輯",
|
||||||
"AppDeviceValues": "軟體: {0}, 裝置: {1}",
|
"AppDeviceValues": "軟體:{0},裝置:{1}",
|
||||||
"Application": "應用程式",
|
"Application": "應用程式",
|
||||||
"Artists": "演出者",
|
"Artists": "演出者",
|
||||||
"AuthenticationSucceededWithUserName": "{0} 成功授權",
|
"AuthenticationSucceededWithUserName": "{0} 成功授權",
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
"Collections": "合輯",
|
"Collections": "合輯",
|
||||||
"DeviceOfflineWithName": "{0} 已經斷線",
|
"DeviceOfflineWithName": "{0} 已經斷線",
|
||||||
"DeviceOnlineWithName": "{0} 已經連線",
|
"DeviceOnlineWithName": "{0} 已經連線",
|
||||||
"FailedLoginAttemptWithUserName": "來自 {0} 的失敗登入嘗試",
|
"FailedLoginAttemptWithUserName": "來自使用者 {0} 的失敗登入",
|
||||||
"Favorites": "我的最愛",
|
"Favorites": "我的最愛",
|
||||||
"Folders": "資料夾",
|
"Folders": "資料夾",
|
||||||
"Genres": "風格",
|
"Genres": "風格",
|
||||||
|
@ -28,8 +28,8 @@
|
||||||
"HomeVideos": "自製影片",
|
"HomeVideos": "自製影片",
|
||||||
"ItemAddedWithName": "{0} 已新增至媒體庫",
|
"ItemAddedWithName": "{0} 已新增至媒體庫",
|
||||||
"ItemRemovedWithName": "{0} 已從媒體庫移除",
|
"ItemRemovedWithName": "{0} 已從媒體庫移除",
|
||||||
"LabelIpAddressValue": "IP 位置: {0}",
|
"LabelIpAddressValue": "IP 位址:{0}",
|
||||||
"LabelRunningTimeValue": "運行時間: {0}",
|
"LabelRunningTimeValue": "運行時間:{0}",
|
||||||
"Latest": "最新",
|
"Latest": "最新",
|
||||||
"MessageApplicationUpdated": "Jellyfin Server 已經更新",
|
"MessageApplicationUpdated": "Jellyfin Server 已經更新",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin Server 已經更新至 {0}",
|
"MessageApplicationUpdatedTo": "Jellyfin Server 已經更新至 {0}",
|
||||||
|
@ -42,18 +42,18 @@
|
||||||
"NameInstallFailed": "{0} 安裝失敗",
|
"NameInstallFailed": "{0} 安裝失敗",
|
||||||
"NameSeasonNumber": "第 {0} 季",
|
"NameSeasonNumber": "第 {0} 季",
|
||||||
"NameSeasonUnknown": "未知季數",
|
"NameSeasonUnknown": "未知季數",
|
||||||
"NewVersionIsAvailable": "新版本的Jellyfin Server 軟體已經推出可供下載。",
|
"NewVersionIsAvailable": "新版本的 Jellyfin Server 軟體已經可供下載。",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "有可用的應用程式更新",
|
"NotificationOptionApplicationUpdateAvailable": "有可用的應用程式更新",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "應用程式已更新",
|
"NotificationOptionApplicationUpdateInstalled": "軟體更新已安裝",
|
||||||
"NotificationOptionAudioPlayback": "音樂開始播放",
|
"NotificationOptionAudioPlayback": "音樂開始播放",
|
||||||
"NotificationOptionAudioPlaybackStopped": "音樂停止播放",
|
"NotificationOptionAudioPlaybackStopped": "音樂停止播放",
|
||||||
"NotificationOptionCameraImageUploaded": "相機相片已上傳",
|
"NotificationOptionCameraImageUploaded": "相機相片已上傳",
|
||||||
"NotificationOptionInstallationFailed": "安裝失敗",
|
"NotificationOptionInstallationFailed": "安裝失敗",
|
||||||
"NotificationOptionNewLibraryContent": "已新增新內容",
|
"NotificationOptionNewLibraryContent": "已新增新內容",
|
||||||
"NotificationOptionPluginError": "插件安裝錯誤",
|
"NotificationOptionPluginError": "外掛安裝失敗",
|
||||||
"NotificationOptionPluginInstalled": "插件已安裝",
|
"NotificationOptionPluginInstalled": "外掛已安裝",
|
||||||
"NotificationOptionPluginUninstalled": "插件已移除",
|
"NotificationOptionPluginUninstalled": "外掛已移除",
|
||||||
"NotificationOptionPluginUpdateInstalled": "插件已更新",
|
"NotificationOptionPluginUpdateInstalled": "外掛已更新",
|
||||||
"NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
|
"NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
|
||||||
"NotificationOptionTaskFailed": "排程任務失敗",
|
"NotificationOptionTaskFailed": "排程任務失敗",
|
||||||
"NotificationOptionUserLockedOut": "使用者已鎖定",
|
"NotificationOptionUserLockedOut": "使用者已鎖定",
|
||||||
|
@ -61,14 +61,14 @@
|
||||||
"NotificationOptionVideoPlaybackStopped": "影片停止播放",
|
"NotificationOptionVideoPlaybackStopped": "影片停止播放",
|
||||||
"Photos": "相片",
|
"Photos": "相片",
|
||||||
"Playlists": "播放清單",
|
"Playlists": "播放清單",
|
||||||
"Plugin": "插件",
|
"Plugin": "外掛",
|
||||||
"PluginInstalledWithName": "{0} 已安裝",
|
"PluginInstalledWithName": "{0} 已安裝",
|
||||||
"PluginUninstalledWithName": "{0} 已移除",
|
"PluginUninstalledWithName": "{0} 已移除",
|
||||||
"PluginUpdatedWithName": "{0} 已更新",
|
"PluginUpdatedWithName": "{0} 已更新",
|
||||||
"ProviderValue": "提供商: {0}",
|
"ProviderValue": "提供商: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} 已失敗",
|
"ScheduledTaskFailedWithName": "排程任務 {0} 已失敗",
|
||||||
"ScheduledTaskStartedWithName": "{0} 已開始",
|
"ScheduledTaskStartedWithName": "排程任務 {0} 已開始",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} 需要重新啟動",
|
"ServerNameNeedsToBeRestarted": "伺服器 {0} 需要重新啟動",
|
||||||
"Shows": "節目",
|
"Shows": "節目",
|
||||||
"Songs": "歌曲",
|
"Songs": "歌曲",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin Server正在啟動,請稍後再試一次。",
|
"StartupEmbyServerIsLoading": "Jellyfin Server正在啟動,請稍後再試一次。",
|
||||||
|
@ -78,10 +78,10 @@
|
||||||
"User": "使用者",
|
"User": "使用者",
|
||||||
"UserCreatedWithName": "使用者 {0} 已建立",
|
"UserCreatedWithName": "使用者 {0} 已建立",
|
||||||
"UserDeletedWithName": "使用者 {0} 已移除",
|
"UserDeletedWithName": "使用者 {0} 已移除",
|
||||||
"UserDownloadingItemWithValues": "{0} 正在下載 {1}",
|
"UserDownloadingItemWithValues": "使用者 {0} 正在下載 {1}",
|
||||||
"UserLockedOutWithName": "使用者 {0} 已鎖定",
|
"UserLockedOutWithName": "使用者 {0} 已鎖定",
|
||||||
"UserOfflineFromDevice": "{0} 已從 {1} 斷線",
|
"UserOfflineFromDevice": "使用者 {0} 已從 {1} 斷線",
|
||||||
"UserOnlineFromDevice": "{0} 已連線,來自 {1}",
|
"UserOnlineFromDevice": "使用者 {0} 已從 {1} 連線",
|
||||||
"UserPasswordChangedWithName": "使用者 {0} 的密碼已變更",
|
"UserPasswordChangedWithName": "使用者 {0} 的密碼已變更",
|
||||||
"UserPolicyUpdatedWithName": "使用者條約已更新為 {0}",
|
"UserPolicyUpdatedWithName": "使用者條約已更新為 {0}",
|
||||||
"UserStartedPlayingItemWithValues": "{0}正在使用 {2} 播放 {1}",
|
"UserStartedPlayingItemWithValues": "{0}正在使用 {2} 播放 {1}",
|
||||||
|
@ -95,23 +95,23 @@
|
||||||
"TaskDownloadMissingSubtitlesDescription": "在網路上透過中繼資料搜尋遺失的字幕。",
|
"TaskDownloadMissingSubtitlesDescription": "在網路上透過中繼資料搜尋遺失的字幕。",
|
||||||
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
|
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
|
||||||
"TaskRefreshChannels": "重新整理頻道",
|
"TaskRefreshChannels": "重新整理頻道",
|
||||||
"TaskUpdatePlugins": "更新插件",
|
"TaskUpdatePlugins": "更新外掛",
|
||||||
"TaskRefreshPeople": "重新整理人員",
|
"TaskRefreshPeople": "重新整理人員",
|
||||||
"TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔案。",
|
"TaskCleanLogsDescription": "刪除超過 {0} 天的舊紀錄檔。",
|
||||||
"TaskCleanLogs": "清空紀錄資料夾",
|
"TaskCleanLogs": "清空紀錄資料夾",
|
||||||
"TaskRefreshLibraryDescription": "掃描媒體庫內新的檔案並重新整理描述資料。",
|
"TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新描述資料。",
|
||||||
"TaskRefreshLibrary": "掃描媒體庫",
|
"TaskRefreshLibrary": "重新掃描媒體庫",
|
||||||
"TaskRefreshChapterImages": "擷取章節圖片",
|
"TaskRefreshChapterImages": "擷取章節圖片",
|
||||||
"TaskCleanCacheDescription": "刪除系統長時間不需要的快取。",
|
"TaskCleanCacheDescription": "刪除系統已不需要的快取。",
|
||||||
"TaskCleanCache": "清除快取資料夾",
|
"TaskCleanCache": "清除快取資料夾",
|
||||||
"TasksLibraryCategory": "媒體庫",
|
"TasksLibraryCategory": "媒體庫",
|
||||||
"TaskRefreshChannelsDescription": "重新整理網絡頻道資料。",
|
"TaskRefreshChannelsDescription": "重新整理網路頻道資料。",
|
||||||
"TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。",
|
"TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。",
|
||||||
"TaskCleanTranscode": "清除轉碼資料夾",
|
"TaskCleanTranscode": "清除轉碼資料夾",
|
||||||
"TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。",
|
"TaskUpdatePluginsDescription": "為設置自動更新的外掛下載並安裝更新。",
|
||||||
"TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。",
|
"TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。",
|
||||||
"TaskRefreshChapterImagesDescription": "為有章節的視頻創建縮圖。",
|
"TaskRefreshChapterImagesDescription": "為有章節的影片建立縮圖。",
|
||||||
"TasksChannelsCategory": "網絡頻道",
|
"TasksChannelsCategory": "網路頻道",
|
||||||
"TasksApplicationCategory": "應用程式",
|
"TasksApplicationCategory": "應用程式",
|
||||||
"TasksMaintenanceCategory": "維修"
|
"TasksMaintenanceCategory": "維修"
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,12 +68,6 @@ namespace Emby.Server.Implementations.Udp
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error sending response message");
|
_logger.LogError(ex, "Error sending response message");
|
||||||
}
|
}
|
||||||
|
|
||||||
var parts = messageText.Split('|');
|
|
||||||
if (parts.Length > 1)
|
|
||||||
{
|
|
||||||
_appHost.EnableLoopback(parts[1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -183,7 +183,8 @@ namespace Emby.Server.Implementations.Updates
|
||||||
IEnumerable<PackageInfo> availablePackages,
|
IEnumerable<PackageInfo> availablePackages,
|
||||||
string name = null,
|
string name = null,
|
||||||
Guid guid = default,
|
Guid guid = default,
|
||||||
Version minVersion = null)
|
Version minVersion = null,
|
||||||
|
Version specificVersion = null)
|
||||||
{
|
{
|
||||||
var package = FilterPackages(availablePackages, name, guid).FirstOrDefault();
|
var package = FilterPackages(availablePackages, name, guid).FirstOrDefault();
|
||||||
|
|
||||||
|
@ -197,7 +198,11 @@ namespace Emby.Server.Implementations.Updates
|
||||||
var availableVersions = package.versions
|
var availableVersions = package.versions
|
||||||
.Where(x => Version.Parse(x.targetAbi) <= appVer);
|
.Where(x => Version.Parse(x.targetAbi) <= appVer);
|
||||||
|
|
||||||
if (minVersion != null)
|
if (specificVersion != null)
|
||||||
|
{
|
||||||
|
availableVersions = availableVersions.Where(x => new Version(x.version) == specificVersion);
|
||||||
|
}
|
||||||
|
else if (minVersion != null)
|
||||||
{
|
{
|
||||||
availableVersions = availableVersions.Where(x => new Version(x.version) >= minVersion);
|
availableVersions = availableVersions.Where(x => new Version(x.version) >= minVersion);
|
||||||
}
|
}
|
||||||
|
@ -227,8 +232,8 @@ namespace Emby.Server.Implementations.Updates
|
||||||
{
|
{
|
||||||
foreach (var plugin in _applicationHost.Plugins)
|
foreach (var plugin in _applicationHost.Plugins)
|
||||||
{
|
{
|
||||||
var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version);
|
var compatibleVersions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, minVersion: plugin.Version);
|
||||||
var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version);
|
var version = compatibleVersions.FirstOrDefault(y => y.Version > plugin.Version);
|
||||||
if (version != null && CompletedInstallations.All(x => x.Guid != version.Guid))
|
if (version != null && CompletedInstallations.All(x => x.Guid != version.Guid))
|
||||||
{
|
{
|
||||||
yield return version;
|
yield return version;
|
||||||
|
|
|
@ -448,7 +448,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
[HttpGet("Timers/{timerId}")]
|
[HttpGet("Timers/{timerId}")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||||
public async Task<ActionResult<TimerInfoDto>> GetTimer(string timerId)
|
public async Task<ActionResult<TimerInfoDto>> GetTimer([FromRoute, Required] string timerId)
|
||||||
{
|
{
|
||||||
return await _liveTvManager.GetTimer(timerId, CancellationToken.None).ConfigureAwait(false);
|
return await _liveTvManager.GetTimer(timerId, CancellationToken.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,10 @@ namespace Jellyfin.Api.Controllers
|
||||||
{
|
{
|
||||||
var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
|
var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
|
||||||
var result = _installationManager.FilterPackages(
|
var result = _installationManager.FilterPackages(
|
||||||
packages,
|
packages,
|
||||||
name,
|
name,
|
||||||
string.IsNullOrEmpty(assemblyGuid) ? default : Guid.Parse(assemblyGuid)).FirstOrDefault();
|
string.IsNullOrEmpty(assemblyGuid) ? default : Guid.Parse(assemblyGuid))
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -93,7 +94,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
packages,
|
packages,
|
||||||
name,
|
name,
|
||||||
string.IsNullOrEmpty(assemblyGuid) ? Guid.Empty : Guid.Parse(assemblyGuid),
|
string.IsNullOrEmpty(assemblyGuid) ? Guid.Empty : Guid.Parse(assemblyGuid),
|
||||||
string.IsNullOrEmpty(version) ? null : Version.Parse(version)).FirstOrDefault();
|
specificVersion: string.IsNullOrEmpty(version) ? null : Version.Parse(version))
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (package == null)
|
if (package == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -123,10 +123,9 @@ namespace Jellyfin.Api.Helpers
|
||||||
state.Dispose();
|
state.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
var memoryStream = new MemoryStream();
|
await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None)
|
||||||
await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None).WriteToAsync(memoryStream, CancellationToken.None).ConfigureAwait(false);
|
.WriteToAsync(httpContext.Response.Body, CancellationToken.None).ConfigureAwait(false);
|
||||||
memoryStream.Position = 0;
|
return new FileStreamResult(httpContext.Response.Body, contentType);
|
||||||
return new FileStreamResult(memoryStream, contentType);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -92,7 +92,6 @@ namespace Jellyfin.Api.Helpers
|
||||||
}
|
}
|
||||||
|
|
||||||
var enableDlnaHeaders = !string.IsNullOrWhiteSpace(streamingRequest.Params) ||
|
var enableDlnaHeaders = !string.IsNullOrWhiteSpace(streamingRequest.Params) ||
|
||||||
streamingRequest.StreamOptions.ContainsKey("dlnaheaders") ||
|
|
||||||
string.Equals(httpRequest.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase);
|
string.Equals(httpRequest.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper)
|
var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Data.Interfaces;
|
using Jellyfin.Data.Interfaces;
|
||||||
|
@ -9,7 +8,7 @@ using Microsoft.EntityFrameworkCore;
|
||||||
namespace Jellyfin.Server.Implementations
|
namespace Jellyfin.Server.Implementations
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public partial class JellyfinDb : DbContext
|
public class JellyfinDb : DbContext
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="JellyfinDb"/> class.
|
/// Initializes a new instance of the <see cref="JellyfinDb"/> class.
|
||||||
|
@ -138,47 +137,20 @@ namespace Jellyfin.Server.Implementations
|
||||||
return base.SaveChanges();
|
return base.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override void Dispose()
|
|
||||||
{
|
|
||||||
foreach (var entry in ChangeTracker.Entries())
|
|
||||||
{
|
|
||||||
entry.State = EntityState.Detached;
|
|
||||||
}
|
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
base.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
|
||||||
{
|
|
||||||
CustomInit(optionsBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
OnModelCreatingImpl(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.HasDefaultSchema("jellyfin");
|
modelBuilder.HasDefaultSchema("jellyfin");
|
||||||
|
|
||||||
/*modelBuilder.Entity<Artwork>().HasIndex(t => t.Kind);
|
modelBuilder.Entity<DisplayPreferences>()
|
||||||
|
.HasIndex(entity => entity.UserId)
|
||||||
|
.IsUnique(false);
|
||||||
|
|
||||||
modelBuilder.Entity<Genre>().HasIndex(t => t.Name)
|
modelBuilder.Entity<DisplayPreferences>()
|
||||||
.IsUnique();
|
.HasIndex(entity => new { entity.UserId, entity.Client })
|
||||||
|
.IsUnique();
|
||||||
modelBuilder.Entity<LibraryItem>().HasIndex(t => t.UrlId)
|
|
||||||
.IsUnique();*/
|
|
||||||
|
|
||||||
OnModelCreatedImpl(modelBuilder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void CustomInit(DbContextOptionsBuilder optionsBuilder);
|
|
||||||
|
|
||||||
partial void OnModelCreatingImpl(ModelBuilder modelBuilder);
|
|
||||||
|
|
||||||
partial void OnModelCreatedImpl(ModelBuilder modelBuilder);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
461
Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs
generated
Normal file
461
Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs
generated
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Jellyfin.Server.Implementations;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Implementations.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(JellyfinDb))]
|
||||||
|
[Migration("20200905220533_FixDisplayPreferencesIndex")]
|
||||||
|
partial class FixDisplayPreferencesIndex
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasDefaultSchema("jellyfin")
|
||||||
|
.HasAnnotation("ProductVersion", "3.1.7");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("DayOfWeek")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<double>("EndHour")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<double>("StartHour")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AccessSchedules");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateCreated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ItemId")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<int>("LogSeverity")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(512);
|
||||||
|
|
||||||
|
b.Property<string>("Overview")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(512);
|
||||||
|
|
||||||
|
b.Property<uint>("RowVersion")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ShortOverview")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(512);
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ActivityLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ChromecastVersion")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Client")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(32);
|
||||||
|
|
||||||
|
b.Property<string>("DashboardTheme")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(32);
|
||||||
|
|
||||||
|
b.Property<bool>("EnableNextVideoInfoOverlay")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("IndexBy")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ScrollDirection")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("ShowBackdrop")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("ShowSidebar")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("SkipBackwardLength")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("SkipForwardLength")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("TvHome")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(32);
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId", "Client")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("DisplayPreferences");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("DisplayPreferencesId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Order")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("DisplayPreferencesId");
|
||||||
|
|
||||||
|
b.ToTable("HomeSection");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastModified")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(512);
|
||||||
|
|
||||||
|
b.Property<Guid?>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("ImageInfos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Client")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(32);
|
||||||
|
|
||||||
|
b.Property<int?>("IndexBy")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("ItemId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("RememberIndexing")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("RememberSorting")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SortBy")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(64);
|
||||||
|
|
||||||
|
b.Property<int>("SortOrder")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ViewType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("ItemDisplayPreferences");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Kind")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid?>("Permission_Permissions_Guid")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<uint>("RowVersion")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("Value")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Permission_Permissions_Guid");
|
||||||
|
|
||||||
|
b.ToTable("Permissions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Kind")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid?>("Preference_Preferences_Guid")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<uint>("RowVersion")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(65535);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Preference_Preferences_Guid");
|
||||||
|
|
||||||
|
b.ToTable("Preferences");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("AudioLanguagePreference")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(255);
|
||||||
|
|
||||||
|
b.Property<string>("AuthenticationProviderId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(255);
|
||||||
|
|
||||||
|
b.Property<bool>("DisplayCollectionsView")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("DisplayMissingEpisodes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("EasyPassword")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(65535);
|
||||||
|
|
||||||
|
b.Property<bool>("EnableAutoLogin")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableLocalPassword")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableNextEpisodeAutoPlay")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableUserPreferenceAccess")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("HidePlayedInLatest")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("InternalId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("InvalidLoginAttemptCount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastActivityDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastLoginDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("LoginAttemptsBeforeLockout")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("MaxParentalAgeRating")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("MustUpdatePassword")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Password")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(65535);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordResetProviderId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(255);
|
||||||
|
|
||||||
|
b.Property<bool>("PlayDefaultAudioTrack")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("RememberAudioSelections")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("RememberSubtitleSelections")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("RemoteClientBitrateLimit")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<uint>("RowVersion")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SubtitleLanguagePreference")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(255);
|
||||||
|
|
||||||
|
b.Property<int>("SubtitleMode")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("SyncPlayAccess")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(255);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
|
.WithMany("AccessSchedules")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
|
.WithOne("DisplayPreferences")
|
||||||
|
.HasForeignKey("Jellyfin.Data.Entities.DisplayPreferences", "UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.DisplayPreferences", null)
|
||||||
|
.WithMany("HomeSections")
|
||||||
|
.HasForeignKey("DisplayPreferencesId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
|
.WithOne("ProfileImage")
|
||||||
|
.HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
|
.WithMany("ItemDisplayPreferences")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
|
.WithMany("Permissions")
|
||||||
|
.HasForeignKey("Permission_Permissions_Guid");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
|
.WithMany("Preferences")
|
||||||
|
.HasForeignKey("Preference_Preferences_Guid");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1601
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Implementations.Migrations
|
||||||
|
{
|
||||||
|
public partial class FixDisplayPreferencesIndex : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_DisplayPreferences_UserId",
|
||||||
|
schema: "jellyfin",
|
||||||
|
table: "DisplayPreferences");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_DisplayPreferences_UserId",
|
||||||
|
schema: "jellyfin",
|
||||||
|
table: "DisplayPreferences",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_DisplayPreferences_UserId_Client",
|
||||||
|
schema: "jellyfin",
|
||||||
|
table: "DisplayPreferences",
|
||||||
|
columns: new[] { "UserId", "Client" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_DisplayPreferences_UserId",
|
||||||
|
schema: "jellyfin",
|
||||||
|
table: "DisplayPreferences");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_DisplayPreferences_UserId_Client",
|
||||||
|
schema: "jellyfin",
|
||||||
|
table: "DisplayPreferences");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_DisplayPreferences_UserId",
|
||||||
|
schema: "jellyfin",
|
||||||
|
table: "DisplayPreferences",
|
||||||
|
column: "UserId",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ namespace Jellyfin.Server.Implementations.Migrations
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasDefaultSchema("jellyfin")
|
.HasDefaultSchema("jellyfin")
|
||||||
.HasAnnotation("ProductVersion", "3.1.6");
|
.HasAnnotation("ProductVersion", "3.1.7");
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
|
||||||
{
|
{
|
||||||
|
@ -136,7 +136,9 @@ namespace Jellyfin.Server.Implementations.Migrations
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("UserId")
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId", "Client")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("DisplayPreferences");
|
b.ToTable("DisplayPreferences");
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Configuration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cors policy provider.
|
||||||
|
/// </summary>
|
||||||
|
public class CorsPolicyProvider : ICorsPolicyProvider
|
||||||
|
{
|
||||||
|
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="CorsPolicyProvider"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||||
|
public CorsPolicyProvider(IServerConfigurationManager serverConfigurationManager)
|
||||||
|
{
|
||||||
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<CorsPolicy> GetPolicyAsync(HttpContext context, string policyName)
|
||||||
|
{
|
||||||
|
var corsHosts = _serverConfigurationManager.Configuration.CorsHosts;
|
||||||
|
var builder = new CorsPolicyBuilder()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader();
|
||||||
|
|
||||||
|
// No hosts configured or only default configured.
|
||||||
|
if (corsHosts.Length == 0
|
||||||
|
|| (corsHosts.Length == 1
|
||||||
|
&& string.Equals(corsHosts[0], CorsConstants.AnyOrigin, StringComparison.Ordinal)))
|
||||||
|
{
|
||||||
|
builder.AllowAnyOrigin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.WithOrigins(corsHosts)
|
||||||
|
.AllowCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(builder.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,14 +15,15 @@ using Jellyfin.Api.Auth.LocalAccessPolicy;
|
||||||
using Jellyfin.Api.Auth.RequiresElevationPolicy;
|
using Jellyfin.Api.Auth.RequiresElevationPolicy;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Controllers;
|
using Jellyfin.Api.Controllers;
|
||||||
|
using Jellyfin.Server.Configuration;
|
||||||
using Jellyfin.Server.Filters;
|
using Jellyfin.Server.Filters;
|
||||||
using Jellyfin.Server.Formatters;
|
using Jellyfin.Server.Formatters;
|
||||||
using Jellyfin.Server.Models;
|
|
||||||
using MediaBrowser.Common.Json;
|
using MediaBrowser.Common.Json;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
@ -139,10 +140,8 @@ namespace Jellyfin.Server.Extensions
|
||||||
public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, IEnumerable<Assembly> pluginAssemblies)
|
public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, IEnumerable<Assembly> pluginAssemblies)
|
||||||
{
|
{
|
||||||
IMvcBuilder mvcBuilder = serviceCollection
|
IMvcBuilder mvcBuilder = serviceCollection
|
||||||
.AddCors(options =>
|
.AddCors()
|
||||||
{
|
.AddTransient<ICorsPolicyProvider, CorsPolicyProvider>()
|
||||||
options.AddPolicy(ServerCorsPolicy.DefaultPolicyName, ServerCorsPolicy.DefaultPolicy);
|
|
||||||
})
|
|
||||||
.Configure<ForwardedHeadersOptions>(options =>
|
.Configure<ForwardedHeadersOptions>(options =>
|
||||||
{
|
{
|
||||||
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Graylog" Version="2.1.3" />
|
<PackageReference Include="Serilog.Sinks.Graylog" Version="2.1.3" />
|
||||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.3" />
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.4" />
|
||||||
<PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.14" />
|
<PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.14" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,11 @@ namespace Jellyfin.Server.Middleware
|
||||||
var localPath = httpContext.Request.Path.ToString();
|
var localPath = httpContext.Request.Path.ToString();
|
||||||
var baseUrlPrefix = serverConfigurationManager.Configuration.BaseUrl;
|
var baseUrlPrefix = serverConfigurationManager.Configuration.BaseUrl;
|
||||||
|
|
||||||
if (!localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(localPath, baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(localPath, baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.IsNullOrEmpty(localPath)
|
||||||
|
|| !localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// Always redirect back to the default path if the base prefix is invalid or missing
|
// Always redirect back to the default path if the base prefix is invalid or missing
|
||||||
_logger.LogDebug("Normalizing an URL at {LocalPath}", localPath);
|
_logger.LogDebug("Normalizing an URL at {LocalPath}", localPath);
|
||||||
|
|
|
@ -125,6 +125,7 @@ namespace Jellyfin.Server.Middleware
|
||||||
switch (ex)
|
switch (ex)
|
||||||
{
|
{
|
||||||
case ArgumentException _: return StatusCodes.Status400BadRequest;
|
case ArgumentException _: return StatusCodes.Status400BadRequest;
|
||||||
|
case AuthenticationException _:
|
||||||
case SecurityException _: return StatusCodes.Status401Unauthorized;
|
case SecurityException _: return StatusCodes.Status401Unauthorized;
|
||||||
case DirectoryNotFoundException _:
|
case DirectoryNotFoundException _:
|
||||||
case FileNotFoundException _:
|
case FileNotFoundException _:
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
|
||||||
|
|
||||||
namespace Jellyfin.Server.Models
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Server Cors Policy.
|
|
||||||
/// </summary>
|
|
||||||
public static class ServerCorsPolicy
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Default policy name.
|
|
||||||
/// </summary>
|
|
||||||
public const string DefaultPolicyName = "DefaultCorsPolicy";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default Policy. Allow Everything.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly CorsPolicy DefaultPolicy = new CorsPolicy
|
|
||||||
{
|
|
||||||
// Allow any origin
|
|
||||||
Origins = { "*" },
|
|
||||||
|
|
||||||
// Allow any method
|
|
||||||
Methods = { "*" },
|
|
||||||
|
|
||||||
// Allow any header
|
|
||||||
Headers = { "*" }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@ using Jellyfin.Api.TypeConverters;
|
||||||
using Jellyfin.Server.Extensions;
|
using Jellyfin.Server.Extensions;
|
||||||
using Jellyfin.Server.Implementations;
|
using Jellyfin.Server.Implementations;
|
||||||
using Jellyfin.Server.Middleware;
|
using Jellyfin.Server.Middleware;
|
||||||
using Jellyfin.Server.Models;
|
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
@ -94,11 +93,7 @@ namespace Jellyfin.Server
|
||||||
IWebHostEnvironment env,
|
IWebHostEnvironment env,
|
||||||
IConfiguration appConfig)
|
IConfiguration appConfig)
|
||||||
{
|
{
|
||||||
// Only add base url redirection if a base url is set.
|
app.UseBaseUrlRedirection();
|
||||||
if (!string.IsNullOrEmpty(_serverConfigurationManager.Configuration.BaseUrl))
|
|
||||||
{
|
|
||||||
app.UseBaseUrlRedirection();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap rest of configuration so everything only listens on BaseUrl.
|
// Wrap rest of configuration so everything only listens on BaseUrl.
|
||||||
app.Map(_serverConfigurationManager.Configuration.BaseUrl, mainApp =>
|
app.Map(_serverConfigurationManager.Configuration.BaseUrl, mainApp =>
|
||||||
|
@ -116,7 +111,7 @@ namespace Jellyfin.Server
|
||||||
|
|
||||||
mainApp.UseResponseCompression();
|
mainApp.UseResponseCompression();
|
||||||
|
|
||||||
mainApp.UseCors(ServerCorsPolicy.DefaultPolicyName);
|
mainApp.UseCors();
|
||||||
|
|
||||||
if (_serverConfigurationManager.Configuration.RequireHttps
|
if (_serverConfigurationManager.Configuration.RequireHttps
|
||||||
&& _serverApplicationHost.ListenWithHttps)
|
&& _serverApplicationHost.ListenWithHttps)
|
||||||
|
|
|
@ -73,12 +73,14 @@ namespace MediaBrowser.Common.Updates
|
||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <param name="guid">The guid of the plugin.</param>
|
/// <param name="guid">The guid of the plugin.</param>
|
||||||
/// <param name="minVersion">The minimum required version of the plugin.</param>
|
/// <param name="minVersion">The minimum required version of the plugin.</param>
|
||||||
|
/// <param name="specificVersion">The specific version of the plugin to install.</param>
|
||||||
/// <returns>All compatible versions ordered from newest to oldest.</returns>
|
/// <returns>All compatible versions ordered from newest to oldest.</returns>
|
||||||
IEnumerable<InstallationInfo> GetCompatibleVersions(
|
IEnumerable<InstallationInfo> GetCompatibleVersions(
|
||||||
IEnumerable<PackageInfo> availablePackages,
|
IEnumerable<PackageInfo> availablePackages,
|
||||||
string name = null,
|
string name = null,
|
||||||
Guid guid = default,
|
Guid guid = default,
|
||||||
Version minVersion = null);
|
Version minVersion = null,
|
||||||
|
Version specificVersion = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the available plugin updates.
|
/// Returns the available plugin updates.
|
||||||
|
|
|
@ -114,8 +114,6 @@ namespace MediaBrowser.Controller
|
||||||
/// <exception cref="NotSupportedException"><see cref="CanLaunchWebBrowser"/> is false.</exception>
|
/// <exception cref="NotSupportedException"><see cref="CanLaunchWebBrowser"/> is false.</exception>
|
||||||
void LaunchUrl(string url);
|
void LaunchUrl(string url);
|
||||||
|
|
||||||
void EnableLoopback(string appName);
|
|
||||||
|
|
||||||
IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo();
|
IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo();
|
||||||
|
|
||||||
string ExpandVirtualPath(string path);
|
string ExpandVirtualPath(string path);
|
||||||
|
|
|
@ -263,6 +263,11 @@ namespace MediaBrowser.Model.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long SlowResponseThresholdMs { get; set; }
|
public long SlowResponseThresholdMs { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the cors hosts.
|
||||||
|
/// </summary>
|
||||||
|
public string[] CorsHosts { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -372,6 +377,7 @@ namespace MediaBrowser.Model.Configuration
|
||||||
|
|
||||||
EnableSlowResponseWarning = true;
|
EnableSlowResponseWarning = true;
|
||||||
SlowResponseThresholdMs = 500;
|
SlowResponseThresholdMs = 500;
|
||||||
|
CorsHosts = new[] { "*" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,12 +37,6 @@ namespace MediaBrowser.Model.Dlna
|
||||||
/// <value>The model description.</value>
|
/// <value>The model description.</value>
|
||||||
public string ModelDescription { get; set; }
|
public string ModelDescription { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the device description.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The device description.</value>
|
|
||||||
public string DeviceDescription { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the model URL.
|
/// Gets or sets the model URL.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace MediaBrowser.Model.System
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The product name. This is the AssemblyProduct name.
|
/// Gets or sets the product name. This is the AssemblyProduct name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ProductName { get; set; }
|
public string ProductName { get; set; }
|
||||||
|
|
||||||
|
@ -39,5 +39,11 @@ namespace MediaBrowser.Model.System
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The id.</value>
|
/// <value>The id.</value>
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the startup wizard is completed.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The startup completion status.</value>
|
||||||
|
public bool StartupWizardCompleted { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
|
|
@ -7,7 +7,6 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Data.Entities;
|
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
@ -59,6 +58,16 @@ namespace MediaBrowser.Providers.Manager
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool EnableExtraThumbsDuplication
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var config = _config.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
|
||||||
|
|
||||||
|
return config.EnableExtraThumbsDuplication;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the image.
|
/// Saves the image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -69,7 +78,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
/// <param name="imageIndex">Index of the image.</param>
|
/// <param name="imageIndex">Index of the image.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
/// <exception cref="ArgumentNullException">mimeType</exception>
|
/// <exception cref="ArgumentNullException">mimeType.</exception>
|
||||||
public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
|
public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken);
|
return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken);
|
||||||
|
@ -312,7 +321,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
/// <exception cref="ArgumentNullException">
|
/// <exception cref="ArgumentNullException">
|
||||||
/// imageIndex
|
/// imageIndex
|
||||||
/// or
|
/// or
|
||||||
/// imageIndex
|
/// imageIndex.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
private ItemImageInfo GetCurrentImage(BaseItem item, ImageType type, int imageIndex)
|
private ItemImageInfo GetCurrentImage(BaseItem item, ImageType type, int imageIndex)
|
||||||
{
|
{
|
||||||
|
@ -328,7 +337,8 @@ namespace MediaBrowser.Providers.Manager
|
||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <exception cref="ArgumentNullException">imageIndex
|
/// <exception cref="ArgumentNullException">imageIndex
|
||||||
/// or
|
/// or
|
||||||
/// imageIndex</exception>
|
/// imageIndex.
|
||||||
|
/// </exception>
|
||||||
private void SetImagePath(BaseItem item, ImageType type, int? imageIndex, string path)
|
private void SetImagePath(BaseItem item, ImageType type, int? imageIndex, string path)
|
||||||
{
|
{
|
||||||
item.SetImagePath(type, imageIndex ?? 0, _fileSystem.GetFileInfo(path));
|
item.SetImagePath(type, imageIndex ?? 0, _fileSystem.GetFileInfo(path));
|
||||||
|
@ -346,7 +356,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
/// <exception cref="ArgumentNullException">
|
/// <exception cref="ArgumentNullException">
|
||||||
/// imageIndex
|
/// imageIndex
|
||||||
/// or
|
/// or
|
||||||
/// imageIndex
|
/// imageIndex.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
private string GetStandardSavePath(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
|
private string GetStandardSavePath(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
|
||||||
{
|
{
|
||||||
|
@ -500,7 +510,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
/// <param name="imageIndex">Index of the image.</param>
|
/// <param name="imageIndex">Index of the image.</param>
|
||||||
/// <param name="mimeType">Type of the MIME.</param>
|
/// <param name="mimeType">Type of the MIME.</param>
|
||||||
/// <returns>IEnumerable{System.String}.</returns>
|
/// <returns>IEnumerable{System.String}.</returns>
|
||||||
/// <exception cref="ArgumentNullException">imageIndex</exception>
|
/// <exception cref="ArgumentNullException">imageIndex.</exception>
|
||||||
private string[] GetCompatibleSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType)
|
private string[] GetCompatibleSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType)
|
||||||
{
|
{
|
||||||
var season = item as Season;
|
var season = item as Season;
|
||||||
|
@ -604,16 +614,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, true) };
|
return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, true) };
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool EnableExtraThumbsDuplication
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var config = _config.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
|
|
||||||
|
|
||||||
return config.EnableExtraThumbsDuplication;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the save path for item in mixed folder.
|
/// Gets the save path for item in mixed folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -28,6 +28,22 @@ namespace MediaBrowser.Providers.Manager
|
||||||
private readonly IProviderManager _providerManager;
|
private readonly IProviderManager _providerManager;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Image types that are only one per item.
|
||||||
|
/// </summary>
|
||||||
|
private readonly ImageType[] _singularImages =
|
||||||
|
{
|
||||||
|
ImageType.Primary,
|
||||||
|
ImageType.Art,
|
||||||
|
ImageType.Banner,
|
||||||
|
ImageType.Box,
|
||||||
|
ImageType.BoxRear,
|
||||||
|
ImageType.Disc,
|
||||||
|
ImageType.Logo,
|
||||||
|
ImageType.Menu,
|
||||||
|
ImageType.Thumb
|
||||||
|
};
|
||||||
|
|
||||||
public ItemImageProvider(ILogger logger, IProviderManager providerManager, IFileSystem fileSystem)
|
public ItemImageProvider(ILogger logger, IProviderManager providerManager, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
@ -175,22 +191,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Image types that are only one per item.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ImageType[] _singularImages =
|
|
||||||
{
|
|
||||||
ImageType.Primary,
|
|
||||||
ImageType.Art,
|
|
||||||
ImageType.Banner,
|
|
||||||
ImageType.Box,
|
|
||||||
ImageType.BoxRear,
|
|
||||||
ImageType.Disc,
|
|
||||||
ImageType.Logo,
|
|
||||||
ImageType.Menu,
|
|
||||||
ImageType.Thumb
|
|
||||||
};
|
|
||||||
|
|
||||||
private bool HasImage(BaseItem item, ImageType type)
|
private bool HasImage(BaseItem item, ImageType type)
|
||||||
{
|
{
|
||||||
return item.HasImage(type);
|
return item.HasImage(type);
|
||||||
|
@ -378,7 +378,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
var newDateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo);
|
var newDateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo);
|
||||||
|
|
||||||
// If date changed then we need to reset saved image dimensions
|
// If date changed then we need to reset saved image dimensions
|
||||||
|
@ -441,7 +440,9 @@ namespace MediaBrowser.Providers.Manager
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> DownloadImage(BaseItem item, LibraryOptions libraryOptions,
|
private async Task<bool> DownloadImage(
|
||||||
|
BaseItem item,
|
||||||
|
LibraryOptions libraryOptions,
|
||||||
IRemoteImageProvider provider,
|
IRemoteImageProvider provider,
|
||||||
RefreshResult result,
|
RefreshResult result,
|
||||||
IEnumerable<RemoteImageInfo> images,
|
IEnumerable<RemoteImageInfo> images,
|
||||||
|
@ -522,11 +523,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!item.IsSaveLocalMetadataEnabled())
|
|
||||||
//{
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,13 +535,15 @@ namespace MediaBrowser.Providers.Manager
|
||||||
|
|
||||||
private void SaveImageStub(BaseItem item, ImageType imageType, IEnumerable<string> urls, int newIndex)
|
private void SaveImageStub(BaseItem item, ImageType imageType, IEnumerable<string> urls, int newIndex)
|
||||||
{
|
{
|
||||||
var path = string.Join("|", urls.Take(1).ToArray());
|
var path = string.Join('|', urls.Take(1));
|
||||||
|
|
||||||
item.SetImage(new ItemImageInfo
|
item.SetImage(
|
||||||
{
|
new ItemImageInfo
|
||||||
Path = path,
|
{
|
||||||
Type = imageType
|
Path = path,
|
||||||
}, newIndex);
|
Type = imageType
|
||||||
|
},
|
||||||
|
newIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadBackdrops(BaseItem item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
|
private async Task DownloadBackdrops(BaseItem item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
|
||||||
|
|
|
@ -21,12 +21,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
where TItemType : BaseItem, IHasLookupInfo<TIdType>, new()
|
where TItemType : BaseItem, IHasLookupInfo<TIdType>, new()
|
||||||
where TIdType : ItemLookupInfo, new()
|
where TIdType : ItemLookupInfo, new()
|
||||||
{
|
{
|
||||||
protected readonly IServerConfigurationManager ServerConfigurationManager;
|
|
||||||
protected readonly ILogger<MetadataService<TItemType, TIdType>> Logger;
|
|
||||||
protected readonly IProviderManager ProviderManager;
|
|
||||||
protected readonly IFileSystem FileSystem;
|
|
||||||
protected readonly ILibraryManager LibraryManager;
|
|
||||||
|
|
||||||
protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger<MetadataService<TItemType, TIdType>> logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager)
|
protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger<MetadataService<TItemType, TIdType>> logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager)
|
||||||
{
|
{
|
||||||
ServerConfigurationManager = serverConfigurationManager;
|
ServerConfigurationManager = serverConfigurationManager;
|
||||||
|
@ -36,6 +30,26 @@ namespace MediaBrowser.Providers.Manager
|
||||||
LibraryManager = libraryManager;
|
LibraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected IServerConfigurationManager ServerConfigurationManager { get; }
|
||||||
|
|
||||||
|
protected ILogger<MetadataService<TItemType, TIdType>> Logger { get; }
|
||||||
|
|
||||||
|
protected IProviderManager ProviderManager { get; }
|
||||||
|
|
||||||
|
protected IFileSystem FileSystem { get; }
|
||||||
|
|
||||||
|
protected ILibraryManager LibraryManager { get; }
|
||||||
|
|
||||||
|
protected virtual bool EnableUpdatingPremiereDateFromChildren => false;
|
||||||
|
|
||||||
|
protected virtual bool EnableUpdatingGenresFromChildren => false;
|
||||||
|
|
||||||
|
protected virtual bool EnableUpdatingStudiosFromChildren => false;
|
||||||
|
|
||||||
|
protected virtual bool EnableUpdatingOfficialRatingFromChildren => false;
|
||||||
|
|
||||||
|
public virtual int Order => 0;
|
||||||
|
|
||||||
private FileSystemMetadata TryGetFile(string path, IDirectoryService directoryService)
|
private FileSystemMetadata TryGetFile(string path, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -442,14 +456,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
return updateType;
|
return updateType;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual bool EnableUpdatingPremiereDateFromChildren => false;
|
|
||||||
|
|
||||||
protected virtual bool EnableUpdatingGenresFromChildren => false;
|
|
||||||
|
|
||||||
protected virtual bool EnableUpdatingStudiosFromChildren => false;
|
|
||||||
|
|
||||||
protected virtual bool EnableUpdatingOfficialRatingFromChildren => false;
|
|
||||||
|
|
||||||
private ItemUpdateType UpdatePremiereDate(TItemType item, IList<BaseItem> children)
|
private ItemUpdateType UpdatePremiereDate(TItemType item, IList<BaseItem> children)
|
||||||
{
|
{
|
||||||
var updateType = ItemUpdateType.None;
|
var updateType = ItemUpdateType.None;
|
||||||
|
@ -658,7 +664,8 @@ namespace MediaBrowser.Providers.Manager
|
||||||
return type == typeof(TItemType);
|
return type == typeof(TItemType);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual async Task<RefreshResult> RefreshWithProviders(MetadataResult<TItemType> metadata,
|
protected virtual async Task<RefreshResult> RefreshWithProviders(
|
||||||
|
MetadataResult<TItemType> metadata,
|
||||||
TIdType id,
|
TIdType id,
|
||||||
MetadataRefreshOptions options,
|
MetadataRefreshOptions options,
|
||||||
List<IMetadataProvider> providers,
|
List<IMetadataProvider> providers,
|
||||||
|
@ -773,7 +780,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
|
// TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
|
||||||
MergeData(metadata, temp, new MetadataField[] { }, false, false);
|
MergeData(metadata, temp, Array.Empty<MetadataField>(), false, false);
|
||||||
MergeData(temp, metadata, item.LockedFields, true, false);
|
MergeData(temp, metadata, item.LockedFields, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -900,24 +907,23 @@ namespace MediaBrowser.Providers.Manager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void MergeData(MetadataResult<TItemType> source,
|
protected abstract void MergeData(
|
||||||
|
MetadataResult<TItemType> source,
|
||||||
MetadataResult<TItemType> target,
|
MetadataResult<TItemType> target,
|
||||||
MetadataField[] lockedFields,
|
MetadataField[] lockedFields,
|
||||||
bool replaceData,
|
bool replaceData,
|
||||||
bool mergeMetadataSettings);
|
bool mergeMetadataSettings);
|
||||||
|
|
||||||
public virtual int Order => 0;
|
|
||||||
|
|
||||||
private bool HasChanged(BaseItem item, IHasItemChangeMonitor changeMonitor, IDirectoryService directoryService)
|
private bool HasChanged(BaseItem item, IHasItemChangeMonitor changeMonitor, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var hasChanged = changeMonitor.HasChanged(item, directoryService);
|
var hasChanged = changeMonitor.HasChanged(item, directoryService);
|
||||||
|
|
||||||
// if (hasChanged)
|
if (hasChanged)
|
||||||
//{
|
{
|
||||||
// logger.LogDebug("{0} reports change to {1}", changeMonitor.GetType().Name, item.Path ?? item.Name);
|
Logger.LogDebug("{0} reports change to {1}", changeMonitor.GetType().Name, item.Path ?? item.Name);
|
||||||
//}
|
}
|
||||||
|
|
||||||
return hasChanged;
|
return hasChanged;
|
||||||
}
|
}
|
||||||
|
@ -928,13 +934,4 @@ namespace MediaBrowser.Providers.Manager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RefreshResult
|
|
||||||
{
|
|
||||||
public ItemUpdateType UpdateType { get; set; }
|
|
||||||
|
|
||||||
public string ErrorMessage { get; set; }
|
|
||||||
|
|
||||||
public int Failures { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ using System.Net.Http;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Data.Entities;
|
|
||||||
using Jellyfin.Data.Events;
|
using Jellyfin.Data.Events;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Progress;
|
using MediaBrowser.Common.Progress;
|
||||||
|
@ -905,8 +904,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
return provider.GetImageResponse(url, cancellationToken);
|
return provider.GetImageResponse(url, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
private IEnumerable<IExternalId> GetExternalIds(IHasProviderIds item)
|
||||||
public IEnumerable<IExternalId> GetExternalIds(IHasProviderIds item)
|
|
||||||
{
|
{
|
||||||
return _externalIds.Where(i =>
|
return _externalIds.Where(i =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,12 +26,12 @@ namespace MediaBrowser.Providers.Manager
|
||||||
|
|
||||||
if (source == null)
|
if (source == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(source));
|
throw new ArgumentException("Item cannot be null.", nameof(sourceResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target == null)
|
if (target == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(target));
|
throw new ArgumentException("Item cannot be null.", nameof(targetResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lockedFields.Contains(MetadataField.Name))
|
if (!lockedFields.Contains(MetadataField.Name))
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.Manager
|
||||||
|
{
|
||||||
|
public class RefreshResult
|
||||||
|
{
|
||||||
|
public ItemUpdateType UpdateType { get; set; }
|
||||||
|
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public int Failures { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,10 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images");
|
||||||
|
|
||||||
|
public string Name => "Image Extractor";
|
||||||
|
|
||||||
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
|
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
|
||||||
{
|
{
|
||||||
return new List<ImageType> { ImageType.Primary };
|
return new List<ImageType> { ImageType.Primary };
|
||||||
|
@ -97,11 +101,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
if (item.GetType() == typeof(Audio))
|
if (item.GetType() == typeof(Audio))
|
||||||
{
|
{
|
||||||
var albumArtist = item.AlbumArtists.FirstOrDefault();
|
if (item.AlbumArtists.Count > 0
|
||||||
|
&& !string.IsNullOrWhiteSpace(item.Album)
|
||||||
if (!string.IsNullOrWhiteSpace(item.Album) && !string.IsNullOrWhiteSpace(albumArtist))
|
&& !string.IsNullOrWhiteSpace(item.AlbumArtists[0]))
|
||||||
{
|
{
|
||||||
filename = (item.Album + "-" + albumArtist).GetMD5().ToString("N", CultureInfo.InvariantCulture);
|
filename = (item.Album + "-" + item.AlbumArtists[0]).GetMD5().ToString("N", CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -121,10 +125,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return Path.Join(AudioImagesPath, prefix, filename);
|
return Path.Join(AudioImagesPath, prefix, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images");
|
|
||||||
|
|
||||||
public string Name => "Image Extractor";
|
|
||||||
|
|
||||||
public bool Supports(BaseItem item)
|
public bool Supports(BaseItem item)
|
||||||
{
|
{
|
||||||
if (item.IsShortcut)
|
if (item.IsShortcut)
|
||||||
|
|
|
@ -37,7 +37,9 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ItemUpdateType> Probe<T>(T item, MetadataRefreshOptions options,
|
public async Task<ItemUpdateType> Probe<T>(
|
||||||
|
T item,
|
||||||
|
MetadataRefreshOptions options,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
where T : Audio
|
where T : Audio
|
||||||
{
|
{
|
||||||
|
@ -52,19 +54,21 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
protocol = _mediaSourceManager.GetPathProtocol(path);
|
protocol = _mediaSourceManager.GetPathProtocol(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
var result = await _mediaEncoder.GetMediaInfo(
|
||||||
{
|
new MediaInfoRequest
|
||||||
MediaType = DlnaProfileType.Audio,
|
|
||||||
MediaSource = new MediaSourceInfo
|
|
||||||
{
|
{
|
||||||
Path = path,
|
MediaType = DlnaProfileType.Audio,
|
||||||
Protocol = protocol
|
MediaSource = new MediaSourceInfo
|
||||||
}
|
{
|
||||||
}, cancellationToken).ConfigureAwait(false);
|
Path = path,
|
||||||
|
Protocol = protocol
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
Fetch(item, cancellationToken, result);
|
Fetch(item, result, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ItemUpdateType.MetadataImport;
|
return ItemUpdateType.MetadataImport;
|
||||||
|
@ -74,10 +78,9 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
/// Fetches the specified audio.
|
/// Fetches the specified audio.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="audio">The audio.</param>
|
/// <param name="audio">The audio.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <param name="mediaInfo">The media information.</param>
|
/// <param name="mediaInfo">The media information.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
protected void Fetch(Audio audio, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo)
|
protected void Fetch(Audio audio, Model.MediaInfo.MediaInfo mediaInfo, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var mediaStreams = mediaInfo.MediaStreams;
|
var mediaStreams = mediaInfo.MediaStreams;
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,6 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Configuration;
|
|
||||||
using MediaBrowser.Controller.Channels;
|
|
||||||
using MediaBrowser.Controller.Chapters;
|
using MediaBrowser.Controller.Chapters;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
@ -20,9 +18,7 @@ using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Controller.Subtitles;
|
using MediaBrowser.Controller.Subtitles;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using MediaBrowser.Model.Serialization;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.MediaInfo
|
namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
@ -50,9 +46,43 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
private readonly IChapterManager _chapterManager;
|
private readonly IChapterManager _chapterManager;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
|
private readonly SubtitleResolver _subtitleResolver;
|
||||||
|
|
||||||
|
private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
|
||||||
|
|
||||||
|
public FFProbeProvider(
|
||||||
|
ILogger<FFProbeProvider> logger,
|
||||||
|
IMediaSourceManager mediaSourceManager,
|
||||||
|
IMediaEncoder mediaEncoder,
|
||||||
|
IItemRepository itemRepo,
|
||||||
|
IBlurayExaminer blurayExaminer,
|
||||||
|
ILocalizationManager localization,
|
||||||
|
IEncodingManager encodingManager,
|
||||||
|
IServerConfigurationManager config,
|
||||||
|
ISubtitleManager subtitleManager,
|
||||||
|
IChapterManager chapterManager,
|
||||||
|
ILibraryManager libraryManager)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_mediaEncoder = mediaEncoder;
|
||||||
|
_itemRepo = itemRepo;
|
||||||
|
_blurayExaminer = blurayExaminer;
|
||||||
|
_localization = localization;
|
||||||
|
_encodingManager = encodingManager;
|
||||||
|
_config = config;
|
||||||
|
_subtitleManager = subtitleManager;
|
||||||
|
_chapterManager = chapterManager;
|
||||||
|
_libraryManager = libraryManager;
|
||||||
|
_mediaSourceManager = mediaSourceManager;
|
||||||
|
|
||||||
|
_subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
public string Name => "ffprobe";
|
public string Name => "ffprobe";
|
||||||
|
|
||||||
|
// Run last
|
||||||
|
public int Order => 100;
|
||||||
|
|
||||||
public bool HasChanged(BaseItem item, IDirectoryService directoryService)
|
public bool HasChanged(BaseItem item, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var video = item as Video;
|
var video = item as Video;
|
||||||
|
@ -117,37 +147,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return FetchAudioInfo(item, options, cancellationToken);
|
return FetchAudioInfo(item, options, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SubtitleResolver _subtitleResolver;
|
|
||||||
|
|
||||||
public FFProbeProvider(
|
|
||||||
ILogger<FFProbeProvider> logger,
|
|
||||||
IMediaSourceManager mediaSourceManager,
|
|
||||||
IMediaEncoder mediaEncoder,
|
|
||||||
IItemRepository itemRepo,
|
|
||||||
IBlurayExaminer blurayExaminer,
|
|
||||||
ILocalizationManager localization,
|
|
||||||
IEncodingManager encodingManager,
|
|
||||||
IServerConfigurationManager config,
|
|
||||||
ISubtitleManager subtitleManager,
|
|
||||||
IChapterManager chapterManager,
|
|
||||||
ILibraryManager libraryManager)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_mediaEncoder = mediaEncoder;
|
|
||||||
_itemRepo = itemRepo;
|
|
||||||
_blurayExaminer = blurayExaminer;
|
|
||||||
_localization = localization;
|
|
||||||
_encodingManager = encodingManager;
|
|
||||||
_config = config;
|
|
||||||
_subtitleManager = subtitleManager;
|
|
||||||
_chapterManager = chapterManager;
|
|
||||||
_libraryManager = libraryManager;
|
|
||||||
_mediaSourceManager = mediaSourceManager;
|
|
||||||
|
|
||||||
_subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
|
|
||||||
public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
|
public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
|
||||||
where T : Video
|
where T : Video
|
||||||
{
|
{
|
||||||
|
@ -234,8 +233,5 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
return prober.Probe(item, options, cancellationToken);
|
return prober.Probe(item, options, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run last
|
|
||||||
public int Order => 100;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -539,17 +539,18 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
if (enableSubtitleDownloading && enabled)
|
if (enableSubtitleDownloading && enabled)
|
||||||
{
|
{
|
||||||
var downloadedLanguages = await new SubtitleDownloader(_logger,
|
var downloadedLanguages = await new SubtitleDownloader(
|
||||||
_subtitleManager)
|
_logger,
|
||||||
.DownloadSubtitles(video,
|
_subtitleManager).DownloadSubtitles(
|
||||||
currentStreams.Concat(externalSubtitleStreams).ToList(),
|
video,
|
||||||
skipIfEmbeddedSubtitlesPresent,
|
currentStreams.Concat(externalSubtitleStreams).ToList(),
|
||||||
skipIfAudioTrackMatches,
|
skipIfEmbeddedSubtitlesPresent,
|
||||||
requirePerfectMatch,
|
skipIfAudioTrackMatches,
|
||||||
subtitleDownloadLanguages,
|
requirePerfectMatch,
|
||||||
libraryOptions.DisabledSubtitleFetchers,
|
subtitleDownloadLanguages,
|
||||||
libraryOptions.SubtitleFetcherOrder,
|
libraryOptions.DisabledSubtitleFetchers,
|
||||||
cancellationToken).ConfigureAwait(false);
|
libraryOptions.SubtitleFetcherOrder,
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// Rescan
|
// Rescan
|
||||||
if (downloadedLanguages.Count > 0)
|
if (downloadedLanguages.Count > 0)
|
||||||
|
|
|
@ -42,8 +42,16 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
foreach (var lang in languages)
|
foreach (var lang in languages)
|
||||||
{
|
{
|
||||||
var downloaded = await DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent,
|
var downloaded = await DownloadSubtitles(
|
||||||
skipIfAudioTrackMatches, requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, cancellationToken).ConfigureAwait(false);
|
video,
|
||||||
|
mediaStreams,
|
||||||
|
skipIfEmbeddedSubtitlesPresent,
|
||||||
|
skipIfAudioTrackMatches,
|
||||||
|
requirePerfectMatch,
|
||||||
|
lang,
|
||||||
|
disabledSubtitleFetchers,
|
||||||
|
subtitleFetcherOrder,
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (downloaded)
|
if (downloaded)
|
||||||
{
|
{
|
||||||
|
@ -54,7 +62,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return downloadedLanguages;
|
return downloadedLanguages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> DownloadSubtitles(Video video,
|
public Task<bool> DownloadSubtitles(
|
||||||
|
Video video,
|
||||||
List<MediaStream> mediaStreams,
|
List<MediaStream> mediaStreams,
|
||||||
bool skipIfEmbeddedSubtitlesPresent,
|
bool skipIfEmbeddedSubtitlesPresent,
|
||||||
bool skipIfAudioTrackMatches,
|
bool skipIfAudioTrackMatches,
|
||||||
|
@ -90,11 +99,21 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return Task.FromResult(false);
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent, skipIfAudioTrackMatches,
|
return DownloadSubtitles(
|
||||||
requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, mediaType, cancellationToken);
|
video,
|
||||||
|
mediaStreams,
|
||||||
|
skipIfEmbeddedSubtitlesPresent,
|
||||||
|
skipIfAudioTrackMatches,
|
||||||
|
requirePerfectMatch,
|
||||||
|
lang,
|
||||||
|
disabledSubtitleFetchers,
|
||||||
|
subtitleFetcherOrder,
|
||||||
|
mediaType,
|
||||||
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> DownloadSubtitles(Video video,
|
private async Task<bool> DownloadSubtitles(
|
||||||
|
Video video,
|
||||||
List<MediaStream> mediaStreams,
|
List<MediaStream> mediaStreams,
|
||||||
bool skipIfEmbeddedSubtitlesPresent,
|
bool skipIfEmbeddedSubtitlesPresent,
|
||||||
bool skipIfAudioTrackMatches,
|
bool skipIfAudioTrackMatches,
|
||||||
|
|
|
@ -66,9 +66,10 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return streams;
|
return streams;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> GetExternalSubtitleFiles(Video video,
|
public List<string> GetExternalSubtitleFiles(
|
||||||
IDirectoryService directoryService,
|
Video video,
|
||||||
bool clearCache)
|
IDirectoryService directoryService,
|
||||||
|
bool clearCache)
|
||||||
{
|
{
|
||||||
var list = new List<string>();
|
var list = new List<string>();
|
||||||
|
|
||||||
|
@ -87,7 +88,9 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddExternalSubtitleStreams(List<MediaStream> streams, string folder,
|
private void AddExternalSubtitleStreams(
|
||||||
|
List<MediaStream> streams,
|
||||||
|
string folder,
|
||||||
string videoPath,
|
string videoPath,
|
||||||
int startIndex,
|
int startIndex,
|
||||||
IDirectoryService directoryService,
|
IDirectoryService directoryService,
|
||||||
|
@ -98,7 +101,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
|
AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddExternalSubtitleStreams(List<MediaStream> streams,
|
public void AddExternalSubtitleStreams(
|
||||||
|
List<MediaStream> streams,
|
||||||
string videoPath,
|
string videoPath,
|
||||||
int startIndex,
|
int startIndex,
|
||||||
string[] files)
|
string[] files)
|
||||||
|
@ -185,8 +189,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
private string NormalizeFilenameForSubtitleComparison(string filename)
|
private string NormalizeFilenameForSubtitleComparison(string filename)
|
||||||
{
|
{
|
||||||
// Try to account for sloppy file naming
|
// Try to account for sloppy file naming
|
||||||
filename = filename.Replace("_", string.Empty);
|
filename = filename.Replace("_", string.Empty, StringComparison.Ordinal);
|
||||||
filename = filename.Replace(" ", string.Empty);
|
filename = filename.Replace(" ", string.Empty, StringComparison.Ordinal);
|
||||||
|
|
||||||
// can't normalize this due to languages such as pt-br
|
// can't normalize this due to languages such as pt-br
|
||||||
// filename = filename.Replace("-", string.Empty);
|
// filename = filename.Replace("-", string.Empty);
|
||||||
|
|
|
@ -12,11 +12,10 @@ using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Subtitles;
|
using MediaBrowser.Controller.Subtitles;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Providers;
|
using MediaBrowser.Model.Providers;
|
||||||
using MediaBrowser.Model.Serialization;
|
|
||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MediaBrowser.Model.Globalization;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.MediaInfo
|
namespace MediaBrowser.Providers.MediaInfo
|
||||||
{
|
{
|
||||||
|
@ -25,29 +24,37 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly ISubtitleManager _subtitleManager;
|
private readonly ISubtitleManager _subtitleManager;
|
||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
|
||||||
private readonly ILogger<SubtitleScheduledTask> _logger;
|
private readonly ILogger<SubtitleScheduledTask> _logger;
|
||||||
private readonly IJsonSerializer _json;
|
|
||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
|
|
||||||
public SubtitleScheduledTask(
|
public SubtitleScheduledTask(
|
||||||
ILibraryManager libraryManager,
|
ILibraryManager libraryManager,
|
||||||
IJsonSerializer json,
|
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
ISubtitleManager subtitleManager,
|
ISubtitleManager subtitleManager,
|
||||||
ILogger<SubtitleScheduledTask> logger,
|
ILogger<SubtitleScheduledTask> logger,
|
||||||
IMediaSourceManager mediaSourceManager,
|
|
||||||
ILocalizationManager localization)
|
ILocalizationManager localization)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_config = config;
|
_config = config;
|
||||||
_subtitleManager = subtitleManager;
|
_subtitleManager = subtitleManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_mediaSourceManager = mediaSourceManager;
|
|
||||||
_json = json;
|
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name => _localization.GetLocalizedString("TaskDownloadMissingSubtitles");
|
||||||
|
|
||||||
|
public string Description => _localization.GetLocalizedString("TaskDownloadMissingSubtitlesDescription");
|
||||||
|
|
||||||
|
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
|
||||||
|
|
||||||
|
public string Key => "DownloadSubtitles";
|
||||||
|
|
||||||
|
public bool IsHidden => false;
|
||||||
|
|
||||||
|
public bool IsEnabled => true;
|
||||||
|
|
||||||
|
public bool IsLogged => true;
|
||||||
|
|
||||||
private SubtitleOptions GetOptions()
|
private SubtitleOptions GetOptions()
|
||||||
{
|
{
|
||||||
return _config.GetConfiguration<SubtitleOptions>("subtitles");
|
return _config.GetConfiguration<SubtitleOptions>("subtitles");
|
||||||
|
@ -66,23 +73,23 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
var libraryOptions = _libraryManager.GetLibraryOptions(library);
|
var libraryOptions = _libraryManager.GetLibraryOptions(library);
|
||||||
|
|
||||||
string[] subtitleDownloadLanguages;
|
string[] subtitleDownloadLanguages;
|
||||||
bool SkipIfEmbeddedSubtitlesPresent;
|
bool skipIfEmbeddedSubtitlesPresent;
|
||||||
bool SkipIfAudioTrackMatches;
|
bool skipIfAudioTrackMatches;
|
||||||
bool RequirePerfectMatch;
|
bool requirePerfectMatch;
|
||||||
|
|
||||||
if (libraryOptions.SubtitleDownloadLanguages == null)
|
if (libraryOptions.SubtitleDownloadLanguages == null)
|
||||||
{
|
{
|
||||||
subtitleDownloadLanguages = options.DownloadLanguages;
|
subtitleDownloadLanguages = options.DownloadLanguages;
|
||||||
SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
|
skipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
|
||||||
SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
|
skipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
|
||||||
RequirePerfectMatch = options.RequirePerfectMatch;
|
requirePerfectMatch = options.RequirePerfectMatch;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
|
subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
|
||||||
SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
|
skipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
|
||||||
SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
|
skipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
|
||||||
RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
|
requirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var lang in subtitleDownloadLanguages)
|
foreach (var lang in subtitleDownloadLanguages)
|
||||||
|
@ -98,12 +105,12 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
Recursive = true
|
Recursive = true
|
||||||
};
|
};
|
||||||
|
|
||||||
if (SkipIfAudioTrackMatches)
|
if (skipIfAudioTrackMatches)
|
||||||
{
|
{
|
||||||
query.HasNoAudioTrackWithLanguage = lang;
|
query.HasNoAudioTrackWithLanguage = lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SkipIfEmbeddedSubtitlesPresent)
|
if (skipIfEmbeddedSubtitlesPresent)
|
||||||
{
|
{
|
||||||
// Exclude if it already has any subtitles of the same language
|
// Exclude if it already has any subtitles of the same language
|
||||||
query.HasNoSubtitleTrackWithLanguage = lang;
|
query.HasNoSubtitleTrackWithLanguage = lang;
|
||||||
|
@ -160,36 +167,37 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
var libraryOptions = _libraryManager.GetLibraryOptions(video);
|
var libraryOptions = _libraryManager.GetLibraryOptions(video);
|
||||||
|
|
||||||
string[] subtitleDownloadLanguages;
|
string[] subtitleDownloadLanguages;
|
||||||
bool SkipIfEmbeddedSubtitlesPresent;
|
bool skipIfEmbeddedSubtitlesPresent;
|
||||||
bool SkipIfAudioTrackMatches;
|
bool skipIfAudioTrackMatches;
|
||||||
bool RequirePerfectMatch;
|
bool requirePerfectMatch;
|
||||||
|
|
||||||
if (libraryOptions.SubtitleDownloadLanguages == null)
|
if (libraryOptions.SubtitleDownloadLanguages == null)
|
||||||
{
|
{
|
||||||
subtitleDownloadLanguages = options.DownloadLanguages;
|
subtitleDownloadLanguages = options.DownloadLanguages;
|
||||||
SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
|
skipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
|
||||||
SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
|
skipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
|
||||||
RequirePerfectMatch = options.RequirePerfectMatch;
|
requirePerfectMatch = options.RequirePerfectMatch;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
|
subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
|
||||||
SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
|
skipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
|
||||||
SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
|
skipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
|
||||||
RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
|
requirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
var downloadedLanguages = await new SubtitleDownloader(_logger,
|
var downloadedLanguages = await new SubtitleDownloader(
|
||||||
_subtitleManager)
|
_logger,
|
||||||
.DownloadSubtitles(video,
|
_subtitleManager).DownloadSubtitles(
|
||||||
mediaStreams,
|
video,
|
||||||
SkipIfEmbeddedSubtitlesPresent,
|
mediaStreams,
|
||||||
SkipIfAudioTrackMatches,
|
skipIfEmbeddedSubtitlesPresent,
|
||||||
RequirePerfectMatch,
|
skipIfAudioTrackMatches,
|
||||||
subtitleDownloadLanguages,
|
requirePerfectMatch,
|
||||||
libraryOptions.DisabledSubtitleFetchers,
|
subtitleDownloadLanguages,
|
||||||
libraryOptions.SubtitleFetcherOrder,
|
libraryOptions.DisabledSubtitleFetchers,
|
||||||
cancellationToken).ConfigureAwait(false);
|
libraryOptions.SubtitleFetcherOrder,
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// Rescan
|
// Rescan
|
||||||
if (downloadedLanguages.Count > 0)
|
if (downloadedLanguages.Count > 0)
|
||||||
|
@ -203,25 +211,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||||
{
|
{
|
||||||
return new[] {
|
return new[]
|
||||||
|
{
|
||||||
// Every so often
|
// Every so often
|
||||||
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
|
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name => _localization.GetLocalizedString("TaskDownloadMissingSubtitles");
|
|
||||||
|
|
||||||
public string Description => _localization.GetLocalizedString("TaskDownloadMissingSubtitlesDescription");
|
|
||||||
|
|
||||||
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
|
|
||||||
|
|
||||||
public string Key => "DownloadSubtitles";
|
|
||||||
|
|
||||||
public bool IsHidden => false;
|
|
||||||
|
|
||||||
public bool IsEnabled => true;
|
|
||||||
|
|
||||||
public bool IsLogged => true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name => "Screen Grabber";
|
||||||
|
|
||||||
|
// Make sure this comes after internet image providers
|
||||||
|
public int Order => 100;
|
||||||
|
|
||||||
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
|
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
|
||||||
{
|
{
|
||||||
return new List<ImageType> { ImageType.Primary };
|
return new List<ImageType> { ImageType.Primary };
|
||||||
|
@ -127,8 +132,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name => "Screen Grabber";
|
|
||||||
|
|
||||||
public bool Supports(BaseItem item)
|
public bool Supports(BaseItem item)
|
||||||
{
|
{
|
||||||
if (item.IsShortcut)
|
if (item.IsShortcut)
|
||||||
|
@ -150,7 +153,5 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Make sure this comes after internet image providers
|
|
||||||
public int Order => 100;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,22 +36,4 @@ namespace MediaBrowser.Providers.Movies
|
||||||
return item is Movie || item is MusicVideo || item is Series || item is Episode || item is Trailer;
|
return item is Movie || item is MusicVideo || item is Series || item is Episode || item is Trailer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ImdbPersonExternalId : IExternalId
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ProviderName => "IMDb";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => MetadataProvider.Imdb.ToString();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string UrlFormatString => "https://www.imdb.com/name/{0}";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Supports(IHasProviderIds item) => item is Person;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.Movies
|
||||||
|
{
|
||||||
|
public class ImdbPersonExternalId : IExternalId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ProviderName => "IMDb";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => MetadataProvider.Imdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string UrlFormatString => "https://www.imdb.com/name/{0}";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Supports(IHasProviderIds item) => item is Person;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Playlists;
|
using MediaBrowser.Controller.Playlists;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using PlaylistsNET.Content;
|
using PlaylistsNET.Content;
|
||||||
|
|
||||||
|
@ -23,16 +22,17 @@ namespace MediaBrowser.Providers.Playlists
|
||||||
IHasItemChangeMonitor
|
IHasItemChangeMonitor
|
||||||
{
|
{
|
||||||
private readonly ILogger<PlaylistItemsProvider> _logger;
|
private readonly ILogger<PlaylistItemsProvider> _logger;
|
||||||
private IFileSystem _fileSystem;
|
|
||||||
|
|
||||||
public PlaylistItemsProvider(IFileSystem fileSystem, ILogger<PlaylistItemsProvider> logger)
|
public PlaylistItemsProvider(ILogger<PlaylistItemsProvider> logger)
|
||||||
{
|
{
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name => "Playlist Reader";
|
public string Name => "Playlist Reader";
|
||||||
|
|
||||||
|
// Run last
|
||||||
|
public int Order => 100;
|
||||||
|
|
||||||
public Task<ItemUpdateType> FetchAsync(Playlist item, MetadataRefreshOptions options, CancellationToken cancellationToken)
|
public Task<ItemUpdateType> FetchAsync(Playlist item, MetadataRefreshOptions options, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var path = item.Path;
|
var path = item.Path;
|
||||||
|
@ -163,7 +163,5 @@ namespace MediaBrowser.Providers.Playlists
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Run last
|
|
||||||
public int Order => 100;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,16 +23,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
{
|
{
|
||||||
public class AudioDbArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IHasOrder
|
public class AudioDbArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IHasOrder
|
||||||
{
|
{
|
||||||
|
private const string ApiKey = "195003";
|
||||||
|
public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey;
|
||||||
|
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly IJsonSerializer _json;
|
private readonly IJsonSerializer _json;
|
||||||
|
|
||||||
public static AudioDbArtistProvider Current;
|
|
||||||
|
|
||||||
private const string ApiKey = "195003";
|
|
||||||
public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey;
|
|
||||||
|
|
||||||
public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json)
|
public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
|
@ -42,6 +40,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
Current = this;
|
Current = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AudioDbArtistProvider Current { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "TheAudioDB";
|
public string Name => "TheAudioDB";
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
{
|
||||||
|
public class AudioDbAlbumExternalId : IExternalId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ProviderName => "TheAudioDb";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => MetadataProvider.AudioDbAlbum.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => null;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Supports(IHasProviderIds item) => item is MusicAlbum;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
{
|
||||||
|
public class AudioDbArtistExternalId : IExternalId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ProviderName => "TheAudioDb";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => MetadataProvider.AudioDbArtist.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Supports(IHasProviderIds item) => item is MusicArtist;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
{
|
||||||
|
public class AudioDbOtherAlbumExternalId : IExternalId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ProviderName => "TheAudioDb";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => MetadataProvider.AudioDbAlbum.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Supports(IHasProviderIds item) => item is Audio;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
{
|
||||||
|
public class AudioDbOtherArtistExternalId : IExternalId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ProviderName => "TheAudioDb";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => MetadataProvider.AudioDbArtist.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,81 +0,0 @@
|
||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
|
||||||
using MediaBrowser.Controller.Providers;
|
|
||||||
using MediaBrowser.Model.Entities;
|
|
||||||
using MediaBrowser.Model.Providers;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Plugins.AudioDb
|
|
||||||
{
|
|
||||||
public class AudioDbAlbumExternalId : IExternalId
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ProviderName => "TheAudioDb";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => MetadataProvider.AudioDbAlbum.ToString();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ExternalIdMediaType? Type => null;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Supports(IHasProviderIds item) => item is MusicAlbum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AudioDbOtherAlbumExternalId : IExternalId
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ProviderName => "TheAudioDb";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => MetadataProvider.AudioDbAlbum.ToString();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Supports(IHasProviderIds item) => item is Audio;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AudioDbArtistExternalId : IExternalId
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ProviderName => "TheAudioDb";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => MetadataProvider.AudioDbArtist.ToString();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Supports(IHasProviderIds item) => item is MusicArtist;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AudioDbOtherArtistExternalId : IExternalId
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ProviderName => "TheAudioDb";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => MetadataProvider.AudioDbArtist.ToString();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,6 +11,12 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
{
|
{
|
||||||
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||||
{
|
{
|
||||||
|
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
|
||||||
|
: base(applicationPaths, xmlSerializer)
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
public static Plugin Instance { get; private set; }
|
public static Plugin Instance { get; private set; }
|
||||||
|
|
||||||
public override Guid Id => new Guid("a629c0da-fac5-4c7e-931a-7174223f14c8");
|
public override Guid Id => new Guid("a629c0da-fac5-4c7e-931a-7174223f14c8");
|
||||||
|
@ -22,12 +28,6 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
// TODO remove when plugin removed from server.
|
// TODO remove when plugin removed from server.
|
||||||
public override string ConfigurationFileName => "Jellyfin.Plugin.AudioDb.xml";
|
public override string ConfigurationFileName => "Jellyfin.Plugin.AudioDb.xml";
|
||||||
|
|
||||||
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
|
|
||||||
: base(applicationPaths, xmlSerializer)
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<PluginPageInfo> GetPages()
|
public IEnumerable<PluginPageInfo> GetPages()
|
||||||
{
|
{
|
||||||
yield return new PluginPageInfo
|
yield return new PluginPageInfo
|
||||||
|
|
|
@ -8,7 +8,6 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -26,14 +25,6 @@ namespace MediaBrowser.Providers.Music
|
||||||
{
|
{
|
||||||
public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, AlbumInfo>, IHasOrder
|
public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, AlbumInfo>, IHasOrder
|
||||||
{
|
{
|
||||||
/// <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.
|
|
||||||
/// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting
|
|
||||||
/// </summary>
|
|
||||||
private readonly long _musicBrainzQueryIntervalMs;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For each single MB lookup/search, this is the maximum number of
|
/// For each single MB lookup/search, this is the maximum number of
|
||||||
/// attempts that shall be made whilst receiving a 503 Server
|
/// attempts that shall be made whilst receiving a 503 Server
|
||||||
|
@ -41,7 +32,13 @@ namespace MediaBrowser.Providers.Music
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const uint MusicBrainzQueryAttempts = 5u;
|
private const uint MusicBrainzQueryAttempts = 5u;
|
||||||
|
|
||||||
internal static MusicBrainzAlbumProvider Current;
|
/// <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.
|
||||||
|
/// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting
|
||||||
|
/// </summary>
|
||||||
|
private readonly long _musicBrainzQueryIntervalMs;
|
||||||
|
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly IApplicationHost _appHost;
|
private readonly IApplicationHost _appHost;
|
||||||
|
@ -69,6 +66,8 @@ namespace MediaBrowser.Providers.Music
|
||||||
Current = this;
|
Current = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static MusicBrainzAlbumProvider Current { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "MusicBrainz";
|
public string Name => "MusicBrainz";
|
||||||
|
|
||||||
|
@ -112,7 +111,7 @@ namespace MediaBrowser.Providers.Music
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// I'm sure there is a better way but for now it resolves search for 12" Mixes
|
// I'm sure there is a better way but for now it resolves search for 12" Mixes
|
||||||
var queryName = searchInfo.Name.Replace("\"", string.Empty);
|
var queryName = searchInfo.Name.Replace("\"", string.Empty, StringComparison.Ordinal);
|
||||||
|
|
||||||
url = string.Format(
|
url = string.Format(
|
||||||
CultureInfo.InvariantCulture,
|
CultureInfo.InvariantCulture,
|
||||||
|
@ -277,7 +276,9 @@ namespace MediaBrowser.Providers.Music
|
||||||
|
|
||||||
private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken)
|
private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/release/?query=\"{0}\" AND arid:{1}",
|
var url = string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
"/ws/2/release/?query=\"{0}\" AND arid:{1}",
|
||||||
WebUtility.UrlEncode(albumName),
|
WebUtility.UrlEncode(albumName),
|
||||||
artistId);
|
artistId);
|
||||||
|
|
||||||
|
@ -496,7 +497,7 @@ namespace MediaBrowser.Providers.Music
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ValueTuple<string, string> ParseArtistCredit(XmlReader reader)
|
private static (string, string) ParseArtistCredit(XmlReader reader)
|
||||||
{
|
{
|
||||||
reader.MoveToContent();
|
reader.MoveToContent();
|
||||||
reader.Read();
|
reader.Read();
|
||||||
|
@ -531,7 +532,7 @@ namespace MediaBrowser.Providers.Music
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ValueTuple<string, string>();
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (string, string) ParseArtistNameCredit(XmlReader reader)
|
private static (string, string) ParseArtistNameCredit(XmlReader reader)
|
|
@ -36,6 +36,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name => "The Open Movie Database";
|
||||||
|
|
||||||
|
// After other internet providers, because they're better
|
||||||
|
// But before fallback providers like screengrab
|
||||||
|
public int Order => 90;
|
||||||
|
|
||||||
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
|
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
|
||||||
{
|
{
|
||||||
return new List<ImageType>
|
return new List<ImageType>
|
||||||
|
@ -86,15 +92,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
|
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name => "The Open Movie Database";
|
|
||||||
|
|
||||||
public bool Supports(BaseItem item)
|
public bool Supports(BaseItem item)
|
||||||
{
|
{
|
||||||
return item is Movie || item is Trailer || item is Episode;
|
return item is Movie || item is Trailer || item is Episode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// After other internet providers, because they're better
|
|
||||||
// But before fallback providers like screengrab
|
|
||||||
public int Order => 90;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name => "The Open Movie Database";
|
||||||
|
|
||||||
// After primary option
|
// After primary option
|
||||||
public int Order => 2;
|
public int Order => 2;
|
||||||
|
|
||||||
|
@ -199,8 +201,6 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
return GetSearchResults(searchInfo, "movie", cancellationToken);
|
return GetSearchResults(searchInfo, "movie", cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name => "The Open Movie Database";
|
|
||||||
|
|
||||||
public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
|
public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var result = new MetadataResult<Series>
|
var result = new MetadataResult<Series>
|
||||||
|
@ -263,14 +263,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
{
|
{
|
||||||
var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false);
|
var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false);
|
||||||
var first = results.FirstOrDefault();
|
var first = results.FirstOrDefault();
|
||||||
return first == null ? null : first.GetProviderId(MetadataProvider.Imdb);
|
return first?.GetProviderId(MetadataProvider.Imdb);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken)
|
private async Task<string> GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false);
|
var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false);
|
||||||
var first = results.FirstOrDefault();
|
var first = results.FirstOrDefault();
|
||||||
return first == null ? null : first.GetProviderId(MetadataProvider.Imdb);
|
return first?.GetProviderId(MetadataProvider.Imdb);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
|
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||||
|
@ -278,7 +278,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
|
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SearchResult
|
private class SearchResult
|
||||||
{
|
{
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
|
||||||
private readonly IJsonSerializer _json;
|
private readonly IJsonSerializer _json;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILocalizationManager _localization;
|
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
|
@ -46,7 +45,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
|
||||||
IJsonSerializer json,
|
IJsonSerializer json,
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
ILocalizationManager localization,
|
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
ILibraryManager libraryManager)
|
ILibraryManager libraryManager)
|
||||||
{
|
{
|
||||||
|
@ -54,7 +52,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
|
||||||
_json = json;
|
_json = json;
|
||||||
_config = config;
|
_config = config;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_localization = localization;
|
|
||||||
_httpClientFactory = httpClientFactory;
|
_httpClientFactory = httpClientFactory;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
Current = this;
|
Current = this;
|
||||||
|
@ -177,7 +174,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
|
||||||
|
|
||||||
private async Task<CollectionResult> FetchMainResult(string id, string language, CancellationToken cancellationToken)
|
private async Task<CollectionResult> FetchMainResult(string id, string language, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey);
|
var url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(language))
|
if (!string.IsNullOrEmpty(language))
|
||||||
{
|
{
|
||||||
|
@ -195,7 +192,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage);
|
using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
var mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(stream).ConfigureAwait(false);
|
var mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(stream).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -205,7 +202,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
|
if (!string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en";
|
url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en";
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(language))
|
if (!string.IsNullOrEmpty(language))
|
||||||
{
|
{
|
||||||
|
|
|
@ -155,7 +155,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var response = await GetMovieDbResponse(requestMessage).ConfigureAwait(false);
|
using var response = await GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
_tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSettingsResult>(stream).ConfigureAwait(false);
|
_tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSettingsResult>(stream).ConfigureAwait(false);
|
||||||
return _tmdbSettings;
|
return _tmdbSettings;
|
||||||
|
@ -335,7 +335,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var mainResponse = await GetMovieDbResponse(requestMessage).ConfigureAwait(false);
|
using var mainResponse = await GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
if (mainResponse.StatusCode == HttpStatusCode.NotFound)
|
if (mainResponse.StatusCode == HttpStatusCode.NotFound)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
@ -368,7 +368,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
||||||
langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var langResponse = await GetMovieDbResponse(langRequestMessage).ConfigureAwait(false);
|
using var langResponse = await GetMovieDbResponse(langRequestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
await using var langStream = await langResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var langStream = await langResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
var langResult = await _jsonSerializer.DeserializeFromStreamAsync<MovieResult>(stream).ConfigureAwait(false);
|
var langResult = await _jsonSerializer.DeserializeFromStreamAsync<MovieResult>(stream).ConfigureAwait(false);
|
||||||
|
@ -381,10 +381,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the movie db response.
|
/// Gets the movie db response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal Task<HttpResponseMessage> GetMovieDbResponse(HttpRequestMessage message)
|
internal Task<HttpResponseMessage> GetMovieDbResponse(HttpRequestMessage message, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
message.Headers.UserAgent.ParseAdd(_appHost.ApplicationUserAgent);
|
message.Headers.UserAgent.ParseAdd(_appHost.ApplicationUserAgent);
|
||||||
return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(message);
|
return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(message, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
@ -198,7 +198,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false);
|
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<MovieResult>>(stream).ConfigureAwait(false);
|
var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<MovieResult>>(stream).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -261,7 +261,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false);
|
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<TvResult>>(stream).ConfigureAwait(false);
|
var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<TvResult>>(stream).ConfigureAwait(false);
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false);
|
var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var result2 = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSearchResult<PersonSearchResult>>(stream).ConfigureAwait(false)
|
var result2 = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSearchResult<PersonSearchResult>>(stream).ConfigureAwait(false)
|
||||||
|
@ -243,7 +243,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false);
|
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
|
Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
|
||||||
await using var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
|
await using var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
|
||||||
await response.Content.CopyToAsync(fs).ConfigureAwait(false);
|
await response.Content.CopyToAsync(fs).ConfigureAwait(false);
|
||||||
|
|
|
@ -109,7 +109,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
item.ParentIndexNumber = info.ParentIndexNumber;
|
item.ParentIndexNumber = info.ParentIndexNumber;
|
||||||
item.IndexNumberEnd = info.IndexNumberEnd;
|
item.IndexNumberEnd = info.IndexNumberEnd;
|
||||||
|
|
||||||
if (response.External_Ids.Tvdb_Id > 0)
|
if (response.External_Ids != null && response.External_Ids.Tvdb_Id > 0)
|
||||||
{
|
{
|
||||||
item.SetProviderId(MetadataProvider.Tvdb, response.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture));
|
item.SetProviderId(MetadataProvider.Tvdb, response.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
|
|
||||||
internal async Task<EpisodeResult> FetchMainResult(string urlPattern, string id, int seasonNumber, int episodeNumber, string language, CancellationToken cancellationToken)
|
internal async Task<EpisodeResult> FetchMainResult(string urlPattern, string id, int seasonNumber, int episodeNumber, string language, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var url = string.Format(urlPattern, id, seasonNumber.ToString(CultureInfo.InvariantCulture), episodeNumber, TmdbUtils.ApiKey);
|
var url = string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
urlPattern,
|
||||||
|
id,
|
||||||
|
seasonNumber.ToString(CultureInfo.InvariantCulture),
|
||||||
|
episodeNumber,
|
||||||
|
TmdbUtils.ApiKey);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(language))
|
if (!string.IsNullOrEmpty(language))
|
||||||
{
|
{
|
||||||
|
@ -132,7 +138,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage);
|
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
return await _jsonSerializer.DeserializeFromStreamAsync<EpisodeResult>(stream).ConfigureAwait(false);
|
return await _jsonSerializer.DeserializeFromStreamAsync<EpisodeResult>(stream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
|
|
||||||
result.Item.Overview = seasonInfo.Overview;
|
result.Item.Overview = seasonInfo.Overview;
|
||||||
|
|
||||||
if (seasonInfo.External_Ids.Tvdb_Id > 0)
|
if (seasonInfo.External_Ids != null && seasonInfo.External_Ids.Tvdb_Id > 0)
|
||||||
{
|
{
|
||||||
result.Item.SetProviderId(MetadataProvider.Tvdb, seasonInfo.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture));
|
result.Item.SetProviderId(MetadataProvider.Tvdb, seasonInfo.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
|
|
||||||
internal async Task<SeasonResult> FetchMainResult(string id, int seasonNumber, string language, CancellationToken cancellationToken)
|
internal async Task<SeasonResult> FetchMainResult(string id, int seasonNumber, string language, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var url = string.Format(GetTvInfo3, id, seasonNumber.ToString(CultureInfo.InvariantCulture), TmdbUtils.ApiKey);
|
var url = string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
GetTvInfo3,
|
||||||
|
id,
|
||||||
|
seasonNumber.ToString(CultureInfo.InvariantCulture),
|
||||||
|
TmdbUtils.ApiKey);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(language))
|
if (!string.IsNullOrEmpty(language))
|
||||||
{
|
{
|
||||||
|
@ -219,7 +224,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage).ConfigureAwait(false);
|
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
return await _jsonSerializer.DeserializeFromStreamAsync<SeasonResult>(stream).ConfigureAwait(false);
|
return await _jsonSerializer.DeserializeFromStreamAsync<SeasonResult>(stream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
remoteResult.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture));
|
remoteResult.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture));
|
||||||
remoteResult.SetProviderId(MetadataProvider.Imdb, obj.External_Ids.Imdb_Id);
|
remoteResult.SetProviderId(MetadataProvider.Imdb, obj.External_Ids.Imdb_Id);
|
||||||
|
|
||||||
if (obj.External_Ids.Tvdb_Id > 0)
|
if (obj.External_Ids != null && obj.External_Ids.Tvdb_Id > 0)
|
||||||
{
|
{
|
||||||
remoteResult.SetProviderId(MetadataProvider.Tvdb, obj.External_Ids.Tvdb_Id.Value.ToString(_usCulture));
|
remoteResult.SetProviderId(MetadataProvider.Tvdb, obj.External_Ids.Tvdb_Id.Value.ToString(_usCulture));
|
||||||
}
|
}
|
||||||
|
@ -405,7 +405,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
|
|
||||||
internal async Task<SeriesResult> FetchMainResult(string id, string language, CancellationToken cancellationToken)
|
internal async Task<SeriesResult> FetchMainResult(string id, string language, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var url = string.Format(GetTvInfo3, id, TmdbUtils.ApiKey);
|
var url = string.Format(CultureInfo.InvariantCulture, GetTvInfo3, id, TmdbUtils.ApiKey);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(language))
|
if (!string.IsNullOrEmpty(language))
|
||||||
{
|
{
|
||||||
|
@ -421,7 +421,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(mainRequestMessage);
|
using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(mainRequestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var mainStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var mainStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
var mainResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(mainStream).ConfigureAwait(false);
|
var mainResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(mainStream).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -440,7 +440,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
{
|
{
|
||||||
_logger.LogInformation("MovieDbSeriesProvider couldn't find meta for language {Language}. Trying English...", language);
|
_logger.LogInformation("MovieDbSeriesProvider couldn't find meta for language {Language}. Trying English...", language);
|
||||||
|
|
||||||
url = string.Format(GetTvInfo3, id, TmdbUtils.ApiKey) + "&language=en";
|
url = string.Format(CultureInfo.InvariantCulture, GetTvInfo3, id, TmdbUtils.ApiKey) + "&language=en";
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(language))
|
if (!string.IsNullOrEmpty(language))
|
||||||
{
|
{
|
||||||
|
@ -454,7 +454,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage);
|
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(stream).ConfigureAwait(false);
|
var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(stream).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -504,7 +504,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
|
|
||||||
private async Task<RemoteSearchResult> FindByExternalId(string id, string externalSource, CancellationToken cancellationToken)
|
private async Task<RemoteSearchResult> FindByExternalId(string id, string externalSource, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/find/{0}?api_key={1}&external_source={2}",
|
var url = string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
TmdbUtils.BaseTmdbApiUrl + @"3/find/{0}?api_key={1}&external_source={2}",
|
||||||
id,
|
id,
|
||||||
TmdbUtils.ApiKey,
|
TmdbUtils.ApiKey,
|
||||||
externalSource);
|
externalSource);
|
||||||
|
@ -515,7 +517,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage);
|
using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var result = await _jsonSerializer.DeserializeFromStreamAsync<ExternalIdLookupResult>(stream).ConfigureAwait(false);
|
var result = await _jsonSerializer.DeserializeFromStreamAsync<ExternalIdLookupResult>(stream).ConfigureAwait(false);
|
||||||
|
|
|
@ -124,7 +124,8 @@ namespace MediaBrowser.Providers.TV
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the season.
|
/// Adds the season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<Season> AddSeason(Series series,
|
public async Task<Season> AddSeason(
|
||||||
|
Series series,
|
||||||
int? seasonNumber,
|
int? seasonNumber,
|
||||||
bool isVirtualItem,
|
bool isVirtualItem,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
|
@ -211,11 +212,14 @@ namespace MediaBrowser.Providers.TV
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Removing virtual season {0} {1}", series.Name, seasonToRemove.IndexNumber);
|
_logger.LogInformation("Removing virtual season {0} {1}", series.Name, seasonToRemove.IndexNumber);
|
||||||
|
|
||||||
_libraryManager.DeleteItem(seasonToRemove, new DeleteOptions
|
_libraryManager.DeleteItem(
|
||||||
{
|
seasonToRemove,
|
||||||
DeleteFileLocation = true
|
new DeleteOptions
|
||||||
|
{
|
||||||
|
DeleteFileLocation = true
|
||||||
|
|
||||||
}, false);
|
},
|
||||||
|
false);
|
||||||
|
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,7 +159,7 @@ namespace MediaBrowser.Providers.TV
|
||||||
|
|
||||||
var now = DateTime.UtcNow.AddDays(-UnairedEpisodeThresholdDays);
|
var now = DateTime.UtcNow.AddDays(-UnairedEpisodeThresholdDays);
|
||||||
|
|
||||||
if (airDate < now && addMissingEpisodes || airDate > now)
|
if ((airDate < now && addMissingEpisodes) || airDate > now)
|
||||||
{
|
{
|
||||||
// tvdb has a lot of nearly blank episodes
|
// tvdb has a lot of nearly blank episodes
|
||||||
_logger.LogInformation("Creating virtual missing/unaired episode {0} {1}x{2}", series.Name, tuple.seasonNumber, tuple.episodenumber);
|
_logger.LogInformation("Creating virtual missing/unaired episode {0} {1}x{2}", series.Name, tuple.seasonNumber, tuple.episodenumber);
|
||||||
|
@ -232,10 +232,13 @@ namespace MediaBrowser.Providers.TV
|
||||||
|
|
||||||
foreach (var episodeToRemove in episodesToRemove)
|
foreach (var episodeToRemove in episodesToRemove)
|
||||||
{
|
{
|
||||||
_libraryManager.DeleteItem(episodeToRemove, new DeleteOptions
|
_libraryManager.DeleteItem(
|
||||||
{
|
episodeToRemove,
|
||||||
DeleteFileLocation = true
|
new DeleteOptions
|
||||||
}, false);
|
{
|
||||||
|
DeleteFileLocation = true
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
}
|
}
|
||||||
|
@ -246,7 +249,7 @@ namespace MediaBrowser.Providers.TV
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the obsolete or missing seasons.
|
/// Removes the obsolete or missing seasons.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="allRecursiveChildren"></param>
|
/// <param name="allRecursiveChildren">All recursive children.</param>
|
||||||
/// <param name="episodeLookup">The episode lookup.</param>
|
/// <param name="episodeLookup">The episode lookup.</param>
|
||||||
/// <returns><see cref="bool" />.</returns>
|
/// <returns><see cref="bool" />.</returns>
|
||||||
private bool RemoveObsoleteOrMissingSeasons(
|
private bool RemoveObsoleteOrMissingSeasons(
|
||||||
|
@ -297,10 +300,13 @@ namespace MediaBrowser.Providers.TV
|
||||||
|
|
||||||
foreach (var seasonToRemove in seasonsToRemove)
|
foreach (var seasonToRemove in seasonsToRemove)
|
||||||
{
|
{
|
||||||
_libraryManager.DeleteItem(seasonToRemove, new DeleteOptions
|
_libraryManager.DeleteItem(
|
||||||
{
|
seasonToRemove,
|
||||||
DeleteFileLocation = true
|
new DeleteOptions
|
||||||
}, false);
|
{
|
||||||
|
DeleteFileLocation = true
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
}
|
}
|
||||||
|
@ -354,7 +360,10 @@ namespace MediaBrowser.Providers.TV
|
||||||
/// <param name="seasonCounts"></param>
|
/// <param name="seasonCounts"></param>
|
||||||
/// <param name="episodeTuple"></param>
|
/// <param name="episodeTuple"></param>
|
||||||
/// <returns>Episode.</returns>
|
/// <returns>Episode.</returns>
|
||||||
private Episode GetExistingEpisode(IEnumerable<Episode> existingEpisodes, IReadOnlyDictionary<int, int> seasonCounts, (int seasonNumber, int episodeNumber, DateTime firstAired) episodeTuple)
|
private Episode GetExistingEpisode(
|
||||||
|
IEnumerable<Episode> existingEpisodes,
|
||||||
|
IReadOnlyDictionary<int, int> seasonCounts,
|
||||||
|
(int seasonNumber, int episodeNumber, DateTime firstAired) episodeTuple)
|
||||||
{
|
{
|
||||||
var seasonNumber = episodeTuple.seasonNumber;
|
var seasonNumber = episodeTuple.seasonNumber;
|
||||||
var episodeNumber = episodeTuple.episodeNumber;
|
var episodeNumber = episodeTuple.episodeNumber;
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
|
||||||
using MediaBrowser.Controller.Providers;
|
|
||||||
using MediaBrowser.Model.Entities;
|
|
||||||
using MediaBrowser.Model.Providers;
|
|
||||||
using MediaBrowser.Providers.Plugins.TheTvdb;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.TV
|
|
||||||
{
|
|
||||||
public class Zap2ItExternalId : IExternalId
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ProviderName => "Zap2It";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => MetadataProvider.Zap2It.ToString();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ExternalIdMediaType? Type => null;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Supports(IHasProviderIds item) => item is Series;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TvdbExternalId : IExternalId
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ProviderName => "TheTVDB";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => MetadataProvider.Tvdb.ToString();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ExternalIdMediaType? Type => null;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Supports(IHasProviderIds item) => item is Series;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TvdbSeasonExternalId : IExternalId
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ProviderName => "TheTVDB";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => MetadataProvider.Tvdb.ToString();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Season;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string UrlFormatString => null;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Supports(IHasProviderIds item) => item is Season;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TvdbEpisodeExternalId : IExternalId
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ProviderName => "TheTVDB";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => MetadataProvider.Tvdb.ToString();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Episode;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Supports(IHasProviderIds item) => item is Episode;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
using MediaBrowser.Providers.Plugins.TheTvdb;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.TV
|
||||||
|
{
|
||||||
|
public class TvdbEpisodeExternalId : IExternalId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ProviderName => "TheTVDB";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => MetadataProvider.Tvdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Episode;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Supports(IHasProviderIds item) => item is Episode;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
using MediaBrowser.Providers.Plugins.TheTvdb;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.TV
|
||||||
|
{
|
||||||
|
public class TvdbExternalId : IExternalId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ProviderName => "TheTVDB";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => MetadataProvider.Tvdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => null;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Supports(IHasProviderIds item) => item is Series;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
using MediaBrowser.Providers.Plugins.TheTvdb;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.TV
|
||||||
|
{
|
||||||
|
public class TvdbSeasonExternalId : IExternalId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ProviderName => "TheTVDB";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => MetadataProvider.Tvdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Season;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string UrlFormatString => null;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Supports(IHasProviderIds item) => item is Season;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
using MediaBrowser.Providers.Plugins.TheTvdb;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Providers.TV
|
||||||
|
{
|
||||||
|
public class Zap2ItExternalId : IExternalId
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ProviderName => "Zap2It";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => MetadataProvider.Zap2It.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => null;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Supports(IHasProviderIds item) => item is Series;
|
||||||
|
}
|
||||||
|
}
|
|
@ -208,6 +208,5 @@ Global
|
||||||
{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||||
{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||||
{462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
{462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||||
{7C93C84F-105C-48E5-A878-406FA0A5B296} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
Loading…
Reference in New Issue