diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index c03564f970..81fe5add42 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "8.0.0", + "version": "8.0.1", "commands": [ "dotnet-ef" ] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..063901c800 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,28 @@ +{ + "name": "Development Jellyfin Server", + "image":"mcr.microsoft.com/devcontainers/dotnet:8.0-jammy", + // restores nuget packages, installs the dotnet workloads and installs the dev https certificate + "postStartCommand": "dotnet restore; dotnet workload update; dotnet dev-certs https --trust", + // reads the extensions list and installs them + "postAttachCommand": "cat .vscode/extensions.json | jq -r .recommendations[] | xargs -n 1 code --install-extension", + "features": { + "ghcr.io/devcontainers/features/dotnet:2": { + "version": "none", + "dotnetRuntimeVersions": "8.0", + "aspNetCoreRuntimeVersions": "8.0" + }, + "ghcr.io/devcontainers-contrib/features/apt-packages:1": { + "preserve_apt_list": false, + "packages": ["libfontconfig1"] + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "dockerDashComposeVersion": "v2" + }, + "ghcr.io/devcontainers/features/github-cli:1": {}, + "ghcr.io/eitsupi/devcontainer-features/jq-likes:2": {} + }, + "hostRequirements": { + "memory": "8gb", + "cpus": 4 + } +} diff --git a/.github/workflows/ci-codeql-analysis.yml b/.github/workflows/ci-codeql-analysis.yml index 7c07335980..4b2673e82d 100644 --- a/.github/workflows/ci-codeql-analysis.yml +++ b/.github/workflows/ci-codeql-analysis.yml @@ -27,11 +27,11 @@ jobs: dotnet-version: '8.0.x' - name: Initialize CodeQL - uses: github/codeql-action/init@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 + uses: github/codeql-action/init@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 with: languages: ${{ matrix.language }} queries: +security-extended - name: Autobuild - uses: github/codeql-action/autobuild@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 + uses: github/codeql-action/autobuild@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 + uses: github/codeql-action/analyze@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml index c9f17429a2..75ec82c5f1 100644 --- a/.github/workflows/ci-openapi.yml +++ b/.github/workflows/ci-openapi.yml @@ -25,7 +25,7 @@ jobs: - name: Generate openapi.json run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" - name: Upload openapi.json - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # v4.1.0 with: name: openapi-head retention-days: 14 @@ -59,7 +59,7 @@ jobs: - name: Generate openapi.json run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" - name: Upload openapi.json - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # v4.1.0 with: name: openapi-base retention-days: 14 @@ -78,12 +78,12 @@ jobs: - openapi-base steps: - name: Download openapi-head - uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: name: openapi-head path: openapi-head - name: Download openapi-base - uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: name: openapi-base path: openapi-base diff --git a/.github/workflows/project-automation.yml b/.github/workflows/project-automation.yml index 3637eb16ad..d62f655b30 100644 --- a/.github/workflows/project-automation.yml +++ b/.github/workflows/project-automation.yml @@ -15,7 +15,7 @@ jobs: if: ${{ github.repository == 'jellyfin/jellyfin' }} steps: - name: Remove from 'Current Release' project - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport') continue-on-error: true with: @@ -24,7 +24,7 @@ jobs: repo-token: ${{ secrets.JF_BOT_TOKEN }} - name: Add to 'Release Next' project - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened' continue-on-error: true with: @@ -33,7 +33,7 @@ jobs: repo-token: ${{ secrets.JF_BOT_TOKEN }} - name: Add to 'Current Release' project - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport') continue-on-error: true with: @@ -47,7 +47,7 @@ jobs: run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)" - name: Move issue to needs triage - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1 continue-on-error: true with: @@ -56,7 +56,7 @@ jobs: repo-token: ${{ secrets.JF_BOT_TOKEN }} - name: Add issue to triage project - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: github.event.issue.pull_request == '' && github.event.action == 'opened' continue-on-error: true with: diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 59d9452fed..d738e9fba4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,13 +1,11 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - - // List of extensions which should be recommended for users of this workspace. "recommendations": [ "ms-dotnettools.csharp", - "editorconfig.editorconfig" + "editorconfig.editorconfig", + "GitHub.vscode-github-actions", + "ms-dotnettools.vscode-dotnet-runtime", + "ms-dotnettools.csdevkit" ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [ ] diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d208879d17..4e45fd24ad 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -81,6 +81,7 @@ - [Maxr1998](https://github.com/Maxr1998) - [mcarlton00](https://github.com/mcarlton00) - [mitchfizz05](https://github.com/mitchfizz05) + - [mohd-akram](https://github.com/mohd-akram) - [MrTimscampi](https://github.com/MrTimscampi) - [n8225](https://github.com/n8225) - [Nalsai](https://github.com/Nalsai) diff --git a/Directory.Packages.props b/Directory.Packages.props index 30bd8ac553..ebb6038d81 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,46 +18,46 @@ - + - + - + - - - - - + + + + + - + - - + + - + - - + + - + @@ -67,9 +67,9 @@ - - - + + + @@ -78,7 +78,7 @@ - + @@ -86,6 +86,6 @@ - + diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f385f6a512..48d5d8c6a9 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -15,7 +15,6 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Emby.Naming.Common; using Emby.Photos; -using Emby.Server.Implementations.Channels; using Emby.Server.Implementations.Collections; using Emby.Server.Implementations.Configuration; using Emby.Server.Implementations.Cryptography; @@ -25,7 +24,6 @@ using Emby.Server.Implementations.Dto; using Emby.Server.Implementations.HttpServer.Security; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Library; -using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Plugins; @@ -504,8 +502,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(_xmlSerializer); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -557,8 +553,6 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -567,9 +561,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index 01b5fdaeea..25ef57d271 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -104,6 +104,13 @@ namespace Emby.Server.Implementations.Data if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal, out var dateTimeResult)) { + // If the resulting DateTimeKind is Unspecified it is actually Utc. + // This is required downstream for the Json serializer. + if (dateTimeResult.Kind == DateTimeKind.Unspecified) + { + dateTimeResult = DateTime.SpecifyKind(dateTimeResult, DateTimeKind.Utc); + } + result = dateTimeResult; return true; } diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 44b97e8b83..d0d5bb81c1 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -418,15 +418,6 @@ namespace Emby.Server.Implementations.Dto { dto.PlayAccess = item.GetPlayAccess(user); } - - if (options.ContainsField(ItemFields.BasicSyncInfo)) - { - var userCanSync = user is not null && user.HasPermission(PermissionKind.EnableContentDownloading); - if (userCanSync && item.SupportsExternalTransfer) - { - dto.SupportsSync = true; - } - } } private static int GetChildCount(Folder folder, User user) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index b3344bb9ff..34276355a7 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -22,7 +22,6 @@ - diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 96fad9bca8..68eccf311d 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -11,14 +11,15 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using EasyCaching.Core.Configurations; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; @@ -37,6 +38,7 @@ namespace Emby.Server.Implementations.Library // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message. private const char LiveStreamIdDelimeter = '_'; + private readonly IServerApplicationHost _appHost; private readonly IItemRepository _itemRepo; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; @@ -55,6 +57,7 @@ namespace Emby.Server.Implementations.Library private IMediaSourceProvider[] _providers; public MediaSourceManager( + IServerApplicationHost appHost, IItemRepository itemRepo, IApplicationPaths applicationPaths, ILocalizationManager localizationManager, @@ -66,6 +69,7 @@ namespace Emby.Server.Implementations.Library IMediaEncoder mediaEncoder, IDirectoryService directoryService) { + _appHost = appHost; _itemRepo = itemRepo; _userManager = userManager; _libraryManager = libraryManager; @@ -799,6 +803,35 @@ namespace Emby.Server.Implementations.Library return result.Item1; } + public async Task> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken) + { + var stream = new MediaSourceInfo + { + EncoderPath = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveRecordings/" + info.Id + "/stream", + EncoderProtocol = MediaProtocol.Http, + Path = info.Path, + Protocol = MediaProtocol.File, + Id = info.Id, + SupportsDirectPlay = false, + SupportsDirectStream = true, + SupportsTranscoding = true, + IsInfiniteStream = true, + RequiresOpening = false, + RequiresClosing = false, + BufferMs = 0, + IgnoreDts = true, + IgnoreIndex = true + }; + + await new LiveStreamHelper(_mediaEncoder, _logger, _appPaths) + .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false); + + return new List + { + stream + }; + } + public async Task CloseLiveStream(string id) { ArgumentException.ThrowIfNullOrEmpty(id); diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index 5da33febe3..1c7bc75b5c 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -83,8 +83,8 @@ "UserDeletedWithName": "Uživatel {0} byl smazán", "UserDownloadingItemWithValues": "{0} stahuje {1}", "UserLockedOutWithName": "Uživatel {0} byl odemčen", - "UserOfflineFromDevice": "{0} se odpojil od {1}", - "UserOnlineFromDevice": "{0} se připojil z {1}", + "UserOfflineFromDevice": "{0} se odpojil ze zařízení {1}", + "UserOnlineFromDevice": "{0} se připojil ze zařízení {1}", "UserPasswordChangedWithName": "Provedena změna hesla pro uživatele {0}", "UserPolicyUpdatedWithName": "Zásady uživatele pro {0} byly aktualizovány", "UserStartedPlayingItemWithValues": "{0} spustil přehrávání {1}", diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index f1dbf3c89d..7a4c2067ba 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -121,8 +121,8 @@ "Default": "Standard", "TaskOptimizeDatabaseDescription": "Komprimiert die Datenbank und trimmt den freien Speicherplatz. Die Ausführung dieser Aufgabe nach dem Scannen der Bibliothek oder nach anderen Änderungen, die Datenbankänderungen implizieren, kann die Leistung verbessern.", "TaskOptimizeDatabase": "Datenbank optimieren", - "TaskKeyframeExtractorDescription": "Extrahiere Keyframes aus Videodateien, um präzisere HLS-Playlisten zu erzeugen. Dieser Vorgang kann sehr lange dauern.", - "TaskKeyframeExtractor": "Keyframe Extraktor", + "TaskKeyframeExtractorDescription": "Extrahiert Keyframes aus Videodateien, um präzisere HLS-Playlisten zu erzeugen. Dieser Vorgang kann sehr lange dauern.", + "TaskKeyframeExtractor": "Keyframe-Extraktor", "External": "Extern", "HearingImpaired": "Hörgeschädigt", "TaskRefreshTrickplayImages": "Trickplay-Bilder generieren", diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json index 3d5c046336..c6863ff368 100644 --- a/Emby.Server.Implementations/Localization/Core/es_419.json +++ b/Emby.Server.Implementations/Localization/Core/es_419.json @@ -123,5 +123,7 @@ "External": "Externo", "TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reproducción HLS más precisas. Esta tarea puede durar mucho tiempo.", "TaskKeyframeExtractor": "Extractor de Fotogramas Clave", - "HearingImpaired": "Discapacidad auditiva" + "HearingImpaired": "Discapacidad auditiva", + "TaskRefreshTrickplayImagesDescription": "Crea previsualizaciones para la barra de reproducción en las bibliotecas habilitadas.", + "TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción" } diff --git a/Emby.Server.Implementations/Localization/Core/ka.json b/Emby.Server.Implementations/Localization/Core/ka.json index dbbc81eeb2..5368b8eb6e 100644 --- a/Emby.Server.Implementations/Localization/Core/ka.json +++ b/Emby.Server.Implementations/Localization/Core/ka.json @@ -123,5 +123,7 @@ "TaskUpdatePluginsDescription": "ავტომატურად განახლებადად მონიშნული დამატებების განახლებების გადმოწერა და დაყენება.", "TaskCleanTranscodeDescription": "ერთ დღეზე უფრო ძველი ტრანსკოდირების ფაილების წაშლა.", "TaskDownloadMissingSubtitlesDescription": "მეტამონაცემებზე დაყრდნობით ინტერნეტში ნაკლული სუბტიტრების ძებნა.", - "TaskOptimizeDatabaseDescription": "ბაზს შეკუშვა და ადგილის გათავისუფლება. ამ ამოცანის ბიბლიოთეკის სკანირების ან ნებისმიერი ცვლილების, რომელიც ბაზაში რამეს აკეთებს, გაშვებას შეუძლია ბაზის წარმადობა გაზარდოს." + "TaskOptimizeDatabaseDescription": "ბაზს შეკუშვა და ადგილის გათავისუფლება. ამ ამოცანის ბიბლიოთეკის სკანირების ან ნებისმიერი ცვლილების, რომელიც ბაზაში რამეს აკეთებს, გაშვებას შეუძლია ბაზის წარმადობა გაზარდოს.", + "TaskRefreshTrickplayImagesDescription": "ქმნის trickplay წინასწარ ხედებს ვიდეოებისთვის ჩართულ ბიბლიოთეკებში.", + "TaskRefreshTrickplayImages": "Trickplay სურათების გენერირება" } diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index 82a071309e..6e58ef8345 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -123,5 +123,7 @@ "External": "Ārējais", "HearingImpaired": "Ar dzirdes traucējumiem", "TaskKeyframeExtractor": "Atslēgkadru ekstraktors", - "TaskKeyframeExtractorDescription": "Ekstraktē atslēgkadrus no video failiem lai izveidotu precīzākus HLS atskaņošanas sarakstus. Šis process var būt ilgs." + "TaskKeyframeExtractorDescription": "Ekstraktē atslēgkadrus no video failiem lai izveidotu precīzākus HLS atskaņošanas sarakstus. Šis process var būt ilgs.", + "TaskRefreshTrickplayImages": "Ģenerēt partīšanas attēlus", + "TaskRefreshTrickplayImagesDescription": "Izveido priekšskatījumus videoklipu pārtīšanai iespējotajās bibliotēkās." } diff --git a/Emby.Server.Implementations/Localization/Core/nn.json b/Emby.Server.Implementations/Localization/Core/nn.json index 32d4f3a8b6..d0c914de37 100644 --- a/Emby.Server.Implementations/Localization/Core/nn.json +++ b/Emby.Server.Implementations/Localization/Core/nn.json @@ -117,5 +117,6 @@ "TaskCleanActivityLog": "Slett aktivitetslogg", "Undefined": "Udefinert", "Forced": "Tvungen", - "Default": "Standard" + "Default": "Standard", + "External": "Ekstern" } diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json index bd5398f083..6f0dcfbe30 100644 --- a/Emby.Server.Implementations/Localization/Core/uk.json +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -18,16 +18,16 @@ "HeaderContinueWatching": "Продовжити перегляд", "HeaderAlbumArtists": "Виконавці альбому", "Genres": "Жанри", - "Folders": "Каталоги", + "Folders": "Теки", "Favorites": "Обрані", "DeviceOnlineWithName": "Пристрій {0} підключився", "DeviceOfflineWithName": "Пристрій {0} відключився", - "Collections": "Добірки", - "ChapterNameValue": "Розділ {0}", + "Collections": "Колекції", + "ChapterNameValue": "Сцена {0}", "Channels": "Канали", - "CameraImageUploadedFrom": "Нова фотографія завантажена з {0}", + "CameraImageUploadedFrom": "Нову фотографію завантажено з {0}", "Books": "Книги", - "AuthenticationSucceededWithUserName": "{0} успішно автентифіковано", + "AuthenticationSucceededWithUserName": "{0} успішно авторизовано", "Artists": "Виконавці", "Application": "Додаток", "AppDeviceValues": "Додаток: {0}, Пристрій: {1}", @@ -83,7 +83,7 @@ "SubtitleDownloadFailureFromForItem": "Не вдалося завантажити субтитри з {0} для {1}", "StartupEmbyServerIsLoading": "Jellyfin Server завантажується. Будь ласка, спробуйте трішки пізніше.", "Songs": "Пісні", - "Shows": "Шоу", + "Shows": "Телепередачі", "ServerNameNeedsToBeRestarted": "{0} потрібно перезапустити", "ScheduledTaskStartedWithName": "{0} розпочато", "ScheduledTaskFailedWithName": "{0} незавершено, збій", diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index e8b8c2c5fa..66cbee3948 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -1,15 +1,15 @@ { "Albums": "專輯", - "AppDeviceValues": "程式: {0}, 設備: {1}", + "AppDeviceValues": "程式:{0},設備:{1}", "Application": "應用程式", "Artists": "藝人", - "AuthenticationSucceededWithUserName": "{0} 授權成功", + "AuthenticationSucceededWithUserName": "成功授權 {0}", "Books": "書籍", "CameraImageUploadedFrom": "{0} 成功上傳一張新照片", "Channels": "頻道", "ChapterNameValue": "第 {0} 章", "Collections": "系列", - "DeviceOfflineWithName": "{0} 已斷開連接", + "DeviceOfflineWithName": "{0} 已中斷連接", "DeviceOnlineWithName": "{0} 已連接", "FailedLoginAttemptWithUserName": "{0} 登入失敗", "Favorites": "我的最愛", @@ -27,15 +27,15 @@ "HeaderRecordingGroups": "錄製組", "HomeVideos": "家庭影片", "Inherit": "繼承", - "ItemAddedWithName": "{0} 已被添加至媒體庫", + "ItemAddedWithName": "{0} 已被加入至媒體庫", "ItemRemovedWithName": "{0} 已從媒體庫移除", - "LabelIpAddressValue": "IP 地址: {0}", - "LabelRunningTimeValue": "運行時間: {0}", + "LabelIpAddressValue": "IP 地址:{0}", + "LabelRunningTimeValue": "運作時間:{0}", "Latest": "最新", "MessageApplicationUpdated": "Jellyfin 已被更新", "MessageApplicationUpdatedTo": "Jellyfin 已被更新至 {0}", "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 已被更新", - "MessageServerConfigurationUpdated": "伺服器設定已經被更新", + "MessageServerConfigurationUpdated": "已更新伺服器設定", "MixedContent": "混合內容", "Movies": "電影", "Music": "音樂", @@ -43,23 +43,23 @@ "NameInstallFailed": "{0} 安裝失敗", "NameSeasonNumber": "第 {0} 季", "NameSeasonUnknown": "未知的季度", - "NewVersionIsAvailable": "有較新版本的 Jellyfin 可供下載。", + "NewVersionIsAvailable": "有新版本的 Jellyfin 可供下載。", "NotificationOptionApplicationUpdateAvailable": "有可用的更新", - "NotificationOptionApplicationUpdateInstalled": "應用程式已被更新", - "NotificationOptionAudioPlayback": "開始播放音訊", + "NotificationOptionApplicationUpdateInstalled": "完成更新應用程式", + "NotificationOptionAudioPlayback": "播放音訊", "NotificationOptionAudioPlaybackStopped": "停止播放音訊", - "NotificationOptionCameraImageUploaded": "相片已被上傳", + "NotificationOptionCameraImageUploaded": "相片上傳", "NotificationOptionInstallationFailed": "安裝失敗", - "NotificationOptionNewLibraryContent": "已添加新内容", - "NotificationOptionPluginError": "插件出現錯誤", - "NotificationOptionPluginInstalled": "插件已被安裝", - "NotificationOptionPluginUninstalled": "插件已被移除", - "NotificationOptionPluginUpdateInstalled": "插件已被更新", + "NotificationOptionNewLibraryContent": "新增媒體", + "NotificationOptionPluginError": "插件錯誤", + "NotificationOptionPluginInstalled": "安裝插件", + "NotificationOptionPluginUninstalled": "解除安裝插件", + "NotificationOptionPluginUpdateInstalled": "完成更新插件", "NotificationOptionServerRestartRequired": "伺服器需要重啟", - "NotificationOptionTaskFailed": "排程任務執行失敗", - "NotificationOptionUserLockedOut": "用戶已被鎖定", - "NotificationOptionVideoPlayback": "開始播放影片", - "NotificationOptionVideoPlaybackStopped": "已停止播放影片", + "NotificationOptionTaskFailed": "排程工作執行失敗", + "NotificationOptionUserLockedOut": "封鎖用戶", + "NotificationOptionVideoPlayback": "播放影片", + "NotificationOptionVideoPlaybackStopped": "停止播放影片", "Photos": "相片", "Playlists": "播放清單", "Plugin": "插件", @@ -68,7 +68,7 @@ "PluginUpdatedWithName": "已更新 {0}", "ProviderValue": "提供者:{0}", "ScheduledTaskFailedWithName": "{0} 執行失敗", - "ScheduledTaskStartedWithName": "{0} 開始執行", + "ScheduledTaskStartedWithName": "開始執行 {0}", "ServerNameNeedsToBeRestarted": "{0} 需要重啟", "Shows": "節目", "Songs": "歌曲", @@ -79,50 +79,52 @@ "System": "系統", "TvShows": "電視節目", "User": "用戶", - "UserCreatedWithName": "用戶 {0} 已被建立", + "UserCreatedWithName": "建立新用戶 {0}", "UserDeletedWithName": "用戶 {0} 已被移除", "UserDownloadingItemWithValues": "{0} 正在下載 {1}", - "UserLockedOutWithName": "使用者 {0} 已被鎖定", + "UserLockedOutWithName": "用戶 {0} 已被封鎖", "UserOfflineFromDevice": "{0} 從 {1} 斷開連接", "UserOnlineFromDevice": "{0} 從 {1} 連線", "UserPasswordChangedWithName": "{0} 的密碼已被變改", - "UserPolicyUpdatedWithName": "使用者協議已更新為 {0}", - "UserStartedPlayingItemWithValues": "{0} 正在 {2} 上播放 {1}", + "UserPolicyUpdatedWithName": "使用條款已更新為 {0}", + "UserStartedPlayingItemWithValues": "{0} 在 {2} 上播放 {1}", "UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 上播放 {1}", "ValueHasBeenAddedToLibrary": "已添加 {0} 到你的媒體庫", "ValueSpecialEpisodeName": "特典 - {0}", "VersionNumber": "版本 {0}", - "TaskDownloadMissingSubtitles": "下載缺少的字幕", + "TaskDownloadMissingSubtitles": "下載欠缺字幕", "TaskUpdatePlugins": "更新插件", "TasksApplicationCategory": "應用程式", - "TaskRefreshLibraryDescription": "掃描媒體庫以加入新增檔案及重新載入 metadata。", + "TaskRefreshLibraryDescription": "掃描媒體庫以加入新增的檔案及重新載入元數據。", "TasksMaintenanceCategory": "維護", - "TaskDownloadMissingSubtitlesDescription": "根據元數據中的設定,在互聯網上搜索缺少的字幕。", + "TaskDownloadMissingSubtitlesDescription": "根據元數據中的設定,在網上搜尋欠缺的字幕。", "TaskRefreshChannelsDescription": "重新載入網絡頻道的資訊。", "TaskRefreshChannels": "重新載入頻道", - "TaskCleanTranscodeDescription": "刪除超過一天的轉碼文件。", - "TaskCleanTranscode": "清理轉碼目錄", + "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。", + "TaskCleanTranscode": "清理轉碼檔資料夾", "TaskUpdatePluginsDescription": "下載並更新能夠被自動更新的插件。", - "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的元數據。", + "TaskRefreshPeopleDescription": "更新你的媒體中有關的演員和導演的元數據。", "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔。", - "TaskCleanLogs": "清理紀錄檔目錄", + "TaskCleanLogs": "清理紀錄檔資料夾", "TaskRefreshLibrary": "掃描媒體庫", "TaskRefreshChapterImagesDescription": "為帶有章節的影片建立縮圖。", "TaskRefreshChapterImages": "提取章節圖像", "TaskCleanCacheDescription": "刪除系統不再需要的緩存文件。", - "TaskCleanCache": "清理緩存目錄", + "TaskCleanCache": "清理緩存資料夾", "TasksChannelsCategory": "網絡頻道", - "TasksLibraryCategory": "庫", + "TasksLibraryCategory": "媒體庫", "TaskRefreshPeople": "重新載入人物", "TaskCleanActivityLog": "清理活動記錄", "Undefined": "未定義", "Forced": "強制", "Default": "預設", - "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。", + "TaskOptimizeDatabaseDescription": "壓縮數據庫及釋放可用空間。完成任何會修改數據庫的工作(例如掃描媒體庫)後,執行此工作或可提升伺服器速度。", "TaskOptimizeDatabase": "最佳化數據庫", "TaskCleanActivityLogDescription": "刪除早於設定時間的活動記錄。", - "TaskKeyframeExtractorDescription": "提取關鍵幀以建立更準確的 HLS 播放列表。此工作或需要使用較長時間來完成。", - "TaskKeyframeExtractor": "關鍵幀提取器", + "TaskKeyframeExtractorDescription": "提取關鍵影格(Keyframe)以建立更準確的 HLS playlist。此工作可能需要使用較長時間來完成。", + "TaskKeyframeExtractor": "關鍵影格提取器", "External": "外部", - "HearingImpaired": "聽力障礙" + "HearingImpaired": "聽力障礙", + "TaskRefreshTrickplayImages": "建立 Trickplay 圖像", + "TaskRefreshTrickplayImagesDescription": "為已啟用 Trickplay 的媒體庫內的影片建立 Trickplay 預覽圖。" } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index e8e63d286d..6f599e4c7c 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1670,7 +1670,6 @@ namespace Emby.Server.Implementations.Session var fields = dtoOptions.Fields.ToList(); - fields.Remove(ItemFields.BasicSyncInfo); fields.Remove(ItemFields.CanDelete); fields.Remove(ItemFields.CanDownload); fields.Remove(ItemFields.ChildCount); diff --git a/Jellyfin.Api/Controllers/DevicesController.cs b/Jellyfin.Api/Controllers/DevicesController.cs index aa200a7221..6d9ec343e0 100644 --- a/Jellyfin.Api/Controllers/DevicesController.cs +++ b/Jellyfin.Api/Controllers/DevicesController.cs @@ -42,16 +42,15 @@ public class DevicesController : BaseJellyfinApiController /// /// Get Devices. /// - /// Gets or sets a value indicating whether [supports synchronize]. /// Gets or sets the user identifier. /// Devices retrieved. /// An containing the list of devices. [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> GetDevices([FromQuery] bool? supportsSync, [FromQuery] Guid? userId) + public async Task>> GetDevices([FromQuery] Guid? userId) { userId = RequestHelpers.GetUserId(User, userId); - return await _deviceManager.GetDevicesForUser(userId, supportsSync).ConfigureAwait(false); + return await _deviceManager.GetDevicesForUser(userId).ConfigureAwait(false); } /// diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index fdebb3d450..083515a949 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -385,7 +385,6 @@ public class SessionController : BaseJellyfinApiController /// A list of playable media types, comma delimited. Audio, Video, Book, Photo. /// A list of supported remote control commands, comma delimited. /// Determines whether media can be played remotely.. - /// Determines whether sync is supported. /// Determines whether the device supports a unique identifier. /// Capabilities posted. /// A . @@ -397,7 +396,6 @@ public class SessionController : BaseJellyfinApiController [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] playableMediaTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] GeneralCommandType[] supportedCommands, [FromQuery] bool supportsMediaControl = false, - [FromQuery] bool supportsSync = false, [FromQuery] bool supportsPersistentIdentifier = true) { if (string.IsNullOrWhiteSpace(id)) @@ -410,7 +408,6 @@ public class SessionController : BaseJellyfinApiController PlayableMediaTypes = playableMediaTypes, SupportedCommands = supportedCommands, SupportsMediaControl = supportsMediaControl, - SupportsSync = supportsSync, SupportsPersistentIdentifier = supportsPersistentIdentifier }); return NoContent(); diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 0ffa3ab1ad..035d044741 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -90,7 +90,6 @@ public class UserViewsController : BaseJellyfinApiController fields.Add(ItemFields.PrimaryImageAspectRatio); fields.Add(ItemFields.DisplayPreferencesId); - fields.Remove(ItemFields.BasicSyncInfo); dtoOptions.Fields = fields.ToArray(); var user = _userManager.GetUserById(userId); diff --git a/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs index 27bcd5570c..842a69dd98 100644 --- a/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs @@ -41,6 +41,8 @@ public class IPBasedAccessValidationMiddleware if (!networkManager.HasRemoteAccess(remoteIP)) { + // No access from network, respond with 503 instead of 200. + httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; return; } diff --git a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs index d8c95ddffe..35b0a1dd06 100644 --- a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs +++ b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs @@ -1,3 +1,4 @@ +using System.Net; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -40,6 +41,8 @@ public class LanFilteringMiddleware var host = httpContext.GetNormalizedRemoteIP(); if (!networkManager.IsInLocalNetwork(host)) { + // No access from network, respond with 503 instead of 200. + httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; return; } diff --git a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs b/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs index b021771a0f..acd3f29e34 100644 --- a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs +++ b/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs @@ -30,26 +30,11 @@ public class ClientCapabilitiesDto /// public bool SupportsMediaControl { get; set; } - /// - /// Gets or sets a value indicating whether session supports content uploading. - /// - public bool SupportsContentUploading { get; set; } - - /// - /// Gets or sets the message callback url. - /// - public string? MessageCallbackUrl { get; set; } - /// /// Gets or sets a value indicating whether session supports a persistent identifier. /// public bool SupportsPersistentIdentifier { get; set; } - /// - /// Gets or sets a value indicating whether session supports sync. - /// - public bool SupportsSync { get; set; } - /// /// Gets or sets the device profile. /// @@ -76,10 +61,7 @@ public class ClientCapabilitiesDto PlayableMediaTypes = PlayableMediaTypes, SupportedCommands = SupportedCommands, SupportsMediaControl = SupportsMediaControl, - SupportsContentUploading = SupportsContentUploading, - MessageCallbackUrl = MessageCallbackUrl, SupportsPersistentIdentifier = SupportsPersistentIdentifier, - SupportsSync = SupportsSync, DeviceProfile = DeviceProfile, AppStoreUrl = AppStoreUrl, IconUrl = IconUrl diff --git a/Jellyfin.Data/Enums/AudioSpatialFormat.cs b/Jellyfin.Data/Enums/AudioSpatialFormat.cs new file mode 100644 index 0000000000..5e3a123320 --- /dev/null +++ b/Jellyfin.Data/Enums/AudioSpatialFormat.cs @@ -0,0 +1,22 @@ +namespace Jellyfin.Data.Enums; + +/// +/// An enum representing formats of spatial audio. +/// +public enum AudioSpatialFormat +{ + /// + /// None audio spatial format. + /// + None, + + /// + /// Dolby Atmos audio spatial format. + /// + DolbyAtmos, + + /// + /// DTS:X audio spatial format. + /// + DTSX, +} diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs index d8eee12467..5e5d52b6b3 100644 --- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs +++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs @@ -167,7 +167,7 @@ namespace Jellyfin.Server.Implementations.Devices } /// - public async Task> GetDevicesForUser(Guid? userId, bool? supportsSync) + public async Task> GetDevicesForUser(Guid? userId) { var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) @@ -178,10 +178,6 @@ namespace Jellyfin.Server.Implementations.Devices .ThenBy(d => d.DeviceId) .SelectMany(d => dbContext.DeviceOptions.Where(o => o.DeviceId == d.DeviceId).DefaultIfEmpty(), (d, o) => new { Device = d, Options = o }) .AsAsyncEnumerable(); - if (supportsSync.HasValue) - { - sessions = sessions.Where(i => GetCapabilities(i.Device.DeviceId).SupportsSync == supportsSync.Value); - } if (userId.HasValue) { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 990b9a5bd4..cc464a7a3d 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -1,7 +1,7 @@ #pragma warning disable CA1307 +#pragma warning disable CA1309 // Use ordinal string comparison - EF can't translate this using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -46,8 +46,6 @@ namespace Jellyfin.Server.Implementations.Users private readonly DefaultPasswordResetProvider _defaultPasswordResetProvider; private readonly IServerConfigurationManager _serverConfigurationManager; - private readonly IDictionary _users; - /// /// Initializes a new instance of the class. /// @@ -58,6 +56,8 @@ namespace Jellyfin.Server.Implementations.Users /// The image processor. /// The logger. /// The system config manager. + /// The password reset providers. + /// The authentication providers. public UserManager( IDbContextFactory dbProvider, IEventManager eventManager, @@ -65,7 +65,9 @@ namespace Jellyfin.Server.Implementations.Users IApplicationHost appHost, IImageProcessor imageProcessor, ILogger logger, - IServerConfigurationManager serverConfigurationManager) + IServerConfigurationManager serverConfigurationManager, + IEnumerable passwordResetProviders, + IEnumerable authenticationProviders) { _dbProvider = dbProvider; _eventManager = eventManager; @@ -75,35 +77,36 @@ namespace Jellyfin.Server.Implementations.Users _logger = logger; _serverConfigurationManager = serverConfigurationManager; - _passwordResetProviders = appHost.GetExports(); - _authenticationProviders = appHost.GetExports(); + _passwordResetProviders = passwordResetProviders.ToList(); + _authenticationProviders = authenticationProviders.ToList(); _invalidAuthProvider = _authenticationProviders.OfType().First(); _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); - - _users = new ConcurrentDictionary(); - using var dbContext = _dbProvider.CreateDbContext(); - foreach (var user in dbContext.Users - .AsSplitQuery() - .Include(user => user.Permissions) - .Include(user => user.Preferences) - .Include(user => user.AccessSchedules) - .Include(user => user.ProfileImage) - .AsEnumerable()) - { - _users.Add(user.Id, user); - } } /// public event EventHandler>? OnUserUpdated; /// - public IEnumerable Users => _users.Values; + public IEnumerable Users + { + get + { + using var dbContext = _dbProvider.CreateDbContext(); + return GetUsersInternal(dbContext).ToList(); + } + } /// - public IEnumerable UsersIds => _users.Keys; + public IEnumerable UsersIds + { + get + { + using var dbContext = _dbProvider.CreateDbContext(); + return dbContext.Users.Select(u => u.Id).ToList(); + } + } // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness @@ -119,8 +122,8 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Guid can't be empty", nameof(id)); } - _users.TryGetValue(id, out var user); - return user; + using var dbContext = _dbProvider.CreateDbContext(); + return GetUsersInternal(dbContext).FirstOrDefault(u => u.Id.Equals(id)); } /// @@ -131,7 +134,9 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Invalid username", nameof(name)); } - return _users.Values.FirstOrDefault(u => string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase)); + using var dbContext = _dbProvider.CreateDbContext(); + return GetUsersInternal(dbContext) + .FirstOrDefault(u => string.Equals(u.Username, name)); } /// @@ -196,8 +201,6 @@ namespace Jellyfin.Server.Implementations.Users user.AddDefaultPermissions(); user.AddDefaultPreferences(); - _users.Add(user.Id, user); - return user; } @@ -232,40 +235,46 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task DeleteUserAsync(Guid userId) { - if (!_users.TryGetValue(userId, out var user)) - { - throw new ResourceNotFoundException(nameof(userId)); - } + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); - if (_users.Count == 1) + await using (dbContext.ConfigureAwait(false)) { - throw new InvalidOperationException(string.Format( - CultureInfo.InvariantCulture, - "The user '{0}' cannot be deleted because there must be at least one user in the system.", - user.Username)); - } + var user = await dbContext.Users + .AsSingleQuery() + .Include(u => u.Permissions) + .FirstOrDefaultAsync(u => u.Id.Equals(userId)) + .ConfigureAwait(false); + if (user is null) + { + throw new ResourceNotFoundException(nameof(userId)); + } - if (user.HasPermission(PermissionKind.IsAdministrator) - && Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1) - { - throw new ArgumentException( - string.Format( + if (await dbContext.Users.CountAsync().ConfigureAwait(false) == 1) + { + throw new InvalidOperationException(string.Format( CultureInfo.InvariantCulture, - "The user '{0}' cannot be deleted because there must be at least one admin user in the system.", - user.Username), - nameof(userId)); - } + "The user '{0}' cannot be deleted because there must be at least one user in the system.", + user.Username)); + } + + if (user.HasPermission(PermissionKind.IsAdministrator) + && await dbContext.Users + .CountAsync(u => u.Permissions.Any(p => p.Kind == PermissionKind.IsAdministrator && p.Value)) + .ConfigureAwait(false) == 1) + { + throw new ArgumentException( + string.Format( + CultureInfo.InvariantCulture, + "The user '{0}' cannot be deleted because there must be at least one admin user in the system.", + user.Username), + nameof(userId)); + } - var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); - await using (dbContext.ConfigureAwait(false)) - { dbContext.Users.Remove(user); await dbContext.SaveChangesAsync().ConfigureAwait(false); - } - _users.Remove(userId); - - await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false); + await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false); + } } /// @@ -532,23 +541,23 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task InitializeAsync() { - // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. - if (_users.Any()) + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) { - return; - } + // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. + if (await dbContext.Users.AnyAsync().ConfigureAwait(false)) + { + return; + } - var defaultName = Environment.UserName; - if (string.IsNullOrWhiteSpace(defaultName) || !ValidUsernameRegex().IsMatch(defaultName)) - { - defaultName = "MyJellyfinUser"; - } + var defaultName = Environment.UserName; + if (string.IsNullOrWhiteSpace(defaultName) || !ValidUsernameRegex().IsMatch(defaultName)) + { + defaultName = "MyJellyfinUser"; + } - _logger.LogWarning("No users, creating one with username {UserName}", defaultName); + _logger.LogWarning("No users, creating one with username {UserName}", defaultName); - var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); - await using (dbContext.ConfigureAwait(false)) - { var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false); newUser.SetPermission(PermissionKind.IsAdministrator, true); newUser.SetPermission(PermissionKind.EnableContentDeletion, true); @@ -595,12 +604,9 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) { - var user = dbContext.Users - .Include(u => u.Permissions) - .Include(u => u.Preferences) - .Include(u => u.AccessSchedules) - .Include(u => u.ProfileImage) - .FirstOrDefault(u => u.Id.Equals(userId)) + var user = await GetUsersInternal(dbContext) + .FirstOrDefaultAsync(u => u.Id.Equals(userId)) + .ConfigureAwait(false) ?? throw new ArgumentException("No user exists with given Id!"); user.SubtitleMode = config.SubtitleMode; @@ -628,7 +634,6 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes); dbContext.Update(user); - _users[user.Id] = user; await dbContext.SaveChangesAsync().ConfigureAwait(false); } } @@ -639,12 +644,9 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) { - var user = dbContext.Users - .Include(u => u.Permissions) - .Include(u => u.Preferences) - .Include(u => u.AccessSchedules) - .Include(u => u.ProfileImage) - .FirstOrDefault(u => u.Id.Equals(userId)) + var user = await GetUsersInternal(dbContext) + .FirstOrDefaultAsync(u => u.Id.Equals(userId)) + .ConfigureAwait(false) ?? throw new ArgumentException("No user exists with given Id!"); // The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0" @@ -704,7 +706,6 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); dbContext.Update(user); - _users[user.Id] = user; await dbContext.SaveChangesAsync().ConfigureAwait(false); } } @@ -725,7 +726,6 @@ namespace Jellyfin.Server.Implementations.Users } user.ProfileImage = null; - _users[user.Id] = user; } internal static void ThrowIfInvalidUsername(string name) @@ -872,8 +872,15 @@ namespace Jellyfin.Server.Implementations.Users private async Task UpdateUserInternalAsync(JellyfinDbContext dbContext, User user) { dbContext.Users.Update(user); - _users[user.Id] = user; await dbContext.SaveChangesAsync().ConfigureAwait(false); } + + private IQueryable GetUsersInternal(JellyfinDbContext dbContext) + => dbContext.Users + .AsSplitQuery() + .Include(user => user.Permissions) + .Include(user => user.Preferences) + .Include(user => user.AccessSchedules) + .Include(user => user.ProfileImage); } } diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index c12c90a689..5192b9e21b 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -6,6 +6,8 @@ using Emby.Server.Implementations.Session; using Jellyfin.Api.WebSocketListeners; using Jellyfin.Drawing; using Jellyfin.Drawing.Skia; +using Jellyfin.LiveTv; +using Jellyfin.LiveTv.Channels; using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Activity; using Jellyfin.Server.Implementations.Devices; @@ -14,16 +16,20 @@ using Jellyfin.Server.Implementations.Security; using Jellyfin.Server.Implementations.Trickplay; using Jellyfin.Server.Implementations.Users; using MediaBrowser.Controller; +using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.BaseItemManager; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Trickplay; using MediaBrowser.Model.Activity; +using MediaBrowser.Model.IO; using MediaBrowser.Providers.Lyric; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -78,6 +84,9 @@ namespace Jellyfin.Server serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddScoped(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -92,6 +101,11 @@ namespace Jellyfin.Server serviceCollection.AddScoped(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + foreach (var type in GetExportTypes()) { serviceCollection.AddSingleton(typeof(ILyricProvider), type); @@ -113,6 +127,9 @@ namespace Jellyfin.Server // Jellyfin.Server.Implementations yield return typeof(JellyfinDbContext).Assembly; + + // Jellyfin.LiveTv + yield return typeof(LiveTvManager).Assembly; } } } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 1d4d97551e..21c6e6f01d 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -58,6 +58,7 @@ + diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index aa7be9109e..1030c6f5fc 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -6,6 +6,7 @@ using System.Net.Mime; using System.Text; using Jellyfin.Api.Middleware; using Jellyfin.MediaEncoding.Hls.Extensions; +using Jellyfin.Networking; using Jellyfin.Networking.HappyEyeballs; using Jellyfin.Server.Extensions; using Jellyfin.Server.HealthChecks; @@ -13,7 +14,6 @@ using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Extensions; using Jellyfin.Server.Infrastructure; using MediaBrowser.Common.Net; -using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Extensions; using Microsoft.AspNetCore.Builder; @@ -121,6 +121,8 @@ namespace Jellyfin.Server .AddCheck>(nameof(JellyfinDbContext)); services.AddHlsPlaylistGenerator(); + + services.AddHostedService(); } /// diff --git a/Jellyfin.sln b/Jellyfin.sln index 4385ac2417..30eab6cc21 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -87,6 +87,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.MediaEncoding.Hls. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.MediaEncoding.Keyframes.Tests", "tests\Jellyfin.MediaEncoding.Keyframes.Tests\Jellyfin.MediaEncoding.Keyframes.Tests.csproj", "{24960660-DE6C-47BF-AEEF-CEE8F19FE6C2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.LiveTv.Tests", "tests\Jellyfin.LiveTv.Tests\Jellyfin.LiveTv.Tests.csproj", "{C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.LiveTv", "src\Jellyfin.LiveTv\Jellyfin.LiveTv.csproj", "{8C6B2B13-58A4-4506-9DAB-1F882A093FE0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -233,6 +237,14 @@ Global {24960660-DE6C-47BF-AEEF-CEE8F19FE6C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {24960660-DE6C-47BF-AEEF-CEE8F19FE6C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {24960660-DE6C-47BF-AEEF-CEE8F19FE6C2}.Release|Any CPU.Build.0 = Release|Any CPU + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Release|Any CPU.Build.0 = Release|Any CPU + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -259,6 +271,8 @@ Global {08FFF49B-F175-4807-A2B5-73B0EBD9F716} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} {154872D9-6C12-4007-96E3-8F70A58386CE} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} {0A3FCC4D-C714-4072-B90F-E374A15F9FF9} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index 8362db1a71..eb181dcc4c 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -59,9 +59,8 @@ namespace MediaBrowser.Controller.Devices /// Gets the devices. /// /// The user's id, or null. - /// A value indicating whether the device supports sync, or null. /// IEnumerable<DeviceInfo>. - Task> GetDevicesForUser(Guid? userId, bool? supportsSync); + Task> GetDevicesForUser(Guid? userId); Task DeleteDevice(Device device); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 7c04fcbfc8..fdbceac961 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -773,8 +773,6 @@ namespace MediaBrowser.Controller.Entities /// The remote trailers. public IReadOnlyList RemoteTrailers { get; set; } - public virtual bool SupportsExternalTransfer => false; - public virtual double GetDefaultPrimaryImageAspectRatio() { return 0; diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index f1758a9d80..bace703ada 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -116,6 +117,14 @@ namespace MediaBrowser.Controller.Library /// An instance of . public ILiveStream GetLiveStreamInfoByUniqueId(string uniqueId); + /// + /// Gets the media sources for an active recording. + /// + /// The . + /// The . + /// A task containing the 's for the recording. + Task> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken); + /// /// Closes the media source. /// diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 3b6a16dee3..4206159e7b 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.LiveTv /// The services. IReadOnlyList Services { get; } - IListingsProvider[] ListingProviders { get; } + IReadOnlyList ListingProviders { get; } /// /// Gets the new timer defaults asynchronous. diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6a16d421cb..400e7f40fb 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1068,7 +1068,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // hw transpose filters should be added manually. - args.Append(" -autorotate 0"); + args.Append(" -noautorotate"); return args.ToString().Trim(); } @@ -1159,7 +1159,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isSwDecoder = string.IsNullOrEmpty(GetHardwareVideoDecoder(state, options)); if (!isSwDecoder && _mediaEncoder.EncoderVersion >= new Version(4, 4)) { - arg.Append(" -autoscale 0"); + arg.Append(" -noautoscale"); } return arg.ToString(); diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index fbad29143b..1c071067df 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -31,6 +31,8 @@ namespace MediaBrowser.Model.Configuration public bool EnableLUFSScan { get; set; } + public bool UseReplayGainTags { get; set; } + public bool EnableChapterImageExtraction { get; set; } public bool ExtractChapterImagesDuringLibraryScan { get; set; } diff --git a/MediaBrowser.Model/Dlna/DeviceIdentification.cs b/MediaBrowser.Model/Dlna/DeviceIdentification.cs deleted file mode 100644 index 6625b79814..0000000000 --- a/MediaBrowser.Model/Dlna/DeviceIdentification.cs +++ /dev/null @@ -1,63 +0,0 @@ -#pragma warning disable CS1591 - -using System; - -namespace MediaBrowser.Model.Dlna -{ - public class DeviceIdentification - { - /// - /// Gets or sets the name of the friendly. - /// - /// The name of the friendly. - public string FriendlyName { get; set; } = string.Empty; - - /// - /// Gets or sets the model number. - /// - /// The model number. - public string ModelNumber { get; set; } = string.Empty; - - /// - /// Gets or sets the serial number. - /// - /// The serial number. - public string SerialNumber { get; set; } = string.Empty; - - /// - /// Gets or sets the name of the model. - /// - /// The name of the model. - public string ModelName { get; set; } = string.Empty; - - /// - /// Gets or sets the model description. - /// - /// The model description. - public string ModelDescription { get; set; } = string.Empty; - - /// - /// Gets or sets the model URL. - /// - /// The model URL. - public string ModelUrl { get; set; } = string.Empty; - - /// - /// Gets or sets the manufacturer. - /// - /// The manufacturer. - public string Manufacturer { get; set; } = string.Empty; - - /// - /// Gets or sets the manufacturer URL. - /// - /// The manufacturer URL. - public string ManufacturerUrl { get; set; } = string.Empty; - - /// - /// Gets or sets the headers. - /// - /// The headers. - public HttpHeaderInfo[] Headers { get; set; } = Array.Empty(); - } -} diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 71d0896a70..2addebbfca 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -1,11 +1,7 @@ #pragma warning disable CA1819 // Properties should not return arrays + using System; -using System.ComponentModel; -using System.Linq; using System.Xml.Serialization; -using Jellyfin.Data.Enums; -using Jellyfin.Extensions; -using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Model.Dlna { @@ -17,7 +13,6 @@ namespace MediaBrowser.Model.Dlna /// the device is able to direct play (without transcoding or remuxing), /// as well as which containers/codecs to transcode to in case it isn't. /// - [XmlRoot("Profile")] public class DeviceProfile { /// @@ -31,104 +26,6 @@ namespace MediaBrowser.Model.Dlna [XmlIgnore] public string? Id { get; set; } - /// - /// Gets or sets the Identification. - /// - public DeviceIdentification? Identification { get; set; } - - /// - /// Gets or sets the friendly name of the device profile, which can be shown to users. - /// - public string? FriendlyName { get; set; } - - /// - /// Gets or sets the manufacturer of the device which this profile represents. - /// - public string? Manufacturer { get; set; } - - /// - /// Gets or sets an url for the manufacturer of the device which this profile represents. - /// - public string? ManufacturerUrl { get; set; } - - /// - /// Gets or sets the model name of the device which this profile represents. - /// - public string? ModelName { get; set; } - - /// - /// Gets or sets the model description of the device which this profile represents. - /// - public string? ModelDescription { get; set; } - - /// - /// Gets or sets the model number of the device which this profile represents. - /// - public string? ModelNumber { get; set; } - - /// - /// Gets or sets the ModelUrl. - /// - public string? ModelUrl { get; set; } - - /// - /// Gets or sets the serial number of the device which this profile represents. - /// - public string? SerialNumber { get; set; } - - /// - /// Gets or sets a value indicating whether EnableAlbumArtInDidl. - /// - [DefaultValue(false)] - public bool EnableAlbumArtInDidl { get; set; } - - /// - /// Gets or sets a value indicating whether EnableSingleAlbumArtLimit. - /// - [DefaultValue(false)] - public bool EnableSingleAlbumArtLimit { get; set; } - - /// - /// Gets or sets a value indicating whether EnableSingleSubtitleLimit. - /// - [DefaultValue(false)] - public bool EnableSingleSubtitleLimit { get; set; } - - /// - /// Gets or sets the SupportedMediaTypes. - /// - public string SupportedMediaTypes { get; set; } = "Audio,Photo,Video"; - - /// - /// Gets or sets the UserId. - /// - public string? UserId { get; set; } - - /// - /// Gets or sets the AlbumArtPn. - /// - public string? AlbumArtPn { get; set; } - - /// - /// Gets or sets the MaxAlbumArtWidth. - /// - public int? MaxAlbumArtWidth { get; set; } - - /// - /// Gets or sets the MaxAlbumArtHeight. - /// - public int? MaxAlbumArtHeight { get; set; } - - /// - /// Gets or sets the maximum allowed width of embedded icons. - /// - public int? MaxIconWidth { get; set; } - - /// - /// Gets or sets the maximum allowed height of embedded icons. - /// - public int? MaxIconHeight { get; set; } - /// /// Gets or sets the maximum allowed bitrate for all streamed content. /// @@ -149,51 +46,6 @@ namespace MediaBrowser.Model.Dlna /// public int? MaxStaticMusicBitrate { get; set; } = 8000000; - /// - /// Gets or sets the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace. - /// - public string? SonyAggregationFlags { get; set; } - - /// - /// Gets or sets the ProtocolInfo. - /// - public string? ProtocolInfo { get; set; } - - /// - /// Gets or sets the TimelineOffsetSeconds. - /// - [DefaultValue(0)] - public int TimelineOffsetSeconds { get; set; } - - /// - /// Gets or sets a value indicating whether RequiresPlainVideoItems. - /// - [DefaultValue(false)] - public bool RequiresPlainVideoItems { get; set; } - - /// - /// Gets or sets a value indicating whether RequiresPlainFolders. - /// - [DefaultValue(false)] - public bool RequiresPlainFolders { get; set; } - - /// - /// Gets or sets a value indicating whether EnableMSMediaReceiverRegistrar. - /// - [DefaultValue(false)] - public bool EnableMSMediaReceiverRegistrar { get; set; } - - /// - /// Gets or sets a value indicating whether IgnoreTranscodeByteRangeRequests. - /// - [DefaultValue(false)] - public bool IgnoreTranscodeByteRangeRequests { get; set; } - - /// - /// Gets or sets the XmlRootAttributes. - /// - public XmlAttribute[] XmlRootAttributes { get; set; } = Array.Empty(); - /// /// Gets or sets the direct play profiles. /// @@ -214,298 +66,9 @@ namespace MediaBrowser.Model.Dlna /// public CodecProfile[] CodecProfiles { get; set; } = Array.Empty(); - /// - /// Gets or sets the ResponseProfiles. - /// - public ResponseProfile[] ResponseProfiles { get; set; } = Array.Empty(); - /// /// Gets or sets the subtitle profiles. /// public SubtitleProfile[] SubtitleProfiles { get; set; } = Array.Empty(); - - /// - /// The GetSupportedMediaTypes. - /// - /// The . - public MediaType[] GetSupportedMediaTypes() - { - return ContainerProfile.SplitValue(SupportedMediaTypes) - .Select(m => Enum.TryParse(m, out var parsed) ? parsed : MediaType.Unknown) - .Where(m => m != MediaType.Unknown) - .ToArray(); - } - - /// - /// Gets the audio transcoding profile. - /// - /// The container. - /// The audio Codec. - /// A . - public TranscodingProfile? GetAudioTranscodingProfile(string? container, string? audioCodec) - { - container = (container ?? string.Empty).TrimStart('.'); - - foreach (var i in TranscodingProfiles) - { - if (i.Type != DlnaProfileType.Audio) - { - continue; - } - - if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - return i; - } - - return null; - } - - /// - /// Gets the video transcoding profile. - /// - /// The container. - /// The audio Codec. - /// The video Codec. - /// The . - public TranscodingProfile? GetVideoTranscodingProfile(string? container, string? audioCodec, string? videoCodec) - { - container = (container ?? string.Empty).TrimStart('.'); - - foreach (var i in TranscodingProfiles) - { - if (i.Type != DlnaProfileType.Video) - { - continue; - } - - if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (!string.Equals(videoCodec, i.VideoCodec, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - return i; - } - - return null; - } - - /// - /// Gets the audio media profile. - /// - /// The container. - /// The audio codec. - /// The audio channels. - /// The audio bitrate. - /// The audio sample rate. - /// The audio bit depth. - /// The . - public ResponseProfile? GetAudioMediaProfile(string? container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth) - { - foreach (var i in ResponseProfiles) - { - if (i.Type != DlnaProfileType.Audio) - { - continue; - } - - if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) - { - continue; - } - - var audioCodecs = i.GetAudioCodecs(); - if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var anyOff = false; - foreach (ProfileCondition c in i.Conditions) - { - if (!ConditionProcessor.IsAudioConditionSatisfied(GetModelProfileCondition(c), audioChannels, audioBitrate, audioSampleRate, audioBitDepth)) - { - anyOff = true; - break; - } - } - - if (anyOff) - { - continue; - } - - return i; - } - - return null; - } - - /// - /// Gets the model profile condition. - /// - /// The c. - /// The . - private ProfileCondition GetModelProfileCondition(ProfileCondition c) - { - return new ProfileCondition - { - Condition = c.Condition, - IsRequired = c.IsRequired, - Property = c.Property, - Value = c.Value - }; - } - - /// - /// Gets the image media profile. - /// - /// The container. - /// The width. - /// The height. - /// The . - public ResponseProfile? GetImageMediaProfile(string container, int? width, int? height) - { - foreach (var i in ResponseProfiles) - { - if (i.Type != DlnaProfileType.Photo) - { - continue; - } - - if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) - { - continue; - } - - var anyOff = false; - foreach (var c in i.Conditions) - { - if (!ConditionProcessor.IsImageConditionSatisfied(GetModelProfileCondition(c), width, height)) - { - anyOff = true; - break; - } - } - - if (anyOff) - { - continue; - } - - return i; - } - - return null; - } - - /// - /// Gets the video media profile. - /// - /// The container. - /// The audio codec. - /// The video codec. - /// The width. - /// The height. - /// The bit depth. - /// The video bitrate. - /// The video profile. - /// The video range type. - /// The video level. - /// The video framerate. - /// The packet length. - /// The timestamp. - /// True if anamorphic. - /// True if interlaced. - /// The ref frames. - /// The number of video streams. - /// The number of audio streams. - /// The video Codec tag. - /// True if Avc. - /// The . - public ResponseProfile? GetVideoMediaProfile( - string? container, - string? audioCodec, - string? videoCodec, - int? width, - int? height, - int? bitDepth, - int? videoBitrate, - string? videoProfile, - VideoRangeType videoRangeType, - double? videoLevel, - float? videoFramerate, - int? packetLength, - TransportStreamTimestamp timestamp, - bool? isAnamorphic, - bool? isInterlaced, - int? refFrames, - int? numVideoStreams, - int? numAudioStreams, - string? videoCodecTag, - bool? isAvc) - { - foreach (var i in ResponseProfiles) - { - if (i.Type != DlnaProfileType.Video) - { - continue; - } - - if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) - { - continue; - } - - var audioCodecs = i.GetAudioCodecs(); - if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var videoCodecs = i.GetVideoCodecs(); - if (videoCodecs.Length > 0 && !videoCodecs.Contains(videoCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var anyOff = false; - foreach (ProfileCondition c in i.Conditions) - { - if (!ConditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) - { - anyOff = true; - break; - } - } - - if (anyOff) - { - continue; - } - - return i; - } - - return null; - } } } diff --git a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs b/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs deleted file mode 100644 index 74c32c523e..0000000000 --- a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Dlna -{ - public class DeviceProfileInfo - { - /// - /// Gets or sets the identifier. - /// - /// The identifier. - public string Id { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - public string Name { get; set; } - - /// - /// Gets or sets the type. - /// - /// The type. - public DeviceProfileType Type { get; set; } - } -} diff --git a/MediaBrowser.Model/Dlna/DeviceProfileType.cs b/MediaBrowser.Model/Dlna/DeviceProfileType.cs deleted file mode 100644 index 46062abd09..0000000000 --- a/MediaBrowser.Model/Dlna/DeviceProfileType.cs +++ /dev/null @@ -1,10 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Dlna -{ - public enum DeviceProfileType - { - System = 0, - User = 1 - } -} diff --git a/MediaBrowser.Model/Dlna/HeaderMatchType.cs b/MediaBrowser.Model/Dlna/HeaderMatchType.cs deleted file mode 100644 index 2a9abb20eb..0000000000 --- a/MediaBrowser.Model/Dlna/HeaderMatchType.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Dlna -{ - public enum HeaderMatchType - { - Equals = 0, - Regex = 1, - Substring = 2 - } -} diff --git a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs deleted file mode 100644 index 17c4dffcc0..0000000000 --- a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System.Xml.Serialization; - -namespace MediaBrowser.Model.Dlna -{ - public class HttpHeaderInfo - { - [XmlAttribute("name")] - public string Name { get; set; } - - [XmlAttribute("value")] - public string Value { get; set; } - - [XmlAttribute("match")] - public HeaderMatchType Match { get; set; } - } -} diff --git a/MediaBrowser.Model/Dlna/ResponseProfile.cs b/MediaBrowser.Model/Dlna/ResponseProfile.cs deleted file mode 100644 index bf9661f7f3..0000000000 --- a/MediaBrowser.Model/Dlna/ResponseProfile.cs +++ /dev/null @@ -1,51 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System; -using System.Xml.Serialization; - -namespace MediaBrowser.Model.Dlna -{ - public class ResponseProfile - { - public ResponseProfile() - { - Conditions = Array.Empty(); - } - - [XmlAttribute("container")] - public string Container { get; set; } - - [XmlAttribute("audioCodec")] - public string AudioCodec { get; set; } - - [XmlAttribute("videoCodec")] - public string VideoCodec { get; set; } - - [XmlAttribute("type")] - public DlnaProfileType Type { get; set; } - - [XmlAttribute("orgPn")] - public string OrgPn { get; set; } - - [XmlAttribute("mimeType")] - public string MimeType { get; set; } - - public ProfileCondition[] Conditions { get; set; } - - public string[] GetContainers() - { - return ContainerProfile.SplitValue(Container); - } - - public string[] GetAudioCodecs() - { - return ContainerProfile.SplitValue(AudioCodec); - } - - public string[] GetVideoCodecs() - { - return ContainerProfile.SplitValue(VideoCodec); - } - } -} diff --git a/MediaBrowser.Model/Dlna/XmlAttribute.cs b/MediaBrowser.Model/Dlna/XmlAttribute.cs deleted file mode 100644 index 03bb2e4b11..0000000000 --- a/MediaBrowser.Model/Dlna/XmlAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System.Xml.Serialization; - -namespace MediaBrowser.Model.Dlna -{ - /// - /// Defines the . - /// - public class XmlAttribute - { - /// - /// Gets or sets the name of the attribute. - /// - [XmlAttribute("name")] - public string Name { get; set; } - - /// - /// Gets or sets the value of the attribute. - /// - [XmlAttribute("value")] - public string Value { get; set; } - } -} diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index d257eab920..cfff717db2 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -85,11 +85,6 @@ namespace MediaBrowser.Model.Dto public string PreferredMetadataCountryCode { get; set; } - /// - /// Gets or sets a value indicating whether [supports synchronize]. - /// - public bool? SupportsSync { get; set; } - public string Container { get; set; } /// diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 34642b83aa..ae4a008bb3 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Globalization; using System.Linq; using System.Text; @@ -214,6 +215,27 @@ namespace MediaBrowser.Model.Entities } } + /// + /// Gets the audio spatial format. + /// + /// The audio spatial format. + [DefaultValue(AudioSpatialFormat.None)] + public AudioSpatialFormat AudioSpatialFormat + { + get + { + if (Type != MediaStreamType.Audio || string.IsNullOrEmpty(Profile)) + { + return AudioSpatialFormat.None; + } + + return + Profile.Contains("Dolby Atmos", StringComparison.OrdinalIgnoreCase) ? AudioSpatialFormat.DolbyAtmos : + Profile.Contains("DTS:X", StringComparison.OrdinalIgnoreCase) ? AudioSpatialFormat.DTSX : + AudioSpatialFormat.None; + } + } + public string LocalizedUndefined { get; set; } public string LocalizedDefault { get; set; } diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 242a1c6e99..49d7c0bcb0 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -175,13 +175,6 @@ namespace MediaBrowser.Model.Querying /// Studios, - BasicSyncInfo, - - /// - /// The synchronize information. - /// - SyncInfo, - /// /// The taglines of the item. /// diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index 7fefce9cd5..597845fc17 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -23,14 +23,8 @@ namespace MediaBrowser.Model.Session public bool SupportsMediaControl { get; set; } - public bool SupportsContentUploading { get; set; } - - public string MessageCallbackUrl { get; set; } - public bool SupportsPersistentIdentifier { get; set; } - public bool SupportsSync { get; set; } - public DeviceProfile DeviceProfile { get; set; } public string AppStoreUrl { get; set; } diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs index d817042274..f718325df5 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs @@ -61,6 +61,9 @@ namespace MediaBrowser.Providers.MediaInfo [GeneratedRegex(@"I:\s+(.*?)\s+LUFS")] private static partial Regex LUFSRegex(); + [GeneratedRegex(@"REPLAYGAIN_TRACK_GAIN:\s+-?([0-9.]+)\s+dB")] + private static partial Regex ReplayGainTagRegex(); + /// /// Probes the specified item for metadata. /// @@ -104,8 +107,50 @@ namespace MediaBrowser.Providers.MediaInfo } var libraryOptions = _libraryManager.GetLibraryOptions(item); + bool foundLUFSValue = false; + + if (libraryOptions.UseReplayGainTags) + { + using (var process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = _mediaEncoder.ProbePath, + Arguments = $"-hide_banner -i \"{path}\"", + RedirectStandardOutput = false, + RedirectStandardError = true + }, + }) + { + try + { + process.Start(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error starting ffmpeg"); + + throw; + } + + using var reader = process.StandardError; + var output = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + Match split = ReplayGainTagRegex().Match(output); + + if (split.Success) + { + item.LUFS = DefaultLUFSValue - float.Parse(split.Groups[1].ValueSpan, CultureInfo.InvariantCulture.NumberFormat); + foundLUFSValue = true; + } + else + { + item.LUFS = DefaultLUFSValue; + } + } + } - if (libraryOptions.EnableLUFSScan) + if (libraryOptions.EnableLUFSScan && !foundLUFSValue) { using (var process = new Process() { @@ -144,7 +189,8 @@ namespace MediaBrowser.Providers.MediaInfo } } } - else + + if (!libraryOptions.EnableLUFSScan && !libraryOptions.UseReplayGainTags) { item.LUFS = DefaultLUFSValue; } diff --git a/README.md b/README.md index 15dd0ae679..62ef21334d 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,17 @@ cd Jellyfin.Server/bin/Debug/net8.0 # Change into the build output directory 2. Execute the build output. On Linux, Mac, etc. use `./jellyfin` and on Windows use `jellyfin.exe`. +### Running from GH-Codespaces + +As Jellyfin will run on a container on a github hosted server, JF needs to handle some things differently. +**NOTE:** If you want to access the JF instance from outside, like with a WebClient on another PC, remember to set the "ports" in the lower VsCode window to public. + +#### FFmpeg installation. +Because sometimes you need FFMPEG to test certain cases, follow the instructions from the wiki on the dev enviorment: +https://jellyfin.org/docs/general/installation/linux/#ffmpeg-installation + +**NOTE:** When first opening the server instance with any WebUI, you will be send to the login instead of the setup page. Refresh the login page once and you should be redirected to the Setup. + ### Running The Tests This repository also includes unit tests that are used to validate functionality as part of a CI pipeline on Azure. There are several ways to run these tests. diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000000..54e660f9c9 --- /dev/null +++ b/nuget.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/src/Jellyfin.LiveTv/Channels/ChannelDynamicMediaSourceProvider.cs similarity index 96% rename from Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs rename to src/Jellyfin.LiveTv/Channels/ChannelDynamicMediaSourceProvider.cs index 3e149cc82c..839549ed67 100644 --- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelDynamicMediaSourceProvider.cs @@ -8,7 +8,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// A media source provider for channels. diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/src/Jellyfin.LiveTv/Channels/ChannelImageProvider.cs similarity index 97% rename from Emby.Server.Implementations/Channels/ChannelImageProvider.cs rename to src/Jellyfin.LiveTv/Channels/ChannelImageProvider.cs index 25cbfcf146..32e224550f 100644 --- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelImageProvider.cs @@ -7,7 +7,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// An image provider for channels. diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs similarity index 97% rename from Emby.Server.Implementations/Channels/ChannelManager.cs rename to src/Jellyfin.LiveTv/Channels/ChannelManager.cs index 8279acb058..f5ce75ff4d 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs @@ -34,7 +34,7 @@ using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; using Season = MediaBrowser.Controller.Entities.TV.Season; using Series = MediaBrowser.Controller.Entities.TV.Series; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// The LiveTV channel manager. @@ -812,11 +812,16 @@ namespace Emby.Server.Implementations.Channels { if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - await using FileStream jsonStream = AsyncFile.OpenRead(cachePath); - var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); - if (cachedResult is not null) + var jsonStream = AsyncFile.OpenRead(cachePath); + await using (jsonStream.ConfigureAwait(false)) { - return null; + var cachedResult = await JsonSerializer + .DeserializeAsync(jsonStream, _jsonOptions, cancellationToken) + .ConfigureAwait(false); + if (cachedResult is not null) + { + return null; + } } } } @@ -835,11 +840,16 @@ namespace Emby.Server.Implementations.Channels { if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - await using FileStream jsonStream = AsyncFile.OpenRead(cachePath); - var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); - if (cachedResult is not null) + var jsonStream = AsyncFile.OpenRead(cachePath); + await using (jsonStream.ConfigureAwait(false)) { - return null; + var cachedResult = await JsonSerializer + .DeserializeAsync(jsonStream, _jsonOptions, cancellationToken) + .ConfigureAwait(false); + if (cachedResult is not null) + { + return null; + } } } } @@ -867,7 +877,7 @@ namespace Emby.Server.Implementations.Channels throw new InvalidOperationException("Channel returned a null result from GetChannelItems"); } - await CacheResponse(result, cachePath); + await CacheResponse(result, cachePath).ConfigureAwait(false); return result; } @@ -883,8 +893,11 @@ namespace Emby.Server.Implementations.Channels { Directory.CreateDirectory(Path.GetDirectoryName(path)); - await using FileStream createStream = File.Create(path); - await JsonSerializer.SerializeAsync(createStream, result, _jsonOptions).ConfigureAwait(false); + var createStream = File.Create(path); + await using (createStream.ConfigureAwait(false)) + { + await JsonSerializer.SerializeAsync(createStream, result, _jsonOptions).ConfigureAwait(false); + } } catch (Exception ex) { diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/src/Jellyfin.LiveTv/Channels/ChannelPostScanTask.cs similarity index 98% rename from Emby.Server.Implementations/Channels/ChannelPostScanTask.cs rename to src/Jellyfin.LiveTv/Channels/ChannelPostScanTask.cs index b358ba4d55..b4f6cf731e 100644 --- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelPostScanTask.cs @@ -8,7 +8,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// A task to remove all non-installed channels from the database. diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs similarity index 98% rename from Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs rename to src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs index cfd08e6535..556e052d4e 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs @@ -9,7 +9,7 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// The "Refresh Channels" scheduled task. diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/src/Jellyfin.LiveTv/EmbyTV/DirectRecorder.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs rename to src/Jellyfin.LiveTv/EmbyTV/DirectRecorder.cs index 7df66d3585..2a25218b63 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/DirectRecorder.cs @@ -5,7 +5,6 @@ using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Api.Helpers; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Streaming; @@ -13,7 +12,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public sealed class DirectRecorder : IRecorder { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs rename to src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs index abe3ff349f..439ed965b0 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs @@ -14,14 +14,12 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using Emby.Server.Implementations.Library; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; -using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -39,7 +37,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Providers; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public sealed class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable { @@ -47,7 +45,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private const int TunerDiscoveryDurationMs = 3000; - private readonly IServerApplicationHost _appHost; private readonly ILogger _logger; private readonly IHttpClientFactory _httpClientFactory; private readonly IServerConfigurationManager _config; @@ -76,7 +73,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private bool _disposed; public EmbyTV( - IServerApplicationHost appHost, IStreamHelper streamHelper, IMediaSourceManager mediaSourceManager, ILogger logger, @@ -91,7 +87,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { Current = this; - _appHost = appHost; _logger = logger; _httpClientFactory = httpClientFactory; _config = config; @@ -1021,35 +1016,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV throw new NotImplementedException(); } - public async Task> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken) - { - var stream = new MediaSourceInfo - { - EncoderPath = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveRecordings/" + info.Id + "/stream", - EncoderProtocol = MediaProtocol.Http, - Path = info.Path, - Protocol = MediaProtocol.File, - Id = info.Id, - SupportsDirectPlay = false, - SupportsDirectStream = true, - SupportsTranscoding = true, - IsInfiniteStream = true, - RequiresOpening = false, - RequiresClosing = false, - BufferMs = 0, - IgnoreDts = true, - IgnoreIndex = true - }; - - await new LiveStreamHelper(_mediaEncoder, _logger, _config.CommonApplicationPaths) - .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false); - - return new List - { - stream - }; - } - public Task CloseLiveStream(string id, CancellationToken cancellationToken) { return Task.CompletedTask; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/src/Jellyfin.LiveTv/EmbyTV/EncodedRecorder.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs rename to src/Jellyfin.LiveTv/EmbyTV/EncodedRecorder.cs index 9a9fd02739..132a5fc516 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EncodedRecorder.cs @@ -23,7 +23,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public class EncodedRecorder : IRecorder { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/src/Jellyfin.LiveTv/EmbyTV/EntryPoint.cs similarity index 87% rename from Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs rename to src/Jellyfin.LiveTv/EmbyTV/EntryPoint.cs index a2ec2df375..e750c05ac8 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EntryPoint.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Plugins; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public sealed class EntryPoint : IServerEntryPoint { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/src/Jellyfin.LiveTv/EmbyTV/EpgChannelData.cs similarity index 97% rename from Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs rename to src/Jellyfin.LiveTv/EmbyTV/EpgChannelData.cs index 20a8213a77..43d308c434 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EpgChannelData.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using MediaBrowser.Controller.LiveTv; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { internal class EpgChannelData { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/src/Jellyfin.LiveTv/EmbyTV/IRecorder.cs similarity index 95% rename from Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs rename to src/Jellyfin.LiveTv/EmbyTV/IRecorder.cs index de14d6d086..7ed42e2634 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/IRecorder.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public interface IRecorder : IDisposable { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/src/Jellyfin.LiveTv/EmbyTV/ItemDataProvider.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs rename to src/Jellyfin.LiveTv/EmbyTV/ItemDataProvider.cs index d5a6feb470..547ffeb668 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/ItemDataProvider.cs @@ -9,7 +9,7 @@ using System.Text.Json; using Jellyfin.Extensions.Json; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public class ItemDataProvider where T : class diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs b/src/Jellyfin.LiveTv/EmbyTV/NfoConfigurationExtensions.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs rename to src/Jellyfin.LiveTv/EmbyTV/NfoConfigurationExtensions.cs index 83f5e84131..e8570f0e0d 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/NfoConfigurationExtensions.cs @@ -1,7 +1,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Configuration; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { /// /// Class containing extension methods for working with the nfo configuration. diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/src/Jellyfin.LiveTv/EmbyTV/RecordingHelper.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs rename to src/Jellyfin.LiveTv/EmbyTV/RecordingHelper.cs index 7bbeae866a..6bda231b24 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/RecordingHelper.cs @@ -5,7 +5,7 @@ using System.Globalization; using System.Text; using MediaBrowser.Controller.LiveTv; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { internal static class RecordingHelper { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/src/Jellyfin.LiveTv/EmbyTV/SeriesTimerManager.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs rename to src/Jellyfin.LiveTv/EmbyTV/SeriesTimerManager.cs index bf28f3b67d..2ebe60b296 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/SeriesTimerManager.cs @@ -4,7 +4,7 @@ using System; using MediaBrowser.Controller.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public class SeriesTimerManager : ItemDataProvider { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/src/Jellyfin.LiveTv/EmbyTV/TimerManager.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs rename to src/Jellyfin.LiveTv/EmbyTV/TimerManager.cs index 9f8441fa48..37b1fa14cc 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/TimerManager.cs @@ -10,7 +10,7 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public class TimerManager : ItemDataProvider { diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/src/Jellyfin.LiveTv/ExclusiveLiveStream.cs similarity index 96% rename from Emby.Server.Implementations/Library/ExclusiveLiveStream.cs rename to src/Jellyfin.LiveTv/ExclusiveLiveStream.cs index b1649afad5..9d442e20cc 100644 --- a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs +++ b/src/Jellyfin.LiveTv/ExclusiveLiveStream.cs @@ -1,5 +1,6 @@ #nullable disable +#pragma warning disable CA1711 #pragma warning disable CS1591 using System; @@ -10,7 +11,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; -namespace Emby.Server.Implementations.Library +namespace Jellyfin.LiveTv { public sealed class ExclusiveLiveStream : ILiveStream { diff --git a/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj b/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj new file mode 100644 index 0000000000..5a826a1da0 --- /dev/null +++ b/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj @@ -0,0 +1,23 @@ + + + net8.0 + true + + + + + <_Parameter1>Jellyfin.LiveTv.Tests + + + + + + + + + + + + + + diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs index 5be3a7488a..3b20cd160b 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs @@ -16,9 +16,9 @@ using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; +using Jellyfin.LiveTv.Listings.SchedulesDirectDtos; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.LiveTv; @@ -27,7 +27,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.Listings +namespace Jellyfin.LiveTv.Listings { public class SchedulesDirect : IListingsProvider, IDisposable { @@ -613,6 +613,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings // Response is automatically disposed in the calling function, // so dispose manually if not returning. +#pragma warning disable IDISP016, IDISP017 response.Dispose(); if (!enableRetry || (int)response.StatusCode >= 500) { @@ -621,6 +622,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings null, response.StatusCode); } +#pragma warning restore IDISP016, IDISP017 _tokens.Clear(); options.Headers.TryAddWithoutValidation("token", await GetToken(providerInfo, cancellationToken).ConfigureAwait(false)); diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs index 95ac996e01..c1a502fd5a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Broadcaster dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs index f6251b9ad8..0cc39f3bb5 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Caption dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CastDto.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CastDto.cs index 0b7a2c63ad..bdcf87fdac 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CastDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Cast dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs index 87c327ed82..4e0d740785 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Channel dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs index c19cd2e48d..5c624c2885 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Content rating dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs index f00c9accdd..6d3c79c185 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Crew dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DayDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DayDto.cs index 1a371965cf..094f9a3194 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DayDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Day dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs similarity index 87% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs index ca6ae7fb13..0063f4cc34 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Description 1_000 dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs similarity index 87% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs index 1577219ed2..1d9a18cc7f 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Description 100 dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs similarity index 90% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs index eaf4a340bd..75e91547bb 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Descriptions program dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs similarity index 81% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs index fbdfb1f716..28abe094ed 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Event details dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs index 6852d89d71..6eefc1744b 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Gracenote dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs index b9844562f3..a62ae61f91 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Headends dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs similarity index 95% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs index a1ae3ca6d4..21b595f244 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Image data dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs similarity index 94% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs index 3dc64e5d8a..856b7a89b0 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// The lineup dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs index f190817813..99f80ce8a1 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Lineups dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs index fecc55e037..d7836384e2 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Logo dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MapDto.cs similarity index 95% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MapDto.cs index ffd02d474b..ea583a1cea 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MapDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Map dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs similarity index 89% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs index 40faa493c5..cafc8e2738 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Metadata dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs similarity index 82% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs index 43f2901566..243ccff5c0 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Metadata programs dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs index 04560ab55d..1c5c5333ce 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs @@ -1,7 +1,7 @@ using System; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Metadata schedule dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs index 31bef423b2..aea7408336 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Movie dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs index e8b15dc07c..328cefadc3 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Multipart dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs index 84c48f67f3..8c3906f863 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Program details dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs similarity index 97% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs index 60389b45bf..527a6f8a19 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Program dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs index c5ddcf7c51..61496155ac 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Quality rating dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs similarity index 85% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs index e04b619a4d..287cd4ed5c 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Rating dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs index c8f79fd1c2..d380ec7aee 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Recommendation dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs similarity index 89% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs index 0cd05709b3..6fc695a393 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Request schedule for channel dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs similarity index 88% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs index 84e224b71e..523900a96a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Show image dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/StationDto.cs similarity index 96% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/StationDto.cs index d797fd49b1..dbde1e117d 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/StationDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Station dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs similarity index 80% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs index 61cd4a9b00..146124f987 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Title dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs similarity index 94% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs index afb9994869..b3bc618376 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs @@ -1,7 +1,7 @@ using System; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// The token dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/src/Jellyfin.LiveTv/Listings/XmlTvListingsProvider.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs rename to src/Jellyfin.LiveTv/Listings/XmlTvListingsProvider.cs index e60e9dcc1c..cecc363f07 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/src/Jellyfin.LiveTv/Listings/XmlTvListingsProvider.cs @@ -23,7 +23,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.Listings +namespace Jellyfin.LiveTv.Listings { public class XmlTvListingsProvider : IListingsProvider { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs rename to src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs index 098f193fba..ddbf6345c5 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs +++ b/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.LiveTv; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { /// /// implementation for . diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/src/Jellyfin.LiveTv/LiveTvDtoService.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs rename to src/Jellyfin.LiveTv/LiveTvDtoService.cs index 9326fbd5c7..7c7c26eb4a 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/src/Jellyfin.LiveTv/LiveTvDtoService.cs @@ -20,7 +20,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { public class LiveTvDtoService { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/src/Jellyfin.LiveTv/LiveTvManager.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/LiveTvManager.cs rename to src/Jellyfin.LiveTv/LiveTvManager.cs index 0544e2a4b7..4fc9956538 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/src/Jellyfin.LiveTv/LiveTvManager.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.Library; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; @@ -34,7 +33,7 @@ using MediaBrowser.Model.Querying; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { /// /// Class LiveTvManager. @@ -105,9 +104,9 @@ namespace Emby.Server.Implementations.LiveTv /// The services. public IReadOnlyList Services => _services; - public ITunerHost[] TunerHosts => _tunerHosts; + public IReadOnlyList TunerHosts => _tunerHosts; - public IListingsProvider[] ListingProviders => _listingProviders; + public IReadOnlyList ListingProviders => _listingProviders; private LiveTvOptions GetConfiguration() { @@ -231,7 +230,9 @@ namespace Emby.Server.Implementations.LiveTv _logger.LogInformation("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId); MediaSourceInfo info; +#pragma warning disable CA1859 // TODO: Analyzer bug? ILiveStream liveStream; +#pragma warning restore CA1859 if (service is ISupportsDirectStreamProvider supportsManagedStream) { liveStream = await supportsManagedStream.GetChannelStreamWithDirectStreamProvider(channel.ExternalId, mediaSourceId, currentLiveStreams, cancellationToken).ConfigureAwait(false); @@ -1095,7 +1096,6 @@ namespace Emby.Server.Implementations.LiveTv // Load these now which will prefetch metadata var dtoOptions = new DtoOptions(); var fields = dtoOptions.Fields.ToList(); - fields.Remove(ItemFields.BasicSyncInfo); dtoOptions.Fields = fields.ToArray(); progress.Report(100); diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/src/Jellyfin.LiveTv/LiveTvMediaSourceProvider.cs similarity index 96% rename from Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs rename to src/Jellyfin.LiveTv/LiveTvMediaSourceProvider.cs index 6a92fc5997..ce9361089c 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/src/Jellyfin.LiveTv/LiveTvMediaSourceProvider.cs @@ -16,7 +16,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { public class LiveTvMediaSourceProvider : IMediaSourceProvider { @@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.LiveTv { if (activeRecordingInfo is not null) { - sources = await EmbyTV.EmbyTV.Current.GetRecordingStreamMediaSources(activeRecordingInfo, cancellationToken) + sources = await _mediaSourceManager.GetRecordingStreamMediaSources(activeRecordingInfo, cancellationToken) .ConfigureAwait(false); } else diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/src/Jellyfin.LiveTv/RecordingNotifier.cs similarity index 98% rename from Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs rename to src/Jellyfin.LiveTv/RecordingNotifier.cs index e0ca02d986..2923948ebf 100644 --- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs +++ b/src/Jellyfin.LiveTv/RecordingNotifier.cs @@ -15,7 +15,7 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.EntryPoints +namespace Jellyfin.LiveTv { public sealed class RecordingNotifier : IServerEntryPoint { diff --git a/Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs b/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs rename to src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs index 72bbdd14a6..e58296a70a 100644 --- a/Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs +++ b/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs @@ -7,7 +7,7 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Tasks; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { /// /// The "Refresh Guide" scheduled task. diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/src/Jellyfin.LiveTv/StreamHelper.cs similarity index 99% rename from Emby.Server.Implementations/IO/StreamHelper.cs rename to src/Jellyfin.LiveTv/StreamHelper.cs index 6eaf22ce48..ab4b6e9b15 100644 --- a/Emby.Server.Implementations/IO/StreamHelper.cs +++ b/src/Jellyfin.LiveTv/StreamHelper.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; -namespace Emby.Server.Implementations.IO +namespace Jellyfin.LiveTv { public class StreamHelper : IStreamHelper { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs rename to src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs index da597056a4..769f196bdf 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs @@ -19,7 +19,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public abstract class BaseTunerHost { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/Channels.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/Channels.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/Channels.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/Channels.cs index 0f04531896..311a71d137 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/Channels.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/Channels.cs @@ -1,6 +1,6 @@ #nullable disable -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { internal class Channels { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs index 39b3571422..3ece181f25 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs @@ -2,7 +2,7 @@ using System; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { internal class DiscoverResponse { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs index aae33503fa..50a887826a 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public class HdHomerunChannelCommands : IHdHomerunChannelCommands { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 79e15a82e2..b1b08e992b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -30,7 +30,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost { @@ -143,7 +143,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) { const string DefaultValue = "HDHR"; - var response = new DiscoverResponse + var discoverResponse = new DiscoverResponse { ModelNumber = DefaultValue }; @@ -152,11 +152,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // HDHR4 doesn't have this api lock (_modelCache) { - _modelCache[cacheKey] = response; + _modelCache[cacheKey] = discoverResponse; } } - return response; + return discoverResponse; } throw; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 68383a5547..8613387270 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; using MediaBrowser.Common; using MediaBrowser.Controller.LiveTv; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public sealed class HdHomerunManager : IDisposable { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 6195c7648f..6c8cde62c9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -1,5 +1,6 @@ #nullable disable +#pragma warning disable CA1711 #pragma warning disable CS1591 using System; @@ -19,7 +20,7 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs similarity index 75% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs index 11bd40ab10..9fcf386f9c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public interface IHdHomerunChannelCommands { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs similarity index 94% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs index 654474e971..6dc9c885f6 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public partial class LegacyHdHomerunChannelCommands : IHdHomerunChannelCommands { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/src/Jellyfin.LiveTv/TunerHosts/LiveStream.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs rename to src/Jellyfin.LiveTv/TunerHosts/LiveStream.cs index c18594a29b..70d8afc5d0 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/LiveStream.cs @@ -1,5 +1,6 @@ #nullable disable +#pragma warning disable CA1711 #pragma warning disable CS1591 using System; @@ -14,7 +15,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public class LiveStream : ILiveStream { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs rename to src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs index 11bf03b182..7235e65b64 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs @@ -24,7 +24,7 @@ using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs rename to src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs index 0b5575b994..5900d1c5be 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs @@ -18,7 +18,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public partial class M3uParser { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/src/Jellyfin.LiveTv/TunerHosts/SharedHttpStream.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs rename to src/Jellyfin.LiveTv/TunerHosts/SharedHttpStream.cs index efb84a5152..5ef04ad9e9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/SharedHttpStream.cs @@ -1,3 +1,4 @@ +#pragma warning disable CA1711 #pragma warning disable CS1591 using System; @@ -16,7 +17,7 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public class SharedHttpStream : LiveStream, IDirectStreamProvider { diff --git a/src/Jellyfin.Networking/AutoDiscoveryHost.cs b/src/Jellyfin.Networking/AutoDiscoveryHost.cs new file mode 100644 index 0000000000..5624c4ed13 --- /dev/null +++ b/src/Jellyfin.Networking/AutoDiscoveryHost.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller; +using MediaBrowser.Model.ApiClient; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Networking; + +/// +/// responsible for responding to auto-discovery messages. +/// +public sealed class AutoDiscoveryHost : BackgroundService +{ + /// + /// The port to listen on for auto-discovery messages. + /// + private const int PortNumber = 7359; + + private readonly ILogger _logger; + private readonly IServerApplicationHost _appHost; + private readonly IConfigurationManager _configurationManager; + private readonly INetworkManager _networkManager; + + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . + /// The . + public AutoDiscoveryHost( + ILogger logger, + IServerApplicationHost appHost, + IConfigurationManager configurationManager, + INetworkManager networkManager) + { + _logger = logger; + _appHost = appHost; + _configurationManager = configurationManager; + _networkManager = networkManager; + } + + /// + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + var networkConfig = _configurationManager.GetNetworkConfiguration(); + if (!networkConfig.AutoDiscovery) + { + return; + } + + var udpServers = new List(); + // Linux needs to bind to the broadcast addresses to receive broadcast traffic + if (OperatingSystem.IsLinux() && networkConfig.EnableIPv4) + { + udpServers.Add(ListenForAutoDiscoveryMessage(IPAddress.Broadcast, stoppingToken)); + } + + udpServers.AddRange(_networkManager.GetInternalBindAddresses() + .Select(intf => ListenForAutoDiscoveryMessage( + OperatingSystem.IsLinux() && intf.AddressFamily == AddressFamily.InterNetwork + ? NetworkUtils.GetBroadcastAddress(intf.Subnet) + : intf.Address, + stoppingToken))); + + await Task.WhenAll(udpServers).ConfigureAwait(false); + } + + private async Task ListenForAutoDiscoveryMessage(IPAddress address, CancellationToken cancellationToken) + { + using var udpClient = new UdpClient(new IPEndPoint(address, PortNumber)); + udpClient.MulticastLoopback = false; + + while (!cancellationToken.IsCancellationRequested) + { + try + { + var result = await udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); + var text = Encoding.UTF8.GetString(result.Buffer); + if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase)) + { + await RespondToV2Message(udpClient, result.RemoteEndPoint, cancellationToken).ConfigureAwait(false); + } + } + catch (SocketException ex) + { + _logger.LogError(ex, "Failed to receive data from socket"); + } + catch (OperationCanceledException) + { + _logger.LogDebug("Broadcast socket operation cancelled"); + } + } + } + + private async Task RespondToV2Message(UdpClient udpClient, IPEndPoint endpoint, CancellationToken cancellationToken) + { + var localUrl = _appHost.GetSmartApiUrl(endpoint.Address); + if (string.IsNullOrEmpty(localUrl)) + { + _logger.LogWarning("Unable to respond to server discovery request because the local ip address could not be determined."); + return; + } + + var response = new ServerDiscoveryInfo(localUrl, _appHost.SystemId, _appHost.FriendlyName); + + try + { + _logger.LogDebug("Sending AutoDiscovery response"); + await udpClient + .SendAsync(JsonSerializer.SerializeToUtf8Bytes(response).AsMemory(), endpoint, cancellationToken) + .ConfigureAwait(false); + } + catch (SocketException ex) + { + _logger.LogError(ex, "Error sending response message"); + } + } +} diff --git a/src/Jellyfin.Networking/Udp/UdpServer.cs b/src/Jellyfin.Networking/Udp/UdpServer.cs deleted file mode 100644 index b130a5a5ff..0000000000 --- a/src/Jellyfin.Networking/Udp/UdpServer.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller; -using MediaBrowser.Model.ApiClient; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; - -namespace Jellyfin.Networking.Udp; - -/// -/// Provides a Udp Server. -/// -public sealed class UdpServer : IDisposable -{ - /// - /// The _logger. - /// - private readonly ILogger _logger; - private readonly IServerApplicationHost _appHost; - private readonly IConfiguration _config; - - private readonly byte[] _receiveBuffer = new byte[8192]; - - private readonly Socket _udpSocket; - private readonly IPEndPoint _endpoint; - private bool _disposed; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// The application host. - /// The configuration manager. - /// The bind address. - /// The port. - public UdpServer( - ILogger logger, - IServerApplicationHost appHost, - IConfiguration configuration, - IPAddress bindAddress, - int port) - { - _logger = logger; - _appHost = appHost; - _config = configuration; - - _endpoint = new IPEndPoint(bindAddress, port); - - _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp) - { - MulticastLoopback = false, - }; - _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - } - - private async Task RespondToV2Message(EndPoint endpoint, CancellationToken cancellationToken) - { - string? localUrl = _config[AddressOverrideKey]; - if (string.IsNullOrEmpty(localUrl)) - { - localUrl = _appHost.GetSmartApiUrl(((IPEndPoint)endpoint).Address); - } - - if (string.IsNullOrEmpty(localUrl)) - { - _logger.LogWarning("Unable to respond to server discovery request because the local ip address could not be determined."); - return; - } - - var response = new ServerDiscoveryInfo(localUrl, _appHost.SystemId, _appHost.FriendlyName); - - try - { - _logger.LogDebug("Sending AutoDiscovery response"); - await _udpSocket.SendToAsync(JsonSerializer.SerializeToUtf8Bytes(response), SocketFlags.None, endpoint, cancellationToken).ConfigureAwait(false); - } - catch (SocketException ex) - { - _logger.LogError(ex, "Error sending response message"); - } - } - - /// - /// Starts the specified port. - /// - /// The cancellation token to cancel operation. - public void Start(CancellationToken cancellationToken) - { - _udpSocket.Bind(_endpoint); - - _ = Task.Run(async () => await BeginReceiveAsync(cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); - } - - private async Task BeginReceiveAsync(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) - { - try - { - var endpoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0); - var result = await _udpSocket.ReceiveFromAsync(_receiveBuffer, endpoint, cancellationToken).ConfigureAwait(false); - var text = Encoding.UTF8.GetString(_receiveBuffer, 0, result.ReceivedBytes); - if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase)) - { - await RespondToV2Message(result.RemoteEndPoint, cancellationToken).ConfigureAwait(false); - } - } - catch (SocketException ex) - { - _logger.LogError(ex, "Failed to receive data from socket"); - } - catch (OperationCanceledException) - { - _logger.LogDebug("Broadcast socket operation cancelled"); - } - } - } - - /// - public void Dispose() - { - if (_disposed) - { - return; - } - - _udpSocket.Dispose(); - _disposed = true; - } -} diff --git a/src/Jellyfin.Networking/UdpServerEntryPoint.cs b/src/Jellyfin.Networking/UdpServerEntryPoint.cs deleted file mode 100644 index 61180c3c0f..0000000000 --- a/src/Jellyfin.Networking/UdpServerEntryPoint.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Networking.Udp; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Plugins; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager; - -namespace Jellyfin.Networking; - -/// -/// Class responsible for registering all UDP broadcast endpoints and their handlers. -/// -public sealed class UdpServerEntryPoint : IServerEntryPoint -{ - /// - /// The port of the UDP server. - /// - public const int PortNumber = 7359; - - /// - /// The logger. - /// - private readonly ILogger _logger; - private readonly IServerApplicationHost _appHost; - private readonly IConfiguration _config; - private readonly IConfigurationManager _configurationManager; - private readonly INetworkManager _networkManager; - - /// - /// The UDP server. - /// - private readonly List _udpServers; - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - private bool _disposed; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public UdpServerEntryPoint( - ILogger logger, - IServerApplicationHost appHost, - IConfiguration configuration, - IConfigurationManager configurationManager, - INetworkManager networkManager) - { - _logger = logger; - _appHost = appHost; - _config = configuration; - _configurationManager = configurationManager; - _networkManager = networkManager; - _udpServers = new List(); - } - - /// - public Task RunAsync() - { - ObjectDisposedException.ThrowIf(_disposed, this); - - if (!_configurationManager.GetNetworkConfiguration().AutoDiscovery) - { - return Task.CompletedTask; - } - - try - { - // Linux needs to bind to the broadcast addresses to get broadcast traffic - // Windows receives broadcast fine when binding to just the interface, it is unable to bind to broadcast addresses - if (OperatingSystem.IsLinux()) - { - // Add global broadcast listener - var server = new UdpServer(_logger, _appHost, _config, IPAddress.Broadcast, PortNumber); - server.Start(_cancellationTokenSource.Token); - _udpServers.Add(server); - - // Add bind address specific broadcast listeners - // IPv6 is currently unsupported - var validInterfaces = _networkManager.GetInternalBindAddresses().Where(i => i.AddressFamily == AddressFamily.InterNetwork); - foreach (var intf in validInterfaces) - { - var broadcastAddress = NetworkUtils.GetBroadcastAddress(intf.Subnet); - _logger.LogDebug("Binding UDP server to {Address} on port {PortNumber}", broadcastAddress, PortNumber); - - server = new UdpServer(_logger, _appHost, _config, broadcastAddress, PortNumber); - server.Start(_cancellationTokenSource.Token); - _udpServers.Add(server); - } - } - else - { - // Add bind address specific broadcast listeners - // IPv6 is currently unsupported - var validInterfaces = _networkManager.GetInternalBindAddresses().Where(i => i.AddressFamily == AddressFamily.InterNetwork); - foreach (var intf in validInterfaces) - { - var intfAddress = intf.Address; - _logger.LogDebug("Binding UDP server to {Address} on port {PortNumber}", intfAddress, PortNumber); - - var server = new UdpServer(_logger, _appHost, _config, intfAddress, PortNumber); - server.Start(_cancellationTokenSource.Token); - _udpServers.Add(server); - } - } - } - catch (SocketException ex) - { - _logger.LogWarning(ex, "Unable to start AutoDiscovery listener on UDP port {PortNumber}", PortNumber); - } - - return Task.CompletedTask; - } - - /// - public void Dispose() - { - if (_disposed) - { - return; - } - - _cancellationTokenSource.Cancel(); - _cancellationTokenSource.Dispose(); - foreach (var server in _udpServers) - { - server.Dispose(); - } - - _udpServers.Clear(); - _disposed = true; - } -} diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs b/tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs similarity index 98% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs rename to tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs index 13ac3ddb0f..cb6eb91844 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs @@ -6,13 +6,13 @@ using System.Threading; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; -using Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun; +using Jellyfin.LiveTv.TunerHosts.HdHomerun; using MediaBrowser.Model.LiveTv; using Moq; using Moq.Protected; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv +namespace Jellyfin.LiveTv.Tests { public class HdHomerunHostTests { diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs b/tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs similarity index 98% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs rename to tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs index fd499d9cf9..4ab0bd723c 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs @@ -1,9 +1,9 @@ using System; using System.Text; -using Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun; +using Jellyfin.LiveTv.TunerHosts.HdHomerun; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv +namespace Jellyfin.LiveTv.Tests { public class HdHomerunManagerTests { diff --git a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj new file mode 100644 index 0000000000..f645f38c44 --- /dev/null +++ b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj @@ -0,0 +1,29 @@ + + + net8.0 + + + + + PreserveNewest + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs b/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs similarity index 96% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs rename to tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs index 92b4178fdb..0fb7894e51 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs @@ -6,13 +6,13 @@ using System.Threading; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; -using Emby.Server.Implementations.LiveTv.Listings; +using Jellyfin.LiveTv.Listings; using MediaBrowser.Model.LiveTv; using Moq; using Moq.Protected; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv.Listings; +namespace Jellyfin.LiveTv.Tests.Listings; public class XmlTvListingsProviderTests { diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs b/tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs similarity index 97% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs rename to tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs index f107b1ef97..b4960dc0b3 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs @@ -1,9 +1,9 @@ using System; -using Emby.Server.Implementations.LiveTv.EmbyTV; +using Jellyfin.LiveTv.EmbyTV; using MediaBrowser.Controller.LiveTv; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv +namespace Jellyfin.LiveTv.Tests { public static class RecordingHelperTests { diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs b/tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs similarity index 98% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs rename to tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs index d4f28f327f..6975d56d9e 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs @@ -2,11 +2,11 @@ using System; using System.Collections.Generic; using System.IO; using System.Text.Json; -using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos; using Jellyfin.Extensions.Json; +using Jellyfin.LiveTv.Listings.SchedulesDirectDtos; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv.SchedulesDirect +namespace Jellyfin.LiveTv.Tests.SchedulesDirect { public class SchedulesDirectDeserializeTests { diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/10.10.10.100/discover.json b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/10.10.10.100/discover.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/10.10.10.100/discover.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/10.10.10.100/discover.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/10.10.10.100/lineup.json b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/10.10.10.100/lineup.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/10.10.10.100/lineup.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/10.10.10.100/lineup.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/192.168.1.182/discover.json b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/192.168.1.182/discover.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/192.168.1.182/discover.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/192.168.1.182/discover.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/192.168.1.182/lineup.json b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/192.168.1.182/lineup.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/192.168.1.182/lineup.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/192.168.1.182/lineup.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/headends_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/headends_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/headends_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/headends_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/lineup_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/lineup_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/lineup_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/lineup_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/lineups_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/lineups_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/lineups_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/lineups_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/metadata_programs_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/metadata_programs_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/metadata_programs_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/metadata_programs_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/programs_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/programs_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/programs_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/programs_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/schedules_request.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/schedules_request.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/schedules_request.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/schedules_request.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/schedules_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/schedules_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/schedules_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/schedules_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/token_live_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/token_live_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/token_live_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/token_live_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/token_offline_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/token_offline_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/token_offline_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/token_offline_response.json diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs index 198dc63efe..344ac8971c 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -46,7 +46,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing var internalMediaInfoResult = JsonSerializer.Deserialize(bytes, _jsonOptions); MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_metadata.mkv", MediaProtocol.File); - Assert.Single(res.MediaStreams); + Assert.Equal(3, res.MediaStreams.Count); Assert.NotNull(res.VideoStream); Assert.Equal("4:3", res.VideoStream.AspectRatio); @@ -83,6 +83,14 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Assert.Equal(1, res.VideoStream.BlPresentFlag); Assert.Equal(0, res.VideoStream.DvBlSignalCompatibilityId); + var audio1 = res.MediaStreams[1]; + Assert.Equal("eac3", audio1.Codec); + Assert.Equal(AudioSpatialFormat.DolbyAtmos, audio1.AudioSpatialFormat); + + var audio2 = res.MediaStreams[2]; + Assert.Equal("dts", audio2.Codec); + Assert.Equal(AudioSpatialFormat.DTSX, audio2.AudioSpatialFormat); + Assert.Empty(res.Chapters); Assert.Equal("Just color bars", res.Overview); } diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json index 519d81179c..a49c686900 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json @@ -61,6 +61,92 @@ "dv_bl_signal_compatibility_id": 0 } ] + }, + { + "index": 1, + "codec_name": "eac3", + "codec_long_name": "ATSC A/52B (AC-3, E-AC-3)", + "profile": "Dolby Digital Plus + Dolby Atmos", + "codec_type": "audio", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "sample_fmt": "fltp", + "sample_rate": "48000", + "channels": 6, + "channel_layout": "5.1(side)", + "bits_per_sample": 0, + "initial_padding": 0, + "r_frame_rate": "0/0", + "avg_frame_rate": "0/0", + "time_base": "1/1000", + "start_pts": 0, + "start_time": "0.000000", + "bit_rate": "640000", + "disposition": { + "default": 1, + "dub": 0, + "original": 1, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0, + "captions": 0, + "descriptions": 0, + "metadata": 0, + "dependent": 0, + "still_image": 0 + }, + "tags": { + "language": "eng" + } + }, + { + "index": 2, + "codec_name": "dts", + "codec_long_name": "DCA (DTS Coherent Acoustics)", + "profile": "DTS-HD MA + DTS:X", + "codec_type": "audio", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "sample_fmt": "s32p", + "sample_rate": "48000", + "channels": 8, + "channel_layout": "7.1", + "bits_per_sample": 0, + "initial_padding": 0, + "r_frame_rate": "0/0", + "avg_frame_rate": "0/0", + "time_base": "1/1000", + "start_pts": 0, + "start_time": "0.000000", + "bits_per_raw_sample": "24", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0, + "captions": 0, + "descriptions": 0, + "metadata": 0, + "dependent": 0, + "still_image": 0 + }, + "tags": { + "language": "eng" + } } ], "chapters": [ @@ -68,7 +154,7 @@ ], "format": { "filename": "some_metadata.mkv", - "nb_streams": 1, + "nb_streams": 3, "nb_programs": 0, "format_name": "matroska,webm", "format_long_name": "Matroska / WebM",