diff options
28 files changed, 235 insertions, 118 deletions
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 6c95ad4ce..46c478b08 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -34,7 +34,7 @@ jobs: displayName: "Check out web" condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: - script: 'git clone --single-branch --branch $(Build.SourceBranch) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' + script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' - task: CmdLine@2 displayName: "Check out web (PR)" @@ -203,7 +203,7 @@ jobs: displayName: "Check out web" condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: - script: 'git clone --single-branch --branch $(Build.SourceBranch) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' + script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' - task: CmdLine@2 displayName: "Check out web (PR)" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 967be0fb7..dc93d2c84 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,6 @@ <!-- Ensure your title is short, descriptive, and in the imperative mood (Fix X, Change Y, instead of Fixed X, Changed Y). -For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our https://jellyfin.readthedocs.io/en/latest/developer-docs/contributing/ page. +For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our documentation. --> **Changes** diff --git a/.github/stale.yml b/.github/stale.yml index ce9fb01a1..9ea0e3796 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -17,6 +17,6 @@ staleLabel: stale markComment: > Issues go stale after 90d of inactivity. Mark the issue as fresh by adding a comment or commit. Stale issues close after an additional 14d of inactivity. If this issue is safe to close now please do so. - If you have any questions you can reach us on [Matrix or Social Media](https://jellyfin.readthedocs.io/en/latest/getting-help/). + If you have any questions you can reach us on [Matrix or Social Media](https://docs.jellyfin.org/general/getting-help.html). # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false diff --git a/Dockerfile b/Dockerfile index 69cb5e0dd..a45576868 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,8 +17,14 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg + FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION} -# libfontconfig1 is required for Skia +COPY --from=ffmpeg / / +COPY --from=builder /jellyfin /jellyfin +COPY --from=web-builder /dist /jellyfin/jellyfin-web +# Install dependencies: +# libfontconfig1: needed for Skia +# mesa-va-drivers: needed for VAAPI RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ libfontconfig1 mesa-va-drivers \ @@ -27,9 +33,6 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media -COPY --from=ffmpeg / / -COPY --from=builder /jellyfin /jellyfin -COPY --from=web-builder /dist /jellyfin/jellyfin-web EXPOSE 8096 VOLUME /cache /config /media diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 1d0293a5f..f36d465dd 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -615,11 +615,34 @@ namespace Emby.Server.Implementations var host = new WebHostBuilder() .UseKestrel(options => { - options.ListenAnyIP(HttpPort); + var addresses = ServerConfigurationManager + .Configuration + .LocalNetworkAddresses + .Select(NormalizeConfiguredLocalAddress) + .Where(i => i != null) + .ToList(); + if (addresses.Any()) + { + foreach (var address in addresses) + { + Logger.LogInformation("Kestrel listening on {ipaddr}", address); + options.Listen(address, HttpPort); - if (EnableHttps && Certificate != null) + if (EnableHttps && Certificate != null) + { + options.Listen(address, HttpsPort, listenOptions => listenOptions.UseHttps(Certificate)); + } + } + } + else { - options.ListenAnyIP(HttpsPort, listenOptions => listenOptions.UseHttps(Certificate)); + Logger.LogInformation("Kestrel listening on all interfaces"); + options.ListenAnyIP(HttpPort); + + if (EnableHttps && Certificate != null) + { + options.ListenAnyIP(HttpsPort, listenOptions => listenOptions.UseHttps(Certificate)); + } } }) .UseContentRoot(contentRoot) @@ -640,7 +663,15 @@ namespace Emby.Server.Implementations }) .Build(); - await host.StartAsync().ConfigureAwait(false); + try + { + await host.StartAsync().ConfigureAwait(false); + } + catch + { + Logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in system.xml and try again."); + throw; + } } private async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next) diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index a5bb47afb..4e392f6c9 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -53,10 +53,10 @@ namespace Emby.Server.Implementations.Data protected virtual int? CacheSize => null; /// <summary> - /// Gets the journal mode. + /// Gets the journal mode. <see href="https://www.sqlite.org/pragma.html#pragma_journal_mode" /> /// </summary> /// <value>The journal mode.</value> - protected virtual string JournalMode => "WAL"; + protected virtual string JournalMode => "TRUNCATE"; /// <summary> /// Gets the page size. diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2f083dda4..96f90b831 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2724,7 +2724,7 @@ namespace Emby.Server.Implementations.Data if (elapsed >= SlowThreshold) { - Logger.LogWarning( + Logger.LogDebug( "{Method} query time (slow): {ElapsedMs}ms. Query: {Query}", methodName, elapsed, diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index a3201f0bc..6c0e32e05 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1099,7 +1099,9 @@ namespace Emby.Server.Implementations.Dto Series episodeSeries = null; - if (options.ContainsField(ItemFields.SeriesPrimaryImage)) + // this block will add the series poster for episodes without a poster + // TODO maybe remove the if statement entirely + //if (options.ContainsField(ItemFields.SeriesPrimaryImage)) { episodeSeries = episodeSeries ?? episode.Series; if (episodeSeries != null) @@ -1143,7 +1145,9 @@ namespace Emby.Server.Implementations.Dto } } - if (options.ContainsField(ItemFields.SeriesPrimaryImage)) + // this block will add the series poster for seasons without a poster + // TODO maybe remove the if statement entirely + //if (options.ContainsField(ItemFields.SeriesPrimaryImage)) { series = series ?? season.Series; if (series != null) diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index eb145b4fe..4da3cdd3b 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -1,22 +1,22 @@ { - "Albums": "الألبومات", - "AppDeviceValues": "التطبيق: {0}. الجهاز: {1}.", + "Albums": "ألبومات", + "AppDeviceValues": "تطبيق: {0}, جهاز: {1}", "Application": "التطبيق", - "Artists": "الفنانون", - "AuthenticationSucceededWithUserName": "تم التأكد من {0} بنجاح", - "Books": "الكتب", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", + "Artists": "الفنان", + "AuthenticationSucceededWithUserName": "{0} سجل الدخول بنجاح", + "Books": "كتب", + "CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}", "Channels": "القنوات", "ChapterNameValue": "الباب {0}", - "Collections": "المجاميع", + "Collections": "مجموعات", "DeviceOfflineWithName": "تم قطع الاتصال بـ{0}", "DeviceOnlineWithName": "{0} متصل", "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}", - "Favorites": "المفضلات", + "Favorites": "التفضيلات", "Folders": "المجلدات", "Genres": "أنواع الأفلام", - "HeaderAlbumArtists": "فنانو الألبومات", - "HeaderCameraUploads": "Camera Uploads", + "HeaderAlbumArtists": "فناني الألبومات", + "HeaderCameraUploads": "تحميلات الكاميرا", "HeaderContinueWatching": "استئناف المشاهدة", "HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderFavoriteArtists": "الفنانون المفضلون", @@ -24,7 +24,7 @@ "HeaderFavoriteShows": "المسلسلات المفضلة", "HeaderFavoriteSongs": "الأغاني المفضلة", "HeaderLiveTV": "التلفاز المباشر", - "HeaderNextUp": "التشغيل التالي", + "HeaderNextUp": "التالي", "HeaderRecordingGroups": "مجموعات التسجيل", "HomeVideos": "الفيديوهات المنزلية", "Inherit": "توريث", @@ -34,29 +34,29 @@ "LabelRunningTimeValue": "وقت التشغيل: {0}", "Latest": "الأحدث", "MessageApplicationUpdated": "لقد تم تحديث خادم أمبي", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", + "MessageApplicationUpdatedTo": "تم تحديث سيرفر Jellyfin الى {0}", "MessageNamedServerConfigurationUpdatedWithValue": "تم تحديث إعدادات الخادم في قسم {0}", "MessageServerConfigurationUpdated": "تم تحديث إعدادات الخادم", "MixedContent": "محتوى مخلوط", "Movies": "الأفلام", "Music": "الموسيقى", "MusicVideos": "الفيديوهات الموسيقية", - "NameInstallFailed": "{0} installation failed", + "NameInstallFailed": "فشل التثبيت {0}", "NameSeasonNumber": "الموسم {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "NameSeasonUnknown": "الموسم غير معروف", + "NewVersionIsAvailable": "نسخة حديثة من سيرفر Jellyfin متوفرة للتحميل .", "NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق", "NotificationOptionApplicationUpdateInstalled": "تم تحديث التطبيق", "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي", "NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي", "NotificationOptionCameraImageUploaded": "تم رقع صورة الكاميرا", - "NotificationOptionInstallationFailed": "عملية التنصيب فشلت", + "NotificationOptionInstallationFailed": "فشل في التثبيت", "NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد", "NotificationOptionPluginError": "فشل في الملحق", "NotificationOptionPluginInstalled": "تم تثبيت الملحق", "NotificationOptionPluginUninstalled": "تمت إزالة الملحق", - "NotificationOptionPluginUpdateInstalled": "تم تحديث الملحق", - "NotificationOptionServerRestartRequired": "يجب إعادة تشغيل الخادم", + "NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق", + "NotificationOptionServerRestartRequired": "يجب إعادة تشغيل السيرفر", "NotificationOptionTaskFailed": "فشل في المهمة المجدولة", "NotificationOptionUserLockedOut": "تم إقفال حساب المستخدم", "NotificationOptionVideoPlayback": "بدأ تشغيل الفيديو", @@ -70,28 +70,28 @@ "ProviderValue": "المزود: {0}", "ScheduledTaskFailedWithName": "العملية {0} فشلت", "ScheduledTaskStartedWithName": "تم بدء {0}", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", - "Shows": "Shows", + "ServerNameNeedsToBeRestarted": "يحتاج لإعادة تشغيله {0}", + "Shows": "الحلقات", "Songs": "الأغاني", - "StartupEmbyServerIsLoading": "خادم أمبي قيد التحميل. الرجاء المحاوية بعد حين", + "StartupEmbyServerIsLoading": "سيرفر Jellyfin قيد التشغيل . الرجاء المحاولة بعد قليل.", "SubtitleDownloadFailureForItem": "عملية إنزال الترجمة فشلت لـ{0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "تم إنزال الترجمات لـ {0}", + "SubtitleDownloadFailureFromForItem": "الترجمات فشلت في التحميل من {0} لـ {1}", + "SubtitlesDownloadedForItem": "تم تحميل الترجمات لـ {0}", "Sync": "مزامنة", "System": "النظام", - "TvShows": "TV Shows", + "TvShows": "البرامج التلفزيونية", "User": "المستخدم", "UserCreatedWithName": "تم إنشاء المستخدم {0}", "UserDeletedWithName": "تم حذف المستخدم {0}", "UserDownloadingItemWithValues": "{0} يقوم بإنزال {1}", "UserLockedOutWithName": "المستخدم {0} تم منعه من الدخول", "UserOfflineFromDevice": "تم قطع اتصال {0} من {1}", - "UserOnlineFromDevice": "{0} متصلة عبر {1}", + "UserOnlineFromDevice": "{0} متصل عبر {1}", "UserPasswordChangedWithName": "تم تغيير كلمة السر للمستخدم {0}", - "UserPolicyUpdatedWithName": "User policy has been updated for {0}", - "UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1}", - "UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", - "ValueSpecialEpisodeName": "خاص - {0}", + "UserPolicyUpdatedWithName": "سياسة المستخدمين تم تحديثها لـ {0}", + "UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}", + "UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}", + "ValueHasBeenAddedToLibrary": "{0} تم اضافتها الى مكتبة الوسائط", + "ValueSpecialEpisodeName": "مميز - {0}", "VersionNumber": "الإصدار رقم {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index 003632968..99fda7aa6 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -15,7 +15,7 @@ "Favorites": "Favoritos", "Folders": "Carpetas", "Genres": "Géneros", - "HeaderAlbumArtists": "Artistas del Álbum", + "HeaderAlbumArtists": "Artistas del álbum", "HeaderCameraUploads": "Subidos desde Camara", "HeaderContinueWatching": "Continuar Viendo", "HeaderFavoriteAlbums": "Álbumes favoritos", diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json new file mode 100644 index 000000000..15aa0a8ee --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -0,0 +1,48 @@ +{ + "HeaderLiveTV": "Netti-TV", + "NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa", + "NameSeasonUnknown": "Tuntematon Kausi", + "NameSeasonNumber": "Kausi {0}", + "NameInstallFailed": "{0} asennus epäonnistui", + "MusicVideos": "Musiikkivideot", + "Music": "Musiikki", + "Movies": "Elokuvat", + "MixedContent": "Sekoitettu sisältö", + "MessageServerConfigurationUpdated": "Palvelimen konfiguraatio on päivitetty", + "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen konfiguraatio-osa {0} on päivitetty", + "MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty {0}", + "MessageApplicationUpdated": "Jellyfin palvelin on päivitetty", + "Latest": "Viimeisin", + "LabelRunningTimeValue": "Kesto: {0}", + "LabelIpAddressValue": "IP-osoite: {0}", + "ItemRemovedWithName": "{0} poistettiin kirjastosta", + "ItemAddedWithName": "{0} lisättiin kirjastoon", + "Inherit": "Periä", + "HomeVideos": "Kotivideot", + "HeaderRecordingGroups": "Äänitysryhmä", + "HeaderNextUp": "Seuraavaksi", + "HeaderFavoriteSongs": "Lempikappaleet", + "HeaderFavoriteShows": "Lempisarjat", + "HeaderFavoriteEpisodes": "Lempijaksot", + "HeaderCameraUploads": "Kamerasta Ladatut", + "HeaderFavoriteArtists": "Lempiartistit", + "HeaderFavoriteAlbums": "Lempialbumit", + "HeaderContinueWatching": "Jatka Katsomista", + "HeaderAlbumArtists": "Albumiartistit", + "Genres": "Genret", + "Folders": "Kansiot", + "Favorites": "Suosikit", + "FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys kohteesta {0}", + "DeviceOnlineWithName": "{0} on yhdistynyt", + "DeviceOfflineWithName": "{0} on katkaissut yhteytensä", + "Collections": "Kokoelmat", + "ChapterNameValue": "Luku: {0}", + "Channels": "Kanavat", + "CameraImageUploadedFrom": "Uusi kamerakuva on lähetetty kohteesta {0}", + "Books": "Kirjat", + "AuthenticationSucceededWithUserName": "{0} todennettu onnistuneesti", + "Artists": "Artistit", + "Application": "Sovellus", + "AppDeviceValues": "Sovellus: {0}, Laite: {1}", + "Albums": "Albumit" +} diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index f2b7c408c..9bf4d2797 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -4,7 +4,7 @@ "Application": "애플리케이션", "Artists": "아티스트", "AuthenticationSucceededWithUserName": "{0} 인증에 성공했습니다.", - "Books": "책", + "Books": "도서", "CameraImageUploadedFrom": "새로운 카메라 이미지가 {0}에서 업로드되었습니다.", "Channels": "채널", "ChapterNameValue": "챕터 {0}", @@ -83,8 +83,8 @@ "User": "사용자", "UserCreatedWithName": "사용자 {0} 생성됨", "UserDeletedWithName": "사용자 {0} 삭제됨", - "UserDownloadingItemWithValues": "{0} is downloading {1}", - "UserLockedOutWithName": "User {0} has been locked out", + "UserDownloadingItemWithValues": "{0}이(가) {1}을 다운로드 중입니다", + "UserLockedOutWithName": "유저 {0} 은(는) 잠금처리 되었습니다", "UserOfflineFromDevice": "{0} has disconnected from {1}", "UserOnlineFromDevice": "{0} is online from {1}", "UserPasswordChangedWithName": "Password has been changed for user {0}", diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index 531dfe51f..c141a40f6 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -3,7 +3,7 @@ "AppDeviceValues": "Aplikacija: {0}, Naprava: {1}", "Application": "Aplikacija", "Artists": "Izvajalci", - "AuthenticationSucceededWithUserName": "{0} preverjanje uspešno", + "AuthenticationSucceededWithUserName": "{0} preverjanje pristnosti uspešno", "Books": "Knjige", "CameraImageUploadedFrom": "Nova fotografija je bila naložena z {0}", "Channels": "Kanali", @@ -44,13 +44,13 @@ "NameInstallFailed": "{0} namestitev neuspešna", "NameSeasonNumber": "Sezona {0}", "NameSeasonUnknown": "Season neznana", - "NewVersionIsAvailable": "Nova razničica Jellyfin strežnika je na voljo za prenos.", + "NewVersionIsAvailable": "Nova različica Jellyfin strežnika je na voljo za prenos.", "NotificationOptionApplicationUpdateAvailable": "Posodobitev aplikacije je na voljo", "NotificationOptionApplicationUpdateInstalled": "Posodobitev aplikacije je bila nameščena", "NotificationOptionAudioPlayback": "Predvajanje zvoka začeto", "NotificationOptionAudioPlaybackStopped": "Predvajanje zvoka zaustavljeno", "NotificationOptionCameraImageUploaded": "Posnetek kamere naložen", - "NotificationOptionInstallationFailed": "Napaka pri nameščanju", + "NotificationOptionInstallationFailed": "Namestitev neuspešna", "NotificationOptionNewLibraryContent": "Nove vsebine dodane", "NotificationOptionPluginError": "Napaka dodatka", "NotificationOptionPluginInstalled": "Dodatek nameščen", @@ -92,6 +92,6 @@ "UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}", "UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}", "ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici", - "ValueSpecialEpisodeName": "Special - {0}", - "VersionNumber": "Version {0}" + "ValueSpecialEpisodeName": "Poseben - {0}", + "VersionNumber": "Različica {0}" } diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index a223a4fe3..7dca7e814 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -610,9 +610,8 @@ namespace MediaBrowser.Api process.Kill(); } } - catch (Exception ex) + catch (InvalidOperationException) { - Logger.LogError(ex, "Error killing transcoding job for {Path}", job.Path); } } } diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 3a15c3776..b05e8c949 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -24,6 +25,7 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; using Microsoft.Net.Http.Headers; +using static MediaBrowser.Common.HexHelper; namespace MediaBrowser.Api.LiveTv { @@ -599,6 +601,7 @@ namespace MediaBrowser.Api.LiveTv { public bool ValidateLogin { get; set; } public bool ValidateListings { get; set; } + public string Pw { get; set; } } [Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")] @@ -866,10 +869,30 @@ namespace MediaBrowser.Api.LiveTv public async Task<object> Post(AddListingProvider request) { + if (request.Pw != null) + { + request.Password = GetHashedString(request.Pw); + } + + request.Pw = null; + var result = await _liveTvManager.SaveListingProvider(request, request.ValidateLogin, request.ValidateListings).ConfigureAwait(false); return ToOptimizedResult(result); } + /// <summary> + /// Gets the hashed string. + /// </summary> + private string GetHashedString(string str) + { + // SchedulesDirect requires a SHA1 hash of the user's password + // https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#obtain-a-token + using (SHA1 sha = SHA1.Create()) { + return ToHexString( + sha.ComputeHash(Encoding.UTF8.GetBytes(str))); + } + } + public void Delete(DeleteListingProvider request) { _liveTvManager.DeleteListingsProvider(request.Id); diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 8fa6c3dac..f5f753684 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -965,6 +965,14 @@ namespace MediaBrowser.Api.Playback.Hls var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request); + var timeDeltaParam = string.Empty; + + if (isEncoding && state.TargetFramerate > 0) + { + float startTime = 1 / (state.TargetFramerate.Value * 2); + timeDeltaParam = string.Format(CultureInfo.InvariantCulture, "-segment_time_delta {0:F3}", startTime); + } + var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.'); if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase)) { @@ -972,7 +980,7 @@ namespace MediaBrowser.Api.Playback.Hls } return string.Format( - "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f hls -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"", + "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format {11} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", inputModifier, EncodingHelper.GetInputArgument(state, encodingOptions), threads, @@ -980,10 +988,11 @@ namespace MediaBrowser.Api.Playback.Hls GetVideoArguments(state, encodingOptions), GetAudioArguments(state, encodingOptions), state.SegmentLength.ToString(CultureInfo.InvariantCulture), - segmentFormat, startNumberParam, + outputPath, outputTsArg, - outputPath + timeDeltaParam, + segmentFormat ).Trim(); } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index eb3d2ab81..991fc0b00 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -83,9 +83,6 @@ namespace MediaBrowser.Controller.MediaEncoding } } - // Avoid performing a second attempt when the first one - // hasn't tried hardware encoding anyway. - encodingOptions.EnableHardwareEncoding = false; return defaultEncoder; } @@ -2548,7 +2545,10 @@ namespace MediaBrowser.Controller.MediaEncoding { if (Environment.OSVersion.Platform == PlatformID.Win32NT) { - return "-hwaccel dxva2"; + if(Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1)) + return "-hwaccel d3d11va"; + else + return "-hwaccel dxva2"; } switch (videoStream.Codec.ToLowerInvariant()) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index da0b7693e..3620abfee 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -118,15 +118,18 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.LogInformation("Found ffmpeg version {0}", version != null ? version.ToString() : "unknown"); - if (version == null && MinVersion != null && MaxVersion != null) // Version is unknown + if (version == null) { - if (MinVersion == MaxVersion) + if (MinVersion != null && MaxVersion != null) // Version is unknown { - _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", MinVersion); - } - else - { - _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", MinVersion, MaxVersion); + if (MinVersion == MaxVersion) + { + _logger.LogWarning("FFmpeg validation: We recommend ffmpeg version {0}", MinVersion); + } + else + { + _logger.LogWarning("FFmpeg validation: We recommend a minimum of {0} and maximum of {1}", MinVersion, MaxVersion); + } } return false; @@ -161,7 +164,7 @@ namespace MediaBrowser.MediaEncoding.Encoder internal static Version GetFFmpegVersion(string output) { // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output - var match = Regex.Match(output, @"ffmpeg version ((?:\d+\.?)+)"); + var match = Regex.Match(output, @"^ffmpeg version n?((?:\d+\.?)+)"); if (match.Success) { @@ -23,17 +23,17 @@ Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET Core framework to enable full cross-platform support. There are no strings attached, no premium licenses or features, and no hidden agendas: just a team who want to build something better and work together to achieve it. We welcome anyone who is interested in joining us in our quest! -For further details, please see [our documentation page](https://jellyfin.readthedocs.io). To receive the latest updates, get help with Jellyfin, and join the community, please visit [one of our communication channels on Matrix/Riot or social media](https://jellyfin.readthedocs.io/en/latest/getting-help/). +For further details, please see [our documentation page](https://docs.jellyfin.org/). To receive the latest updates, get help with Jellyfin, and join the community, please visit [one of our communication channels on Matrix/Riot or social media](https://docs.jellyfin.org/general/getting-help.html). -For more information about the project, please see our [about page](https://jellyfin.readthedocs.io/en/latest/about/). +For more information about the project, please see our [about page](https://docs.jellyfin.org/general/about.html). <p align="center"> <strong>Want to get started?</strong> -<em>Choose from <a href="https://jellyfin.readthedocs.io/en/latest/administrator-docs/installing/">Prebuilt Packages</a> or <a href="https://jellyfin.readthedocs.io/en/latest/administrator-docs/building/">Build from Source</a>, then see our <a href="https://jellyfin.readthedocs.io/en/latest/administrator-docs/quick-start/">quick start guide</a>.</em> +<em>Choose from <a href="https://docs.jellyfin.org/general/administration/installing.html">Prebuilt Packages</a> or <a href="https://docs.jellyfin.org/general/administration/building.html">Build from Source</a>, then see our <a href="https://docs.jellyfin.org/general/administration/quick-start.html">quick start guide</a>.</em> </p> <p align="center"> <strong>Want to contribute?</strong> -<em>Check out <a href="https://jellyfin.readthedocs.io/en/latest/contributor-docs/contributing/">our documentation for guidelines</a>.</em> +<em>Check out <a href="https://docs.jellyfin.org/general/contributing/index.html">our documentation for guidelines</a>.</em> </p> <p align="center"> <strong>New idea or improvement?</strong> @@ -41,5 +41,5 @@ For more information about the project, please see our [about page](https://jell </p> <p align="center"> <strong>Something not working right?</strong> -<em>Open an <a href="https://jellyfin.readthedocs.io/en/latest/contributor-docs/issues/">Issue</a>.</em> +<em>Open an <a href="https://docs.jellyfin.org/general/contributing/issues.html">Issue</a>.</em> </p> diff --git a/bump_version b/bump_version index 590020864..106dd7a78 100755 --- a/bump_version +++ b/bump_version @@ -24,33 +24,6 @@ fi shared_version_file="./SharedVersion.cs" build_file="./build.yaml" -if [[ -z $2 ]]; then - web_branch="$( git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' )" -else - web_branch="$2" -fi - -# Initialize submodules -git submodule update --init --recursive - -# configure branch -pushd MediaBrowser.WebDashboard/jellyfin-web - -if ! git diff-index --quiet HEAD --; then - popd - echo - echo "ERROR: Your 'jellyfin-web' submodule working directory is not clean!" - echo "This script will overwrite your unstaged and unpushed changes." - echo "Please do development on 'jellyfin-web' outside of the submodule." - exit 1 -fi - -git fetch --all -git checkout origin/${web_branch} -popd - -git add MediaBrowser.WebDashboard/jellyfin-web - new_version="$1" # Parse the version from the AssemblyVersion diff --git a/deployment/portable/docker-build.sh b/deployment/portable/docker-build.sh index 3645522d1..0cc6e84f0 100755 --- a/deployment/portable/docker-build.sh +++ b/deployment/portable/docker-build.sh @@ -26,7 +26,7 @@ rm -rf ${web_build_dir} version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" # Build archives -dotnet publish --configuration Release --self-contained --runtime framework --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" +dotnet publish --configuration Release --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} rm -rf /dist/jellyfin_${version} diff --git a/deployment/win-x64/docker-build.sh b/deployment/win-x64/docker-build.sh index 36d6f77fc..20bf430c8 100755 --- a/deployment/win-x64/docker-build.sh +++ b/deployment/win-x64/docker-build.sh @@ -51,7 +51,7 @@ cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version} # Create zip package pushd /dist -zip /jellyfin_${version}.portable.zip jellyfin_${version} +zip -r /jellyfin_${version}.portable.zip jellyfin_${version} popd rm -rf /dist/jellyfin_${version} diff --git a/deployment/win-x86/docker-build.sh b/deployment/win-x86/docker-build.sh index 86ede7452..c5f6e82e7 100755 --- a/deployment/win-x86/docker-build.sh +++ b/deployment/win-x86/docker-build.sh @@ -51,7 +51,7 @@ cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version} # Create zip package pushd /dist -zip /jellyfin_${version}.portable.zip jellyfin_${version} +zip -r /jellyfin_${version}.portable.zip jellyfin_${version} popd rm -rf /dist/jellyfin_${version} diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1 index 454c89bf6..c4fb4b995 100644 --- a/deployment/windows/build-jellyfin.ps1 +++ b/deployment/windows/build-jellyfin.ps1 @@ -51,7 +51,7 @@ function Install-FFMPEG { Write-Warning "FFMPEG will not be installed" }elseif($Architecture -eq 'x64'){ Write-Verbose "Downloading 64 bit FFMPEG" - Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.0.2-win64-shared.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose + Invoke-WebRequest -Uri https://repo.jellyfin.org/releases/server/windows/ffmpeg/jellyfin-ffmpeg.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose }else{ Write-Verbose "Downloading 32 bit FFMPEG" Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0.2-win32-shared.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose @@ -60,7 +60,7 @@ function Install-FFMPEG { Expand-Archive "$tempdir/ffmpeg.zip" -DestinationPath "$tempdir/ffmpeg/" -Force | Write-Verbose if($Architecture -eq 'x64'){ Write-Verbose "Copying Binaries to Jellyfin location" - Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.0.2-win64-shared/bin" | ForEach-Object { + Get-ChildItem "$tempdir/ffmpeg" | ForEach-Object { Copy-Item $_.FullName -Destination $installLocation | Write-Verbose } }else{ diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 242d99381..9188b8a02 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -6,7 +6,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> <PackageReference Include="coverlet.collector" Version="1.1.0" /> diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index 5a759bcb2..a7848316e 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -13,9 +13,11 @@ namespace Jellyfin.MediaEncoding.Tests { public IEnumerator<object[]> GetEnumerator() { + yield return new object[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) }; yield return new object[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; yield return new object[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; yield return new object[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; + yield return new object[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -29,9 +31,11 @@ namespace Jellyfin.MediaEncoding.Tests } [Theory] + [InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV414Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV404Output, true)] + [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)] public void ValidateVersionInternalTest(string versionOutput, bool valid) { var val = new EncoderValidator(new NullLogger<EncoderValidatorTests>()); diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index 1d444e2b3..12fde0770 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -2,6 +2,18 @@ namespace Jellyfin.MediaEncoding.Tests { internal static class EncoderValidatorTestsData { + public const string FFmpegV421Output = @"ffmpeg version 4.2.1 Copyright (c) 2000-2019 the FFmpeg developers +built with gcc 9.1.1 (GCC) 20190807 +configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt +libavutil 56. 31.100 / 56. 31.100 +libavcodec 58. 54.100 / 58. 54.100 +libavformat 58. 29.100 / 58. 29.100 +libavdevice 58. 8.100 / 58. 8.100 +libavfilter 7. 57.100 / 7. 57.100 +libswscale 5. 5.100 / 5. 5.100 +libswresample 3. 5.100 / 3. 5.100 +libpostproc 55. 5.100 / 55. 5.100"; + public const string FFmpegV42Output = @"ffmpeg version n4.2 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 9.1.0 (GCC) configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3 @@ -12,8 +24,7 @@ libavdevice 58. 8.100 / 58. 8.100 libavfilter 7. 57.100 / 7. 57.100 libswscale 5. 5.100 / 5. 5.100 libswresample 3. 5.100 / 3. 5.100 -libpostproc 55. 5.100 / 55. 5.100 -"; +libpostproc 55. 5.100 / 55. 5.100"; public const string FFmpegV414Output = @"ffmpeg version 4.1.4-1~deb10u1 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 8 (Raspbian 8.3.0-6+rpi1) @@ -26,8 +37,7 @@ libavfilter 7. 40.101 / 7. 40.101 libavresample 4. 0. 0 / 4. 0. 0 libswscale 5. 3.100 / 5. 3.100 libswresample 3. 3.100 / 3. 3.100 -libpostproc 55. 3.100 / 55. 3.100 -"; +libpostproc 55. 3.100 / 55. 3.100"; public const string FFmpegV404Output = @"ffmpeg version 4.0.4 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 8 (Debian 8.3.0-6) @@ -39,8 +49,18 @@ libavdevice 58. 3.100 / 58. 3.100 libavfilter 7. 16.100 / 7. 16.100 libswscale 5. 1.100 / 5. 1.100 libswresample 3. 1.100 / 3. 1.100 -libpostproc 55. 1.100 / 55. 1.100 -"; +libpostproc 55. 1.100 / 55. 1.100"; + public const string FFmpegGitUnknownOutput = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers +built with gcc 9.1.1 (GCC) 20190716 +configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt +libavutil 56. 30.100 / 56. 30.100 +libavcodec 58. 53.101 / 58. 53.101 +libavformat 58. 28.102 / 58. 28.102 +libavdevice 58. 7.100 / 58. 7.100 +libavfilter 7. 56.101 / 7. 56.101 +libswscale 5. 4.101 / 5. 4.101 +libswresample 3. 4.100 / 3. 4.100 +libpostproc 55. 4.100 / 55. 4.100"; } } diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index beb03d563..9213484fe 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -6,7 +6,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> <PackageReference Include="coverlet.collector" Version="1.1.0" /> |
