aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Directory.Packages.props2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json142
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs28
-rw-r--r--MediaBrowser.Providers/Books/Isbn/ISBNExternalId.cs23
-rw-r--r--MediaBrowser.Providers/Books/Isbn/ISBNExternalUrlProvider.cs25
-rw-r--r--MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalId.cs23
-rw-r--r--MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalUrlProvider.cs28
-rw-r--r--MediaBrowser.Providers/Plugins/ComicVine/ComicVinePersonExternalId.cs23
-rw-r--r--MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalId.cs23
-rw-r--r--MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalUrlProvider.cs25
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs2
-rw-r--r--src/Jellyfin.LiveTv/IO/EncodedRecorder.cs7
15 files changed, 277 insertions, 82 deletions
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 294cb45b1..09524549e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -13,7 +13,7 @@
<PackageVersion Include="BlurHashSharp.SkiaSharp" Version="1.4.0-pre.1" />
<PackageVersion Include="BlurHashSharp" Version="1.4.0-pre.1" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
- <PackageVersion Include="coverlet.collector" Version="8.0.0" />
+ <PackageVersion Include="coverlet.collector" Version="8.0.1" />
<PackageVersion Include="Diacritics" Version="4.1.4" />
<PackageVersion Include="DiscUtils.Udf" Version="0.16.13" />
<PackageVersion Include="DotNet.Glob" Version="3.1.3" />
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
index 1e885aad6..3ee1c757f 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
{
public class BookResolver : ItemResolver<Book>
{
- private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf" };
+ private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf", ".m4b", ".m4a", ".aac", ".flac", ".mp3", ".opus" };
protected override Book Resolve(ItemResolveArgs args)
{
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 534c64e93..dbbe2cbd0 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -16,14 +16,14 @@
"Folders": "Mappen",
"Genres": "Genres",
"HeaderAlbumArtists": "Albumartiesten",
- "HeaderContinueWatching": "Verderkijken",
+ "HeaderContinueWatching": "Verder kijken",
"HeaderFavoriteAlbums": "Favoriete albums",
"HeaderFavoriteArtists": "Favoriete artiesten",
"HeaderFavoriteEpisodes": "Favoriete afleveringen",
"HeaderFavoriteShows": "Favoriete series",
"HeaderFavoriteSongs": "Favoriete nummers",
"HeaderLiveTV": "Live-tv",
- "HeaderNextUp": "Als volgende",
+ "HeaderNextUp": "Volgende",
"HeaderRecordingGroups": "Opnamegroepen",
"HomeVideos": "Homevideo's",
"Inherit": "Erven",
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 23acd3c53..2393e21b1 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -76,7 +76,7 @@
"SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} till {1}",
"Sync": "Synk",
"System": "System",
- "TvShows": "TV-serier",
+ "TvShows": "Tv-serier",
"User": "Användare",
"UserCreatedWithName": "Användaren {0} har skapats",
"UserDeletedWithName": "Användaren {0} har tagits bort",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index 98dada84a..a42a33b1d 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -1,17 +1,17 @@
{
"Albums": "專輯",
- "AppDeviceValues": "程式:{0},設備:{1}",
+ "AppDeviceValues": "程式:{0},裝置:{1}",
"Application": "應用程式",
"Artists": "藝人",
- "AuthenticationSucceededWithUserName": "成功授權 {0}",
+ "AuthenticationSucceededWithUserName": "{0} 成功通過驗證",
"Books": "書籍",
- "CameraImageUploadedFrom": "{0} 成功上傳一張新照片",
+ "CameraImageUploadedFrom": "{0} 已經成功上傳咗一張新相",
"Channels": "頻道",
"ChapterNameValue": "第 {0} 章",
"Collections": "系列",
- "DeviceOfflineWithName": "{0} 已中斷連接",
- "DeviceOnlineWithName": "{0} 已連接",
- "FailedLoginAttemptWithUserName": "{0} 登入失敗",
+ "DeviceOfflineWithName": "{0} 斷開咗連接",
+ "DeviceOnlineWithName": "{0} 連接咗",
+ "FailedLoginAttemptWithUserName": "來自 {0} 嘅登入嘗試失敗咗",
"Favorites": "我的最愛",
"Folders": "資料夾",
"Genres": "風格",
@@ -27,15 +27,15 @@
"HeaderRecordingGroups": "錄製組",
"HomeVideos": "家庭影片",
"Inherit": "繼承",
- "ItemAddedWithName": "{0} 已被加入至媒體庫",
- "ItemRemovedWithName": "{0} 已從媒體庫移除",
+ "ItemAddedWithName": "{0} 經已加咗入媒體庫",
+ "ItemRemovedWithName": "{0} 經已由媒體庫移除咗",
"LabelIpAddressValue": "IP 地址:{0}",
- "LabelRunningTimeValue": "運作時間:{0}",
+ "LabelRunningTimeValue": "運行時間:{0}",
"Latest": "最新",
- "MessageApplicationUpdated": "Jellyfin 已被更新",
- "MessageApplicationUpdatedTo": "Jellyfin 已被更新至 {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 已被更新",
- "MessageServerConfigurationUpdated": "已更新伺服器設定",
+ "MessageApplicationUpdated": "Jellyfin 經已更新咗",
+ "MessageApplicationUpdatedTo": "Jellyfin 已經更新到 {0} 版本",
+ "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定「{0}」已經更新咗",
+ "MessageServerConfigurationUpdated": "伺服器設定經已更新咗",
"MixedContent": "混合內容",
"Movies": "電影",
"Music": "音樂",
@@ -43,99 +43,99 @@
"NameInstallFailed": "{0} 安裝失敗",
"NameSeasonNumber": "第 {0} 季",
"NameSeasonUnknown": "未知的季度",
- "NewVersionIsAvailable": "有新版本的 Jellyfin 可供下載。",
- "NotificationOptionApplicationUpdateAvailable": "有可用的更新",
- "NotificationOptionApplicationUpdateInstalled": "完成更新應用程式",
- "NotificationOptionAudioPlayback": "播放音訊",
- "NotificationOptionAudioPlaybackStopped": "停止播放音訊",
- "NotificationOptionCameraImageUploaded": "相片上傳",
+ "NewVersionIsAvailable": "有新版本嘅 Jellyfin 可以下載。",
+ "NotificationOptionApplicationUpdateAvailable": "有得更新應用程式",
+ "NotificationOptionApplicationUpdateInstalled": "應用程式更新好咗",
+ "NotificationOptionAudioPlayback": "開始播放音訊",
+ "NotificationOptionAudioPlaybackStopped": "停咗播放音訊",
+ "NotificationOptionCameraImageUploaded": "相機相片上傳咗",
"NotificationOptionInstallationFailed": "安裝失敗",
- "NotificationOptionNewLibraryContent": "新增媒體",
+ "NotificationOptionNewLibraryContent": "加咗新內容",
"NotificationOptionPluginError": "插件錯誤",
"NotificationOptionPluginInstalled": "安裝插件",
"NotificationOptionPluginUninstalled": "解除安裝插件",
- "NotificationOptionPluginUpdateInstalled": "完成更新插件",
- "NotificationOptionServerRestartRequired": "伺服器需要重啟",
- "NotificationOptionTaskFailed": "排程工作執行失敗",
- "NotificationOptionUserLockedOut": "封鎖用戶",
- "NotificationOptionVideoPlayback": "播放影片",
- "NotificationOptionVideoPlaybackStopped": "停止播放影片",
+ "NotificationOptionPluginUpdateInstalled": "插件更新好咗",
+ "NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
+ "NotificationOptionTaskFailed": "排程工作失敗",
+ "NotificationOptionUserLockedOut": "用戶被鎖定咗",
+ "NotificationOptionVideoPlayback": "開始播放影片",
+ "NotificationOptionVideoPlaybackStopped": "停咗播放影片",
"Photos": "相片",
"Playlists": "播放清單",
"Plugin": "插件",
- "PluginInstalledWithName": "已安裝 {0}",
- "PluginUninstalledWithName": "已移除 {0}",
- "PluginUpdatedWithName": "已更新 {0}",
+ "PluginInstalledWithName": "裝好咗 {0}",
+ "PluginUninstalledWithName": "剷走咗 {0}",
+ "PluginUpdatedWithName": "更新好咗 {0}",
"ProviderValue": "提供者:{0}",
"ScheduledTaskFailedWithName": "{0} 執行失敗",
"ScheduledTaskStartedWithName": "開始執行 {0}",
- "ServerNameNeedsToBeRestarted": "{0} 需要重啟",
+ "ServerNameNeedsToBeRestarted": "{0} 需要重新啟動",
"Shows": "節目",
"Songs": "歌曲",
- "StartupEmbyServerIsLoading": "正在載入 Jellyfin,請稍後再試。",
- "SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕",
+ "StartupEmbyServerIsLoading": "Jellyfin 伺服器載入緊,請稍後再試。",
+ "SubtitleDownloadFailureFromForItem": "經 {0} 下載 {1} 嘅字幕失敗咗",
"Sync": "同步",
"System": "系統",
"TvShows": "電視節目",
"User": "用戶",
- "UserCreatedWithName": "建立新用戶 {0}",
- "UserDeletedWithName": "用戶 {0} 已被移除",
- "UserDownloadingItemWithValues": "{0} 正在下載 {1}",
- "UserLockedOutWithName": "用戶 {0} 已被封鎖",
- "UserOfflineFromDevice": "{0} 終止了 {1} 的連接",
- "UserOnlineFromDevice": "{0} 從 {1} 連線",
- "UserPasswordChangedWithName": "{0} 的密碼已被更改",
- "UserPolicyUpdatedWithName": "{0} 嘅用戶權限已經更新咗",
- "UserStartedPlayingItemWithValues": "{0} 在 {2} 上播放 {1}",
- "UserStoppedPlayingItemWithValues": "{0} 停止在 {2} 上播放 {1}",
- "ValueHasBeenAddedToLibrary": "{0} 已被加入至你的媒體庫",
- "ValueSpecialEpisodeName": "特典 - {0}",
+ "UserCreatedWithName": "經已建立咗新用戶 {0}",
+ "UserDeletedWithName": "用戶 {0} 已經被刪除",
+ "UserDownloadingItemWithValues": "{0} 下載緊 {1}",
+ "UserLockedOutWithName": "用戶 {0} 經已被鎖定",
+ "UserOfflineFromDevice": "{0} 經已由 {1} 斷開咗連線",
+ "UserOnlineFromDevice": "{0} 正喺 {1} 連線",
+ "UserPasswordChangedWithName": "用戶 {0} 嘅密碼已經更改咗",
+ "UserPolicyUpdatedWithName": "用戶 {0} 嘅權限已經更新咗",
+ "UserStartedPlayingItemWithValues": "{0} 正喺 {2} 播緊 {1}",
+ "UserStoppedPlayingItemWithValues": "{0} 已經喺 {2} 停止播放 {1}",
+ "ValueHasBeenAddedToLibrary": "{0} 已經成功加入咗你嘅媒體庫",
+ "ValueSpecialEpisodeName": "特別篇 - {0}",
"VersionNumber": "版本 {0}",
- "TaskDownloadMissingSubtitles": "下載欠缺字幕",
+ "TaskDownloadMissingSubtitles": "下載漏咗嘅字幕",
"TaskUpdatePlugins": "更新插件",
"TasksApplicationCategory": "應用程式",
- "TaskRefreshLibraryDescription": "掃描媒體庫以加入新增的檔案及重新載入元數據。",
+ "TaskRefreshLibraryDescription": "掃描媒體庫嚟搵新檔案,同時重新載入元數據。",
"TasksMaintenanceCategory": "維護",
- "TaskDownloadMissingSubtitlesDescription": "根據元數據中的設定,在網上搜尋欠缺的字幕。",
- "TaskRefreshChannelsDescription": "重新載入網絡頻道的資訊。",
+ "TaskDownloadMissingSubtitlesDescription": "根據元數據設定,喺網上幫你搵返啲欠缺嘅字幕。",
+ "TaskRefreshChannelsDescription": "重新整理網上頻道嘅資訊。",
"TaskRefreshChannels": "重新載入頻道",
- "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。",
- "TaskCleanTranscode": "清理轉碼檔資料夾",
- "TaskUpdatePluginsDescription": "下載並更新能夠被自動更新的插件。",
- "TaskRefreshPeopleDescription": "更新你的媒體中有關的演員和導演的元數據。",
- "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔。",
- "TaskCleanLogs": "清理紀錄檔資料夾",
+ "TaskCleanTranscodeDescription": "自動刪除超過一日嘅轉碼檔案。",
+ "TaskCleanTranscode": "清理轉碼資料夾",
+ "TaskUpdatePluginsDescription": "自動幫嗰啲設咗要自動更新嘅插件進行下載同安裝。",
+ "TaskRefreshPeopleDescription": "更新媒體庫入面演員同導演嘅元數據。",
+ "TaskCleanLogsDescription": "自動刪除超過 {0} 日嘅紀錄檔。",
+ "TaskCleanLogs": "清理日誌資料夾",
"TaskRefreshLibrary": "掃描媒體庫",
- "TaskRefreshChapterImagesDescription": "幫有章節嘅影片整縮圖。",
- "TaskRefreshChapterImages": "提取章節圖像",
- "TaskCleanCacheDescription": "刪除系統不再需要的緩存文件。",
- "TaskCleanCache": "清理緩存資料夾",
+ "TaskRefreshChapterImagesDescription": "幫有章節嘅影片整返啲章節縮圖。",
+ "TaskRefreshChapterImages": "擷取章節圖片",
+ "TaskCleanCacheDescription": "刪除系統已經唔再需要嘅快取檔案。",
+ "TaskCleanCache": "清理快取(Cache)資料夾",
"TasksChannelsCategory": "網絡頻道",
"TasksLibraryCategory": "媒體庫",
"TaskRefreshPeople": "重新載入人物",
- "TaskCleanActivityLog": "清理活動記錄",
+ "TaskCleanActivityLog": "清理活動紀錄",
"Undefined": "未定義",
"Forced": "強制",
"Default": "預設",
"TaskOptimizeDatabaseDescription": "壓縮數據庫並釋放剩餘空間。喺掃描媒體庫或者做咗一啲會修改數據庫嘅操作之後行呢個任務,或者可以提升效能。",
"TaskOptimizeDatabase": "最佳化數據庫",
- "TaskCleanActivityLogDescription": "刪除早於設定時間的活動記錄。",
+ "TaskCleanActivityLogDescription": "刪除超過設定日期嘅活動記錄。",
"TaskKeyframeExtractorDescription": "提取關鍵影格(Keyframe)嚟建立更準確嘅 HLS 播放列表。呢個任務可能要行好耐。",
"TaskKeyframeExtractor": "關鍵影格提取器",
"External": "外部",
"HearingImpaired": "聽力障礙",
- "TaskRefreshTrickplayImages": "建立 Trickplay 圖像",
- "TaskRefreshTrickplayImagesDescription": "幫已啟用功能嘅媒體庫影片整快轉預覽圖。",
+ "TaskRefreshTrickplayImages": "產生搜畫預覽圖",
+ "TaskRefreshTrickplayImagesDescription": "爲已啟用功能嘅媒體庫影片製作快轉預覽圖。",
"TaskExtractMediaSegments": "掃描媒體分段資訊",
- "TaskExtractMediaSegmentsDescription": "從允許MediaSegment 功能的插件中獲取媒體片段。",
- "TaskDownloadMissingLyrics": "下載欠缺歌詞",
- "TaskDownloadMissingLyricsDescription": "下載歌詞",
- "TaskCleanCollectionsAndPlaylists": "整理媒體與播放清單",
+ "TaskExtractMediaSegmentsDescription": "從支援 MediaSegment 功能嘅插件入面提取媒體片段。",
+ "TaskDownloadMissingLyrics": "下載缺失嘅歌詞",
+ "TaskDownloadMissingLyricsDescription": "幫啲歌下載歌詞",
+ "TaskCleanCollectionsAndPlaylists": "清理媒體系列(Collections)同埋播放清單",
"TaskAudioNormalization": "音訊同等化",
- "TaskAudioNormalizationDescription": "掃描檔案裏的音訊同等化資料。",
- "TaskCleanCollectionsAndPlaylistsDescription": "從資料庫及播放清單中移除已不存在的項目。",
- "TaskMoveTrickplayImagesDescription": "根據媒體庫設定移動現有的 Trickplay 檔案。",
- "TaskMoveTrickplayImages": "轉移 Trickplay 影像位置",
- "CleanupUserDataTask": "用戶資料清理工作",
+ "TaskAudioNormalizationDescription": "掃描檔案入面嘅音訊標准化(Audio Normalization)數據。",
+ "TaskCleanCollectionsAndPlaylistsDescription": "自動清理資料庫同播放清單入面已經唔存在嘅項目。",
+ "TaskMoveTrickplayImagesDescription": "根據媒體庫設定,將現有嘅 Trickplay(快轉預覽)檔案搬去對應位置。",
+ "TaskMoveTrickplayImages": "搬移快轉預覽圖嘅位置",
+ "CleanupUserDataTask": "清理用戶資料嘅任務",
"CleanupUserDataTaskDescription": "從用戶數據入面清除嗰啲已經被刪除咗超過 90 日嘅媒體相關資料。"
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index c7b11f47d..9ebaef171 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -85,6 +85,7 @@ namespace MediaBrowser.Controller.MediaEncoding
private readonly Version _minFFmpegVaapiDeviceVendorId = new Version(7, 0, 1);
private readonly Version _minFFmpegQsvVppScaleModeOption = new Version(6, 0);
private readonly Version _minFFmpegRkmppHevcDecDoviRpu = new Version(7, 1, 1);
+ private readonly Version _minFFmpegReadrateCatchupOption = new Version(8, 0);
private static readonly Regex _containerValidationRegex = new(ContainerValidationRegex, RegexOptions.Compiled);
@@ -1566,14 +1567,15 @@ namespace MediaBrowser.Controller.MediaEncoding
int bitrate = state.OutputVideoBitrate.Value;
- // Bit rate under 1000k is not allowed in h264_qsv
+ // Bit rate under 1000k is not allowed in h264_qsv.
if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
bitrate = Math.Max(bitrate, 1000);
}
- // Currently use the same buffer size for all encoders
- int bufsize = bitrate * 2;
+ // Currently use the same buffer size for all non-QSV encoders.
+ // Use long arithmetic to prevent int32 overflow for very high bitrate values.
+ int bufsize = (int)Math.Min((long)bitrate * 2, int.MaxValue);
if (string.Equals(videoCodec, "libsvtav1", StringComparison.OrdinalIgnoreCase))
{
@@ -1603,7 +1605,13 @@ namespace MediaBrowser.Controller.MediaEncoding
// Set (maxrate == bitrate + 1) to trigger VBR for better bitrate allocation
// Set (rc_init_occupancy == 2 * bitrate) and (bufsize == 4 * bitrate) to deal with drastic scene changes
- return FormattableString.Invariant($"{mbbrcOpt} -b:v {bitrate} -maxrate {bitrate + 1} -rc_init_occupancy {bitrate * 2} -bufsize {bitrate * 4}");
+ // Use long arithmetic and clamp to int.MaxValue to prevent int32 overflow
+ // (e.g. bitrate * 4 wraps to a negative value for bitrates above ~537 million)
+ int qsvMaxrate = (int)Math.Min((long)bitrate + 1, int.MaxValue);
+ int qsvInitOcc = (int)Math.Min((long)bitrate * 2, int.MaxValue);
+ int qsvBufsize = (int)Math.Min((long)bitrate * 4, int.MaxValue);
+
+ return FormattableString.Invariant($"{mbbrcOpt} -b:v {bitrate} -maxrate {qsvMaxrate} -rc_init_occupancy {qsvInitOcc} -bufsize {qsvBufsize}");
}
if (string.Equals(videoCodec, "h264_amf", StringComparison.OrdinalIgnoreCase)
@@ -7226,8 +7234,10 @@ namespace MediaBrowser.Controller.MediaEncoding
inputModifier += GetVideoSyncOption(state.InputVideoSync, _mediaEncoder.EncoderVersion);
}
+ int readrate = 0;
if (state.ReadInputAtNativeFramerate && state.InputProtocol != MediaProtocol.Rtsp)
{
+ readrate = 1;
inputModifier += " -re";
}
else if (encodingOptions.EnableSegmentDeletion
@@ -7238,7 +7248,15 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// Set an input read rate limit 10x for using SegmentDeletion with stream-copy
// to prevent ffmpeg from exiting prematurely (due to fast drive)
- inputModifier += " -readrate 10";
+ readrate = 10;
+ inputModifier += $" -readrate {readrate}";
+ }
+
+ // Set a larger catchup value to revert to the old behavior,
+ // otherwise, remuxing might stall due to this new option
+ if (readrate > 0 && _mediaEncoder.EncoderVersion >= _minFFmpegReadrateCatchupOption)
+ {
+ inputModifier += $" -readrate_catchup {readrate * 100}";
}
var flags = new List<string>();
diff --git a/MediaBrowser.Providers/Books/Isbn/ISBNExternalId.cs b/MediaBrowser.Providers/Books/Isbn/ISBNExternalId.cs
new file mode 100644
index 000000000..a86275d5a
--- /dev/null
+++ b/MediaBrowser.Providers/Books/Isbn/ISBNExternalId.cs
@@ -0,0 +1,23 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+
+namespace MediaBrowser.Providers.Books.Isbn
+{
+ /// <inheritdoc />
+ public class IsbnExternalId : IExternalId
+ {
+ /// <inheritdoc />
+ public string ProviderName => "ISBN";
+
+ /// <inheritdoc />
+ public string Key => "ISBN";
+
+ /// <inheritdoc />
+ public ExternalIdMediaType? Type => null;
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is Book;
+ }
+}
diff --git a/MediaBrowser.Providers/Books/Isbn/ISBNExternalUrlProvider.cs b/MediaBrowser.Providers/Books/Isbn/ISBNExternalUrlProvider.cs
new file mode 100644
index 000000000..9d7b1ff20
--- /dev/null
+++ b/MediaBrowser.Providers/Books/Isbn/ISBNExternalUrlProvider.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Providers.Books.Isbn;
+
+/// <inheritdoc/>
+public class IsbnExternalUrlProvider : IExternalUrlProvider
+{
+ /// <inheritdoc/>
+ public string Name => "ISBN";
+
+ /// <inheritdoc />
+ public IEnumerable<string> GetExternalUrls(BaseItem item)
+ {
+ if (item.TryGetProviderId("ISBN", out var externalId))
+ {
+ if (item is Book)
+ {
+ yield return $"https://search.worldcat.org/search?q=bn:{externalId}";
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalId.cs b/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalId.cs
new file mode 100644
index 000000000..8cbd1f89a
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalId.cs
@@ -0,0 +1,23 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+
+namespace MediaBrowser.Providers.Plugins.ComicVine
+{
+ /// <inheritdoc />
+ public class ComicVineExternalId : IExternalId
+ {
+ /// <inheritdoc />
+ public string ProviderName => "Comic Vine";
+
+ /// <inheritdoc />
+ public string Key => "ComicVine";
+
+ /// <inheritdoc />
+ public ExternalIdMediaType? Type => null;
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is Book;
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalUrlProvider.cs
new file mode 100644
index 000000000..912239917
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/ComicVine/ComicVineExternalUrlProvider.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Providers.Plugins.ComicVine;
+
+/// <inheritdoc/>
+public class ComicVineExternalUrlProvider : IExternalUrlProvider
+{
+ /// <inheritdoc/>
+ public string Name => "Comic Vine";
+
+ /// <inheritdoc />
+ public IEnumerable<string> GetExternalUrls(BaseItem item)
+ {
+ if (item.TryGetProviderId("ComicVine", out var externalId))
+ {
+ switch (item)
+ {
+ case Person:
+ case Book:
+ yield return $"https://comicvine.gamespot.com/{externalId}";
+ break;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/ComicVine/ComicVinePersonExternalId.cs b/MediaBrowser.Providers/Plugins/ComicVine/ComicVinePersonExternalId.cs
new file mode 100644
index 000000000..26b8e1138
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/ComicVine/ComicVinePersonExternalId.cs
@@ -0,0 +1,23 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+
+namespace MediaBrowser.Providers.Plugins.ComicVine
+{
+ /// <inheritdoc />
+ public class ComicVinePersonExternalId : IExternalId
+ {
+ /// <inheritdoc />
+ public string ProviderName => "Comic Vine";
+
+ /// <inheritdoc />
+ public string Key => "ComicVine";
+
+ /// <inheritdoc />
+ public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is Person;
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalId.cs b/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalId.cs
new file mode 100644
index 000000000..02d3b3697
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalId.cs
@@ -0,0 +1,23 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+
+namespace MediaBrowser.Providers.Plugins.GoogleBooks
+{
+ /// <inheritdoc />
+ public class GoogleBooksExternalId : IExternalId
+ {
+ /// <inheritdoc />
+ public string ProviderName => "Google Books";
+
+ /// <inheritdoc />
+ public string Key => "GoogleBooks";
+
+ /// <inheritdoc />
+ public ExternalIdMediaType? Type => null;
+
+ /// <inheritdoc />
+ public bool Supports(IHasProviderIds item) => item is Book;
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalUrlProvider.cs
new file mode 100644
index 000000000..95047ee83
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/GoogleBooks/GoogleBooksExternalUrlProvider.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Providers.Plugins.GoogleBooks;
+
+/// <inheritdoc/>
+public class GoogleBooksExternalUrlProvider : IExternalUrlProvider
+{
+ /// <inheritdoc />
+ public string Name => "Google Books";
+
+ /// <inheritdoc />
+ public IEnumerable<string> GetExternalUrls(BaseItem item)
+ {
+ if (item.TryGetProviderId("GoogleBooks", out var externalId))
+ {
+ if (item is Book)
+ {
+ yield return $"https://books.google.com/books?id={externalId}";
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index 0217bded1..0757155aa 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -547,7 +547,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("aspectratio", hasAspectRatio.AspectRatio);
}
- if (item.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbCollection))
+ if (item.TryGetProviderId(MetadataProvider.TmdbCollection, out var tmdbCollection))
{
writer.WriteElementString("collectionnumber", tmdbCollection);
writtenProviderIds.Add(MetadataProvider.TmdbCollection.ToString());
diff --git a/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs b/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs
index be7ff5297..d877a0d12 100644
--- a/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs
+++ b/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs
@@ -156,6 +156,13 @@ namespace Jellyfin.LiveTv.IO
if (mediaSource.ReadAtNativeFramerate)
{
inputModifier += " -re";
+
+ // Set a larger catchup value to revert to the old behavior,
+ // otherwise, remuxing might stall due to this new option
+ if (_mediaEncoder.EncoderVersion >= new Version(8, 0))
+ {
+ inputModifier += " -readrate_catchup 100";
+ }
}
if (mediaSource.RequiresLooping)