Merge branch 'master' into renovate/dotnet-monorepo
This commit is contained in:
commit
2d8b375a64
|
@ -35,14 +35,14 @@ jobs:
|
|||
packageType: sdk
|
||||
version: ${{ parameters.DotNetSdkVersion }}
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
- task: DotNetCoreCLI@2.210.0
|
||||
displayName: 'Install ABI CompatibilityChecker Tool'
|
||||
inputs:
|
||||
command: custom
|
||||
custom: tool
|
||||
arguments: 'update compatibilitychecker -g'
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
- task: DownloadPipelineArtifact@2.198.0
|
||||
displayName: 'Download New Assembly Build Artifact'
|
||||
inputs:
|
||||
source: 'current'
|
||||
|
@ -50,7 +50,7 @@ jobs:
|
|||
path: "$(System.ArtifactsDirectory)/new-artifacts"
|
||||
runVersion: "latest"
|
||||
|
||||
- task: CopyFiles@2
|
||||
- task: CopyFiles@2.211.0
|
||||
displayName: 'Copy New Assembly Build Artifact'
|
||||
inputs:
|
||||
sourceFolder: $(System.ArtifactsDirectory)/new-artifacts
|
||||
|
@ -60,7 +60,7 @@ jobs:
|
|||
overWrite: true
|
||||
flattenFolders: true
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
- task: DownloadPipelineArtifact@2.198.0
|
||||
displayName: 'Download Reference Assembly Build Artifact'
|
||||
enabled: false
|
||||
inputs:
|
||||
|
@ -72,7 +72,7 @@ jobs:
|
|||
runVersion: "latestFromBranch"
|
||||
runBranch: "refs/heads/$(System.PullRequest.TargetBranch)"
|
||||
|
||||
- task: CopyFiles@2
|
||||
- task: CopyFiles@2.211.0
|
||||
displayName: 'Copy Reference Assembly Build Artifact'
|
||||
enabled: false
|
||||
inputs:
|
||||
|
@ -83,7 +83,7 @@ jobs:
|
|||
overWrite: true
|
||||
flattenFolders: true
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
- task: DotNetCoreCLI@2.210.0
|
||||
displayName: 'Execute ABI Compatibility Check Tool'
|
||||
enabled: false
|
||||
inputs:
|
||||
|
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
submodules: true
|
||||
persistCredentials: true
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
- task: DownloadPipelineArtifact@2.198.0
|
||||
displayName: 'Download Web Branch'
|
||||
condition: in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')
|
||||
inputs:
|
||||
|
@ -31,7 +31,7 @@ jobs:
|
|||
pipeline: 'Jellyfin Web'
|
||||
runBranch: variables['Build.SourceBranch']
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
- task: DownloadPipelineArtifact@2.198.0
|
||||
displayName: 'Download Web Target'
|
||||
condition: eq(variables['Build.Reason'], 'PullRequest')
|
||||
inputs:
|
||||
|
@ -42,7 +42,7 @@ jobs:
|
|||
pipeline: 'Jellyfin Web'
|
||||
runBranch: variables['System.PullRequest.TargetBranch']
|
||||
|
||||
- task: ExtractFiles@1
|
||||
- task: ExtractFiles@1.211.0
|
||||
displayName: 'Extract Web Client'
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Agent.TempDirectory)/*.zip'
|
||||
|
@ -55,7 +55,7 @@ jobs:
|
|||
packageType: sdk
|
||||
version: ${{ parameters.DotNetSdkVersion }}
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
- task: DotNetCoreCLI@2.210.0
|
||||
displayName: 'Publish Server'
|
||||
inputs:
|
||||
command: publish
|
||||
|
|
|
@ -69,7 +69,7 @@ jobs:
|
|||
runOptions: 'inline'
|
||||
inline: 'mkdir -p /srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
|
||||
|
||||
- task: CopyFilesOverSSH@0
|
||||
- task: CopyFilesOverSSH@0.212.0
|
||||
displayName: 'Upload artifacts to repository server'
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
|
@ -90,7 +90,7 @@ jobs:
|
|||
displayName: Set release version (stable)
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
- task: DownloadPipelineArtifact@2.198.0
|
||||
displayName: 'Download OpenAPI Spec'
|
||||
inputs:
|
||||
source: 'current'
|
||||
|
@ -105,7 +105,7 @@ jobs:
|
|||
runOptions: 'inline'
|
||||
inline: 'mkdir -p /srv/repository/incoming/azure/$(Build.BuildNumber)'
|
||||
|
||||
- task: CopyFilesOverSSH@0
|
||||
- task: CopyFilesOverSSH@0.212.0
|
||||
displayName: 'Upload artifacts to repository server'
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
|
@ -137,7 +137,7 @@ jobs:
|
|||
displayName: Set release version (stable)
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||
|
||||
- task: Docker@2
|
||||
- task: Docker@2.211.0
|
||||
displayName: 'Push Unstable Image'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
|
@ -150,7 +150,7 @@ jobs:
|
|||
unstable-$(Build.BuildNumber)-$(BuildConfiguration)
|
||||
unstable-$(BuildConfiguration)
|
||||
|
||||
- task: Docker@2
|
||||
- task: Docker@2.211.0
|
||||
displayName: 'Push Stable Image'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||
inputs:
|
||||
|
@ -210,7 +210,7 @@ jobs:
|
|||
packageType: 'sdk'
|
||||
version: '6.0.x'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
- task: DotNetCoreCLI@2.210.0
|
||||
displayName: 'Build Stable Nuget packages'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||
inputs:
|
||||
|
@ -225,7 +225,7 @@ jobs:
|
|||
custom: 'pack'
|
||||
arguments: -o $(Build.ArtifactStagingDirectory) -p:Version=$(JellyfinVersion)
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
- task: DotNetCoreCLI@2.210.0
|
||||
displayName: 'Build Unstable Nuget packages'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
|
@ -256,7 +256,7 @@ jobs:
|
|||
publishFeedCredentials: 'NugetOrg'
|
||||
allowPackageConflicts: true # This ignores an error if the version already exists
|
||||
|
||||
- task: NuGetAuthenticate@0
|
||||
- task: NuGetAuthenticate@0.203.0
|
||||
displayName: 'Authenticate to unstable Nuget feed'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ jobs:
|
|||
organization: 'jellyfin'
|
||||
projectKey: 'jellyfin_jellyfin'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
- task: DotNetCoreCLI@2.210.0
|
||||
displayName: 'Run CLI Tests'
|
||||
inputs:
|
||||
command: "test"
|
||||
|
|
12
.github/workflows/automation.yml
vendored
12
.github/workflows/automation.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
if: ${{ github.repository == 'jellyfin/jellyfin' }}
|
||||
steps:
|
||||
- name: Apply label
|
||||
uses: eps1lon/actions-label-merge-conflict@v2.0.1
|
||||
uses: eps1lon/actions-label-merge-conflict@b8bf8341285ec9a4567d4318ba474fee998a6919 # tag=v2.0.1
|
||||
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request_target'}}
|
||||
with:
|
||||
dirtyLabel: 'merge conflict'
|
||||
|
@ -26,7 +26,7 @@ jobs:
|
|||
if: ${{ github.repository == 'jellyfin/jellyfin' }}
|
||||
steps:
|
||||
- name: Remove from 'Current Release' project
|
||||
uses: alex-page/github-project-automation-plus@v0.8.1
|
||||
uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
|
||||
if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
|
||||
continue-on-error: true
|
||||
with:
|
||||
|
@ -35,7 +35,7 @@ jobs:
|
|||
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
|
||||
- name: Add to 'Release Next' project
|
||||
uses: alex-page/github-project-automation-plus@v0.8.1
|
||||
uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
|
||||
if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened'
|
||||
continue-on-error: true
|
||||
with:
|
||||
|
@ -44,7 +44,7 @@ jobs:
|
|||
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
|
||||
- name: Add to 'Current Release' project
|
||||
uses: alex-page/github-project-automation-plus@v0.8.1
|
||||
uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
|
||||
if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
|
||||
continue-on-error: true
|
||||
with:
|
||||
|
@ -58,7 +58,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@v0.8.1
|
||||
uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
|
||||
if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1
|
||||
continue-on-error: true
|
||||
with:
|
||||
|
@ -67,7 +67,7 @@ jobs:
|
|||
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
|
||||
- name: Add issue to triage project
|
||||
uses: alex-page/github-project-automation-plus@v0.8.1
|
||||
uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
|
||||
if: github.event.issue.pull_request == '' && github.event.action == 'opened'
|
||||
continue-on-error: true
|
||||
with:
|
||||
|
|
10
.github/workflows/codeql-analysis.yml
vendored
10
.github/workflows/codeql-analysis.yml
vendored
|
@ -20,18 +20,18 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@4d4a70f4a5b2a5a5329f13be4ac933f2c9206ac0 # tag=v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@cc7986c02bac29104a72998e67239bb5ee2ee110 # tag=v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-extended
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@cc7986c02bac29104a72998e67239bb5ee2ee110 # tag=v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@cc7986c02bac29104a72998e67239bb5ee2ee110 # tag=v2
|
||||
|
|
16
.github/workflows/commands.yml
vendored
16
.github/workflows/commands.yml
vendored
|
@ -16,20 +16,20 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Notify as seen
|
||||
uses: peter-evans/create-or-update-comment@v2
|
||||
uses: peter-evans/create-or-update-comment@2b2c85d0bf1b8a7b4e7e344bd5c71dc4b9196e9f # tag=v2
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
comment-id: ${{ github.event.comment.id }}
|
||||
reactions: '+1'
|
||||
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Automatic Rebase
|
||||
uses: cirrus-actions/rebase@1.7
|
||||
uses: cirrus-actions/rebase@6e572f08c244e2f04f9beb85a943eb618218714d # tag=1.7
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
|
||||
|
||||
|
@ -39,7 +39,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Notify as seen
|
||||
uses: peter-evans/create-or-update-comment@v2
|
||||
uses: peter-evans/create-or-update-comment@2b2c85d0bf1b8a7b4e7e344bd5c71dc4b9196e9f # tag=v2
|
||||
if: ${{ github.event.comment != null }}
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
|
@ -47,14 +47,14 @@ jobs:
|
|||
reactions: eyes
|
||||
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Notify as running
|
||||
id: comment_running
|
||||
uses: peter-evans/create-or-update-comment@v2
|
||||
uses: peter-evans/create-or-update-comment@2b2c85d0bf1b8a7b4e7e344bd5c71dc4b9196e9f # tag=v2
|
||||
if: ${{ github.event.comment != null }}
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
|
@ -89,7 +89,7 @@ jobs:
|
|||
exit ${retcode}
|
||||
|
||||
- name: Notify with result success
|
||||
uses: peter-evans/create-or-update-comment@v2
|
||||
uses: peter-evans/create-or-update-comment@2b2c85d0bf1b8a7b4e7e344bd5c71dc4b9196e9f # tag=v2
|
||||
if: ${{ github.event.comment != null && success() }}
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
|
@ -104,7 +104,7 @@ jobs:
|
|||
reactions: hooray
|
||||
|
||||
- name: Notify with result failure
|
||||
uses: peter-evans/create-or-update-comment@v2
|
||||
uses: peter-evans/create-or-update-comment@2b2c85d0bf1b8a7b4e7e344bd5c71dc4b9196e9f # tag=v2
|
||||
if: ${{ github.event.comment != null && failure() }}
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
|
|
22
.github/workflows/openapi.yml
vendored
22
.github/workflows/openapi.yml
vendored
|
@ -12,18 +12,18 @@ jobs:
|
|||
permissions: read-all
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@4d4a70f4a5b2a5a5329f13be4ac933f2c9206ac0 # tag=v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
- 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@v3
|
||||
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # tag=v3
|
||||
with:
|
||||
name: openapi-head
|
||||
retention-days: 14
|
||||
|
@ -37,17 +37,17 @@ jobs:
|
|||
permissions: read-all
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
|
||||
with:
|
||||
ref: ${{ github.base_ref }}
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@4d4a70f4a5b2a5a5329f13be4ac933f2c9206ac0 # tag=v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
- 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@v3
|
||||
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # tag=v3
|
||||
with:
|
||||
name: openapi-base
|
||||
retention-days: 14
|
||||
|
@ -63,12 +63,12 @@ jobs:
|
|||
- openapi-base
|
||||
steps:
|
||||
- name: Download openapi-head
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # tag=v3
|
||||
with:
|
||||
name: openapi-head
|
||||
path: openapi-head
|
||||
- name: Download openapi-base
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # tag=v3
|
||||
with:
|
||||
name: openapi-base
|
||||
path: openapi-base
|
||||
|
@ -90,14 +90,14 @@ jobs:
|
|||
body="${body//$'\r'/'%0D'}"
|
||||
echo ::set-output name=body::$body
|
||||
- name: Find difference comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
uses: peter-evans/find-comment@b657a70ff16d17651703a84bee1cb9ad9d2be2ea # tag=v2
|
||||
id: find-comment
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
direction: last
|
||||
body-includes: openapi-diff-workflow-comment
|
||||
- name: Reply or edit difference comment (changed)
|
||||
uses: peter-evans/create-or-update-comment@v2
|
||||
uses: peter-evans/create-or-update-comment@2b2c85d0bf1b8a7b4e7e344bd5c71dc4b9196e9f # tag=v2
|
||||
if: ${{ steps.read-diff.outputs.body != '' }}
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
|
@ -112,7 +112,7 @@ jobs:
|
|||
|
||||
</details>
|
||||
- name: Edit difference comment (unchanged)
|
||||
uses: peter-evans/create-or-update-comment@v2
|
||||
uses: peter-evans/create-or-update-comment@2b2c85d0bf1b8a7b4e7e344bd5c71dc4b9196e9f # tag=v2
|
||||
if: ${{ steps.read-diff.outputs.body == '' && steps.find-comment.outputs.comment-id != '' }}
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
|
|
2
.github/workflows/repo-stale.yaml
vendored
2
.github/workflows/repo-stale.yaml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: ${{ contains(github.repository, 'jellyfin/') }}
|
||||
steps:
|
||||
- uses: actions/stale@v6
|
||||
- uses: actions/stale@5ebf00ea0e4c1561e9b43a292ed34424fb1d4578 # tag=v6
|
||||
with:
|
||||
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
days-before-stale: 120
|
||||
|
|
|
@ -1088,15 +1088,7 @@ namespace Emby.Server.Implementations
|
|||
return GetLocalApiUrl(request.Host.Host, request.Scheme, requestPort);
|
||||
}
|
||||
|
||||
// Published server ends with a /
|
||||
if (!string.IsNullOrEmpty(PublishedServerUrl))
|
||||
{
|
||||
// Published server ends with a '/', so we need to remove it.
|
||||
return PublishedServerUrl.Trim('/');
|
||||
}
|
||||
|
||||
string smart = NetManager.GetBindInterface(request, out var port);
|
||||
return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port);
|
||||
return GetSmartApiUrl(request.HttpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.10" />
|
||||
<PackageReference Include="Mono.Nat" Version="3.0.3" />
|
||||
<PackageReference Include="Mono.Nat" Version="3.0.4" />
|
||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.3.0" />
|
||||
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
|
||||
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
"TasksChannelsCategory": "قنوات الإنترنت",
|
||||
"TasksLibraryCategory": "مكتبة",
|
||||
"TasksMaintenanceCategory": "صيانة",
|
||||
"TaskRefreshLibraryDescription": "يفصح مكتبة الوسائط الخاصة بك بحثًا عن ملفات جديدة، ومن ثم يتحدث البيانات الوصفية.",
|
||||
"TaskRefreshLibraryDescription": "يفحص مكتبة الوسائط الخاصة بك باحثا عن ملفات جديدة، ومن ثم يتحدث البيانات الوصفية.",
|
||||
"TaskRefreshLibrary": "افحص مكتبة الوسائط",
|
||||
"TaskRefreshChapterImagesDescription": "يُنشئ صور مصغرة لمقاطع الفيديو التي تحتوي على فصول.",
|
||||
"TaskRefreshChapterImages": "استخراج صور الفصل",
|
||||
|
|
|
@ -123,5 +123,6 @@
|
|||
"TaskOptimizeDatabase": "Optimalizovat databázi",
|
||||
"TaskKeyframeExtractorDescription": "Vytahuje klíčové snímky ze souborů videa za účelem vytváření přesnějších seznamů přehrávání HLS. Tento úkol může trvat velmi dlouho.",
|
||||
"TaskKeyframeExtractor": "Vytahovač klíčových snímků",
|
||||
"External": "Externí"
|
||||
"External": "Externí",
|
||||
"HearingImpaired": "Sluchově postižení"
|
||||
}
|
||||
|
|
|
@ -123,5 +123,6 @@
|
|||
"TaskOptimizeDatabase": "Optimise database",
|
||||
"TaskKeyframeExtractorDescription": "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time.",
|
||||
"TaskKeyframeExtractor": "Keyframe Extractor",
|
||||
"External": "External"
|
||||
"External": "External",
|
||||
"HearingImpaired": "Hearing Impaired"
|
||||
}
|
||||
|
|
|
@ -123,5 +123,6 @@
|
|||
"TaskOptimizeDatabase": "Optimización de base de datos",
|
||||
"External": "Externo",
|
||||
"TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reprodución HLS más precisas. Esta tarea puede durar mucho tiempo.",
|
||||
"TaskKeyframeExtractor": "Extractor de Fotogramas Clave"
|
||||
"TaskKeyframeExtractor": "Extractor de Fotogramas Clave",
|
||||
"HearingImpaired": "Personas con discapacidad auditiva"
|
||||
}
|
||||
|
|
|
@ -116,5 +116,12 @@
|
|||
"CameraImageUploadedFrom": "{0}-tik kamera irudi berri bat igo da",
|
||||
"AuthenticationSucceededWithUserName": "{0} ongi autentifikatu da",
|
||||
"Application": "Aplikazioa",
|
||||
"AppDeviceValues": "App: {0}, Gailua: {1}"
|
||||
"AppDeviceValues": "App: {0}, Gailua: {1}",
|
||||
"HearingImpaired": "Entzunaldia aldatua",
|
||||
"ProviderValue": "Hornitzailea: {0}",
|
||||
"TaskKeyframeExtractorDescription": "Bideo fitxategietako fotograma gakoak ateratzen ditu HLS erreprodukzio-zerrenda zehatzagoak sortzeko. Zeregin honek denbora asko iraun dezake.",
|
||||
"HeaderRecordingGroups": "Grabaketa taldeak",
|
||||
"Inherit": "Oinordetu",
|
||||
"TaskOptimizeDatabaseDescription": "Datu-basea trinkotu eta bertatik espazioa askatzen du. Liburutegia eskaneatu ondoren edo datu-basean aldaketak egin ondoren ataza hau exekutatzeak errendimendua hobetu lezake.",
|
||||
"TaskKeyframeExtractor": "Fotograma gakoen erauzgailua"
|
||||
}
|
||||
|
|
|
@ -122,5 +122,6 @@
|
|||
"TaskOptimizeDatabase": "Optimoi tietokanta",
|
||||
"TaskKeyframeExtractorDescription": "Purkaa videotiedostojen avainkuvat tarkempien HLS-toistolistojen luomiseksi. Tehtävä saattaa kestää huomattavan pitkään.",
|
||||
"TaskKeyframeExtractor": "Avainkuvien purkain",
|
||||
"External": "Ulkoinen"
|
||||
"External": "Ulkoinen",
|
||||
"HearingImpaired": "Kuulorajoitteinen"
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"Artists": "Artistes",
|
||||
"AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
|
||||
"Books": "Livres",
|
||||
"CameraImageUploadedFrom": "Une nouvelle image de caméra a été téléchargée depuis {0}",
|
||||
"CameraImageUploadedFrom": "Une nouvelle photo a été téléversée depuis {0}",
|
||||
"Channels": "Chaînes",
|
||||
"ChapterNameValue": "Chapitre {0}",
|
||||
"Collections": "Collections",
|
||||
|
@ -123,5 +123,6 @@
|
|||
"TaskOptimizeDatabase": "Optimiser la base de données",
|
||||
"TaskKeyframeExtractorDescription": "Extrait les images clés des fichiers vidéo pour créer des listes de lecture HLS plus précises. Cette tâche peut durer très longtemps.",
|
||||
"TaskKeyframeExtractor": "Extracteur d'image clé",
|
||||
"External": "Externe"
|
||||
"External": "Externe",
|
||||
"HearingImpaired": "Malentendants"
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
"HeaderFavoriteEpisodes": "Episodios Favoritos",
|
||||
"HeaderFavoriteArtists": "Artistas Favoritos",
|
||||
"HeaderFavoriteAlbums": "Álbunes Favoritos",
|
||||
"HeaderContinueWatching": "Seguir mirando",
|
||||
"HeaderContinueWatching": "Seguir vendo",
|
||||
"HeaderAlbumArtists": "Artistas do Album",
|
||||
"Genres": "Xéneros",
|
||||
"Forced": "Forzado",
|
||||
|
@ -119,5 +119,9 @@
|
|||
"UserOnlineFromDevice": "{0} está en liña desde {1}",
|
||||
"UserOfflineFromDevice": "{0} desconectouse desde {1}",
|
||||
"TaskOptimizeDatabaseDescription": "Compacta e libera o espazo libre da base de datos. Executar esta tarefa logo de realizar mudanzas que impliquen modificacións da base de datos ou despois de escanear a biblioteca pode traer mellorías de desempeño.",
|
||||
"TaskOptimizeDatabase": "Optimizar base de datos"
|
||||
"TaskOptimizeDatabase": "Optimizar base de datos",
|
||||
"TaskKeyframeExtractorDescription": "Extrae fragmentos do vídeo para crear listas de reprodución HLS máis precisas. Podería levarlle bastante tempo.",
|
||||
"External": "Externo",
|
||||
"HearingImpaired": "Problemas de audición",
|
||||
"TaskKeyframeExtractor": "Extractor de fragmentos"
|
||||
}
|
||||
|
|
|
@ -123,5 +123,6 @@
|
|||
"TaskOptimizeDatabaseDescription": "דוחס את מסד הנתונים ומוריד את שטח האחסון שבשימוש. הרצה של פעולה זו לאחר סריקת הספרייה או שינויים אחרים שמשפיעים על מסד הנתונים יכולה לשפר ביצועים.",
|
||||
"TaskKeyframeExtractorDescription": "חלץ תמונות מפתח מקבצי וידאו בכדי ליצור רשימות השמעה מדויקות יותר של HLS. משימה זו עלולה להימשך זמן רב.",
|
||||
"TaskKeyframeExtractor": "מחלץ תמונות מפתח",
|
||||
"External": "חיצוני"
|
||||
"External": "חיצוני",
|
||||
"HearingImpaired": "לקוי שמיעה"
|
||||
}
|
||||
|
|
|
@ -123,5 +123,6 @@
|
|||
"TaskOptimizeDatabase": "Adatbázis optimalizálása",
|
||||
"TaskKeyframeExtractor": "Kulcskockák kibontása",
|
||||
"TaskKeyframeExtractorDescription": "Kulcskockákat bont ki a videofájlokból, hogy pontosabb HLS lejátszási listákat hozzon létre. Ez a feladat hosszú ideig tarthat.",
|
||||
"External": "Külső"
|
||||
"External": "Külső",
|
||||
"HearingImpaired": "Hallássérült"
|
||||
}
|
||||
|
|
|
@ -122,5 +122,6 @@
|
|||
"TaskOptimizeDatabase": "Optimalkan basis data",
|
||||
"TaskKeyframeExtractorDescription": "Ekstrak bingkai utama dari file video untuk membuat daftar putar HLS yang lebih tepat. Tugas ini dapat berjalan untuk waktu yang lama.",
|
||||
"TaskKeyframeExtractor": "Ekstraktor Bingkai Utama",
|
||||
"External": "Luar"
|
||||
"External": "Luar",
|
||||
"HearingImpaired": "Gangguan Pendengaran"
|
||||
}
|
||||
|
|
|
@ -1 +1,7 @@
|
|||
{}
|
||||
{
|
||||
"Albums": "lo albuma",
|
||||
"Artists": "lo larpra",
|
||||
"Books": "lo cukta",
|
||||
"HeaderAlbumArtists": "lo albuma larpra",
|
||||
"Playlists": "lo zgipor"
|
||||
}
|
||||
|
|
|
@ -123,5 +123,6 @@
|
|||
"TaskOptimizeDatabase": "Otimizar base de dados",
|
||||
"TaskKeyframeExtractor": "Extrator de quadro-chave",
|
||||
"TaskKeyframeExtractorDescription": "Extrai quadros-chave de arquivos de vídeo para criar listas de reprodução HLS mais precisas. Esta tarefa pode ser executada por um longo tempo.",
|
||||
"External": "Externo"
|
||||
"External": "Externo",
|
||||
"HearingImpaired": "Deficiência Auditiva"
|
||||
}
|
||||
|
|
|
@ -119,5 +119,9 @@
|
|||
"Forced": "I detyruar",
|
||||
"Default": "Parazgjedhur",
|
||||
"TaskOptimizeDatabaseDescription": "Kompakton bazën e të dhënave dhe shkurton hapësirën e lirë. Drejtimi i kësaj detyre pasi skanoni bibliotekën ose bëni ndryshime të tjera që nënkuptojnë modifikime të bazës së të dhënave mund të përmirësojë performancën.",
|
||||
"TaskOptimizeDatabase": "Optimizo databazën"
|
||||
"TaskOptimizeDatabase": "Optimizo databazën",
|
||||
"TaskKeyframeExtractorDescription": "Nxjerrë kornizat kryesore nga skedarët video për të krijuar lista luajtjeje më të sakta HLS. Ky veprim mund të dojë një kohë të gjatë për tu kompletuar.",
|
||||
"TaskKeyframeExtractor": "Nxjerrës i kornizës kryesore",
|
||||
"External": "Jashtem",
|
||||
"HearingImpaired": "Dëgjimi i dëmtuar"
|
||||
}
|
||||
|
|
|
@ -122,5 +122,6 @@
|
|||
"TaskOptimizeDatabaseDescription": "Стискає базу даних та збільшує вільний простір. Виконання цього завдання після сканування медіатеки або внесення інших змін, які передбачають модифікацію бази даних може покращити продуктивність.",
|
||||
"TaskKeyframeExtractorDescription": "Витягує ключові кадри з відеофайлів для створення більш точних списків відтворення HLS. Це завдання може виконуватися протягом тривалого часу.",
|
||||
"TaskKeyframeExtractor": "Екстрактор ключових кадрів",
|
||||
"External": "Зовнішній"
|
||||
"External": "Зовнішній",
|
||||
"HearingImpaired": "З порушеннями слуху"
|
||||
}
|
||||
|
|
|
@ -123,5 +123,6 @@
|
|||
"TaskOptimizeDatabase": "优化数据库",
|
||||
"TaskKeyframeExtractorDescription": "从视频文件中提取关键帧以创建更准确的HLS播放列表。这项任务可能需要很长时间。",
|
||||
"TaskKeyframeExtractor": "关键帧提取器",
|
||||
"External": "外部"
|
||||
"External": "外部",
|
||||
"HearingImpaired": "听力障碍"
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LrcParser" Version="2022.529.1" />
|
||||
<PackageReference Include="MetaBrainz.MusicBrainz" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
|
|
|
@ -1,37 +1,52 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using MetaBrainz.MusicBrainz;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// MusicBrainz plugin configuration.
|
||||
/// </summary>
|
||||
public class PluginConfiguration : BasePluginConfiguration
|
||||
{
|
||||
public class PluginConfiguration : BasePluginConfiguration
|
||||
private const string DefaultServer = "musicbrainz.org";
|
||||
|
||||
private const double DefaultRateLimit = 1.0;
|
||||
|
||||
private string _server = DefaultServer;
|
||||
|
||||
private double _rateLimit = DefaultRateLimit;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the server url.
|
||||
/// </summary>
|
||||
public string Server
|
||||
{
|
||||
private string _server = Plugin.DefaultServer;
|
||||
get => _server;
|
||||
|
||||
private long _rateLimit = Plugin.DefaultRateLimit;
|
||||
set => _server = value.TrimEnd('/');
|
||||
}
|
||||
|
||||
public string Server
|
||||
/// <summary>
|
||||
/// Gets or sets the rate limit.
|
||||
/// </summary>
|
||||
public double RateLimit
|
||||
{
|
||||
get => _rateLimit;
|
||||
set
|
||||
{
|
||||
get => _server;
|
||||
set => _server = value.TrimEnd('/');
|
||||
}
|
||||
|
||||
public long RateLimit
|
||||
{
|
||||
get => _rateLimit;
|
||||
set
|
||||
if (value < DefaultRateLimit && _server == DefaultServer)
|
||||
{
|
||||
if (value < Plugin.DefaultRateLimit && _server == Plugin.DefaultServer)
|
||||
{
|
||||
_rateLimit = Plugin.DefaultRateLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rateLimit = value;
|
||||
}
|
||||
_rateLimit = DefaultRateLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rateLimit = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReplaceArtistName { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to replace the artist name.
|
||||
/// </summary>
|
||||
public bool ReplaceArtistName { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
namespace MediaBrowser.Providers.Music
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
/// <summary>
|
||||
/// MusicBrainz album artist external id.
|
||||
/// </summary>
|
||||
public class MusicBrainzAlbumArtistExternalId : IExternalId
|
||||
{
|
||||
public class MusicBrainzAlbumArtistExternalId : IExternalId
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzAlbumArtist.ToString();
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzAlbumArtist.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.AlbumArtist;
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.AlbumArtist;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance!.Configuration.Server + "/artist/{0}";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio;
|
||||
}
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
namespace MediaBrowser.Providers.Music
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
/// <summary>
|
||||
/// MusicBrainz album external id.
|
||||
/// </summary>
|
||||
public class MusicBrainzAlbumExternalId : IExternalId
|
||||
{
|
||||
public class MusicBrainzAlbumExternalId : IExternalId
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzAlbum.ToString();
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzAlbum.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}";
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance!.Configuration.Server + "/release/{0}";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,28 +1,27 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
namespace MediaBrowser.Providers.Music
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
/// <summary>
|
||||
/// MusicBrainz artist external id.
|
||||
/// </summary>
|
||||
public class MusicBrainzArtistExternalId : IExternalId
|
||||
{
|
||||
public class MusicBrainzArtistExternalId : IExternalId
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance!.Configuration.Server + "/artist/{0}";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is MusicArtist;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is MusicArtist;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
@ -18,257 +10,152 @@ using MediaBrowser.Controller.Entities.Audio;
|
|||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
using MediaBrowser.Providers.Music;
|
||||
using MetaBrainz.MusicBrainz;
|
||||
using MetaBrainz.MusicBrainz.Interfaces.Entities;
|
||||
using MetaBrainz.MusicBrainz.Interfaces.Searches;
|
||||
|
||||
namespace MediaBrowser.Providers.Music
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
/// <summary>
|
||||
/// MusicBrainz artist provider.
|
||||
/// </summary>
|
||||
public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IDisposable
|
||||
{
|
||||
public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>
|
||||
private readonly Query _musicBrainzQuery;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MusicBrainzArtistProvider"/> class.
|
||||
/// </summary>
|
||||
public MusicBrainzArtistProvider()
|
||||
{
|
||||
public string Name => "MusicBrainz";
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
var musicBrainzId = searchInfo.GetMusicBrainzArtistId();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(musicBrainzId))
|
||||
MusicBrainz.Plugin.Instance!.ConfigurationChanged += (_, _) =>
|
||||
{
|
||||
var url = "/ws/2/artist/?query=arid:{0}" + musicBrainzId.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
return GetResultsFromResponse(stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
// They seem to throw bad request failures on any term with a slash
|
||||
var nameToSearch = searchInfo.Name.Replace('/', ' ');
|
||||
|
||||
var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch));
|
||||
|
||||
using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
|
||||
await using (var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
var results = GetResultsFromResponse(stream).ToList();
|
||||
|
||||
if (results.Count > 0)
|
||||
{
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
if (searchInfo.Name.HasDiacritics())
|
||||
{
|
||||
// Try again using the search with accent characters url
|
||||
url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
|
||||
|
||||
using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
return GetResultsFromResponse(stream);
|
||||
}
|
||||
}
|
||||
|
||||
return Enumerable.Empty<RemoteSearchResult>();
|
||||
}
|
||||
|
||||
private IEnumerable<RemoteSearchResult> GetResultsFromResponse(Stream stream)
|
||||
{
|
||||
using var oReader = new StreamReader(stream, Encoding.UTF8);
|
||||
var settings = new XmlReaderSettings()
|
||||
{
|
||||
ValidationType = ValidationType.None,
|
||||
CheckCharacters = false,
|
||||
IgnoreProcessingInstructions = true,
|
||||
IgnoreComments = true
|
||||
Query.DefaultServer = MusicBrainz.Plugin.Instance.Configuration.Server;
|
||||
Query.DelayBetweenRequests = MusicBrainz.Plugin.Instance.Configuration.RateLimit;
|
||||
};
|
||||
|
||||
using var reader = XmlReader.Create(oReader, settings);
|
||||
reader.MoveToContent();
|
||||
reader.Read();
|
||||
_musicBrainzQuery = new Query();
|
||||
}
|
||||
|
||||
// Loop through each element
|
||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "artist-list":
|
||||
{
|
||||
if (reader.IsEmptyElement)
|
||||
{
|
||||
reader.Read();
|
||||
continue;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public string Name => "MusicBrainz";
|
||||
|
||||
using var subReader = reader.ReadSubtree();
|
||||
return ParseArtistList(subReader).ToList();
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
var artistId = searchInfo.GetMusicBrainzArtistId();
|
||||
|
||||
default:
|
||||
{
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
return Enumerable.Empty<RemoteSearchResult>();
|
||||
if (!string.IsNullOrWhiteSpace(artistId))
|
||||
{
|
||||
var artistResult = await _musicBrainzQuery.LookupArtistAsync(new Guid(artistId), Include.Aliases, null, null, cancellationToken).ConfigureAwait(false);
|
||||
return GetResultFromResponse(artistResult).SingleItemAsEnumerable();
|
||||
}
|
||||
|
||||
private IEnumerable<RemoteSearchResult> ParseArtistList(XmlReader reader)
|
||||
var artistSearchResults = await _musicBrainzQuery.FindArtistsAsync($"\"{searchInfo.Name}\"", null, null, false, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (artistSearchResults.Results.Count > 0)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
reader.Read();
|
||||
return GetResultsFromResponse(artistSearchResults.Results);
|
||||
}
|
||||
|
||||
// Loop through each element
|
||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
||||
if (searchInfo.Name.HasDiacritics())
|
||||
{
|
||||
// Try again using the search with an accented characters query
|
||||
var artistAccentsSearchResults = await _musicBrainzQuery.FindArtistsAsync($"artistaccent:\"{searchInfo.Name}\"", null, null, false, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (artistAccentsSearchResults.Results.Count > 0)
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
return GetResultsFromResponse(artistAccentsSearchResults.Results);
|
||||
}
|
||||
}
|
||||
|
||||
return Enumerable.Empty<RemoteSearchResult>();
|
||||
}
|
||||
|
||||
private IEnumerable<RemoteSearchResult> GetResultsFromResponse(IEnumerable<ISearchResult<IArtist>>? releaseSearchResults)
|
||||
{
|
||||
if (releaseSearchResults is null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var result in releaseSearchResults)
|
||||
{
|
||||
yield return GetResultFromResponse(result.Item);
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteSearchResult GetResultFromResponse(IArtist artist)
|
||||
{
|
||||
var searchResult = new RemoteSearchResult
|
||||
{
|
||||
Name = artist.Name,
|
||||
ProductionYear = artist.LifeSpan?.Begin?.Year,
|
||||
PremiereDate = artist.LifeSpan?.Begin?.NearestDate
|
||||
};
|
||||
|
||||
searchResult.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.Id.ToString());
|
||||
|
||||
return searchResult;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = new MetadataResult<MusicArtist> { Item = new MusicArtist() };
|
||||
|
||||
var musicBrainzId = info.GetMusicBrainzArtistId();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(musicBrainzId))
|
||||
{
|
||||
var searchResults = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var singleResult = searchResults.FirstOrDefault();
|
||||
|
||||
if (singleResult != null)
|
||||
{
|
||||
musicBrainzId = singleResult.GetProviderId(MetadataProvider.MusicBrainzArtist);
|
||||
result.Item.Overview = singleResult.Overview;
|
||||
|
||||
if (Plugin.Instance!.Configuration.ReplaceArtistName)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "artist":
|
||||
{
|
||||
if (reader.IsEmptyElement)
|
||||
{
|
||||
reader.Read();
|
||||
continue;
|
||||
}
|
||||
|
||||
var mbzId = reader.GetAttribute("id");
|
||||
|
||||
using var subReader = reader.ReadSubtree();
|
||||
var artist = ParseArtist(subReader, mbzId);
|
||||
if (artist != null)
|
||||
{
|
||||
yield return artist;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Read();
|
||||
result.Item.Name = singleResult.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteSearchResult ParseArtist(XmlReader reader, string artistId)
|
||||
if (!string.IsNullOrWhiteSpace(musicBrainzId))
|
||||
{
|
||||
var result = new RemoteSearchResult();
|
||||
|
||||
reader.MoveToContent();
|
||||
reader.Read();
|
||||
|
||||
// http://stackoverflow.com/questions/2299632/why-does-xmlreader-skip-every-other-element-if-there-is-no-whitespace-separator
|
||||
|
||||
// Loop through each element
|
||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "name":
|
||||
{
|
||||
result.Name = reader.ReadElementContentAsString();
|
||||
break;
|
||||
}
|
||||
|
||||
case "annotation":
|
||||
{
|
||||
result.Overview = reader.ReadElementContentAsString();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// there is sort-name if ever needed
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
result.SetProviderId(MetadataProvider.MusicBrainzArtist, artistId);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(artistId) || string.IsNullOrWhiteSpace(result.Name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
result.HasMetadata = true;
|
||||
result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, musicBrainzId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose all resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Whether to dispose.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
var result = new MetadataResult<MusicArtist>
|
||||
{
|
||||
Item = new MusicArtist()
|
||||
};
|
||||
|
||||
var musicBrainzId = info.GetMusicBrainzArtistId();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(musicBrainzId))
|
||||
{
|
||||
var searchResults = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var singleResult = searchResults.FirstOrDefault();
|
||||
|
||||
if (singleResult != null)
|
||||
{
|
||||
musicBrainzId = singleResult.GetProviderId(MetadataProvider.MusicBrainzArtist);
|
||||
result.Item.Overview = singleResult.Overview;
|
||||
|
||||
if (Plugin.Instance.Configuration.ReplaceArtistName)
|
||||
{
|
||||
result.Item.Name = singleResult.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(musicBrainzId))
|
||||
{
|
||||
result.HasMetadata = true;
|
||||
result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, musicBrainzId);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes an URL.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private static string UrlEncode(string name)
|
||||
{
|
||||
return WebUtility.UrlEncode(name);
|
||||
}
|
||||
|
||||
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
_musicBrainzQuery.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
namespace MediaBrowser.Providers.Music
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
/// <summary>
|
||||
/// MusicBrainz other artist external id.
|
||||
/// </summary>
|
||||
public class MusicBrainzOtherArtistExternalId : IExternalId
|
||||
{
|
||||
public class MusicBrainzOtherArtistExternalId : IExternalId
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance!.Configuration.Server + "/artist/{0}";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio or MusicAlbum;
|
||||
}
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
namespace MediaBrowser.Providers.Music
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
/// <summary>
|
||||
/// MusicBrainz release group external id.
|
||||
/// </summary>
|
||||
public class MusicBrainzReleaseGroupExternalId : IExternalId
|
||||
{
|
||||
public class MusicBrainzReleaseGroupExternalId : IExternalId
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzReleaseGroup.ToString();
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzReleaseGroup.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.ReleaseGroup;
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.ReleaseGroup;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}";
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance!.Configuration.Server + "/release-group/{0}";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio or MusicAlbum;
|
||||
}
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
namespace MediaBrowser.Providers.Music
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
/// <summary>
|
||||
/// MusicBrainz track id.
|
||||
/// </summary>
|
||||
public class MusicBrainzTrackId : IExternalId
|
||||
{
|
||||
public class MusicBrainzTrackId : IExternalId
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => "MusicBrainz";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzTrack.ToString();
|
||||
/// <inheritdoc />
|
||||
public string Key => MetadataProvider.MusicBrainzTrack.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Track;
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.Track;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}";
|
||||
/// <inheritdoc />
|
||||
public string? UrlFormatString => Plugin.Instance!.Configuration.Server + "/track/{0}";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item) => item is Audio;
|
||||
}
|
||||
|
|
|
@ -1,45 +1,64 @@
|
|||
#nullable disable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http.Headers;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
|
||||
using MetaBrainz.MusicBrainz;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz
|
||||
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
|
||||
/// <summary>
|
||||
/// Plugin instance.
|
||||
/// </summary>
|
||||
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
{
|
||||
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Plugin"/> class.
|
||||
/// </summary>
|
||||
/// <param name="applicationPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
|
||||
/// <param name="xmlSerializer">Instance of the <see cref="IXmlSerializer"/> interface.</param>
|
||||
/// <param name="applicationHost">Instance of the <see cref="IApplicationHost"/> interface.</param>
|
||||
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer, IApplicationHost applicationHost)
|
||||
: base(applicationPaths, xmlSerializer)
|
||||
{
|
||||
public const string DefaultServer = "https://musicbrainz.org";
|
||||
Instance = this;
|
||||
|
||||
public const long DefaultRateLimit = 2000u;
|
||||
// TODO: Change this to "JellyfinMusicBrainzPlugin" once we take it out of the server repo.
|
||||
Query.DefaultUserAgent.Add(new ProductInfoHeaderValue(applicationHost.Name.Replace(' ', '-'), applicationHost.ApplicationVersionString));
|
||||
Query.DefaultUserAgent.Add(new ProductInfoHeaderValue($"({applicationHost.ApplicationUserAgentAddress})"));
|
||||
Query.DelayBetweenRequests = Instance.Configuration.RateLimit;
|
||||
Query.DefaultServer = Instance.Configuration.Server;
|
||||
}
|
||||
|
||||
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
|
||||
: base(applicationPaths, xmlSerializer)
|
||||
/// <summary>
|
||||
/// Gets the current plugin instance.
|
||||
/// </summary>
|
||||
public static Plugin? Instance { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Guid Id => new Guid("8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "MusicBrainz";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Description => "Get artist and album metadata from any MusicBrainz server.";
|
||||
|
||||
/// <inheritdoc />
|
||||
// TODO remove when plugin removed from server.
|
||||
public override string ConfigurationFileName => "Jellyfin.Plugin.MusicBrainz.xml";
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<PluginPageInfo> GetPages()
|
||||
{
|
||||
yield return new PluginPageInfo
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public static Plugin Instance { get; private set; }
|
||||
|
||||
public override Guid Id => new Guid("8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a");
|
||||
|
||||
public override string Name => "MusicBrainz";
|
||||
|
||||
public override string Description => "Get artist and album metadata from any MusicBrainz server.";
|
||||
|
||||
// TODO remove when plugin removed from server.
|
||||
public override string ConfigurationFileName => "Jellyfin.Plugin.MusicBrainz.xml";
|
||||
|
||||
public IEnumerable<PluginPageInfo> GetPages()
|
||||
{
|
||||
yield return new PluginPageInfo
|
||||
{
|
||||
Name = Name,
|
||||
EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html"
|
||||
};
|
||||
}
|
||||
Name = Name,
|
||||
EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="Moq" Version="4.18.2" />
|
||||
<PackageReference Include="SharpFuzz" Version="1.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,42 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Jellyfin.Extensions
|
||||
namespace Jellyfin.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Static extensions for the <see cref="IEnumerable{T}"/> interface.
|
||||
/// </summary>
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Static extensions for the <see cref="IEnumerable{T}"/> interface.
|
||||
/// Determines whether the value is contained in the source collection.
|
||||
/// </summary>
|
||||
public static class EnumerableExtensions
|
||||
/// <param name="source">An instance of the <see cref="IEnumerable{String}"/> interface.</param>
|
||||
/// <param name="value">The value to look for in the collection.</param>
|
||||
/// <param name="stringComparison">The string comparison.</param>
|
||||
/// <returns>A value indicating whether the value is contained in the collection.</returns>
|
||||
/// <exception cref="ArgumentNullException">The source is null.</exception>
|
||||
public static bool Contains(this IEnumerable<string> source, ReadOnlySpan<char> value, StringComparison stringComparison)
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the value is contained in the source collection.
|
||||
/// </summary>
|
||||
/// <param name="source">An instance of the <see cref="IEnumerable{String}"/> interface.</param>
|
||||
/// <param name="value">The value to look for in the collection.</param>
|
||||
/// <param name="stringComparison">The string comparison.</param>
|
||||
/// <returns>A value indicating whether the value is contained in the collection.</returns>
|
||||
/// <exception cref="ArgumentNullException">The source is null.</exception>
|
||||
public static bool Contains(this IEnumerable<string> source, ReadOnlySpan<char> value, StringComparison stringComparison)
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
|
||||
if (source is IList<string> list)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
|
||||
if (source is IList<string> list)
|
||||
int len = list.Count;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
int len = list.Count;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (value.Equals(list[i], stringComparison))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (string element in source)
|
||||
{
|
||||
if (value.Equals(element, stringComparison))
|
||||
if (value.Equals(list[i], stringComparison))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -44,5 +33,26 @@ namespace Jellyfin.Extensions
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (string element in source)
|
||||
{
|
||||
if (value.Equals(element, stringComparison))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an IEnumerable from a single item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to return.</param>
|
||||
/// <typeparam name="T">The type of item.</typeparam>
|
||||
/// <returns>The IEnumerable{T}.</returns>
|
||||
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ using MediaBrowser.Controller.Providers;
|
|||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Music;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
using MediaBrowser.XbmcMetadata.Parsers;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
|
|
|
@ -7,7 +7,7 @@ using MediaBrowser.Controller.Providers;
|
|||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Music;
|
||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||
using MediaBrowser.XbmcMetadata.Parsers;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
|
|
Loading…
Reference in New Issue
Block a user