mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-04 14:58:36 +01:00
Compare commits
16 Commits
standards-
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53c1c4982a | ||
|
|
21c0a35edf | ||
|
|
857b99ce61 | ||
|
|
cf88058099 | ||
|
|
5ee9e79da2 | ||
|
|
5ed7798c36 | ||
|
|
b71b4cc26f | ||
|
|
7185257da5 | ||
|
|
d4c962f6e4 | ||
|
|
52cf8d1ba4 | ||
|
|
081f0ef4a0 | ||
|
|
cc5fb3f1ee | ||
|
|
9ab7cc0fe9 | ||
|
|
285fc1b9f6 | ||
|
|
5ce7170813 | ||
|
|
e627c723e2 |
8
.github/workflows/ci-codeql-analysis.yml
vendored
8
.github/workflows/ci-codeql-analysis.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
|
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
|
||||||
@@ -32,13 +32,13 @@ jobs:
|
|||||||
dotnet-version: '10.0.x'
|
dotnet-version: '10.0.x'
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
uses: github/codeql-action/init@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
queries: +security-extended
|
queries: +security-extended
|
||||||
|
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
uses: github/codeql-action/autobuild@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
uses: github/codeql-action/analyze@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
|
||||||
|
|||||||
4
.github/workflows/ci-compat.yml
vendored
4
.github/workflows/ci-compat.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
permissions: read-all
|
permissions: read-all
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
permissions: read-all
|
permissions: read-all
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
|||||||
2
.github/workflows/ci-format.yml
vendored
2
.github/workflows/ci-format.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
format-check:
|
format-check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
|
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/ci-tests.yml
vendored
2
.github/workflows/ci-tests.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
|
|
||||||
runs-on: "${{ matrix.os }}"
|
runs-on: "${{ matrix.os }}"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
|
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.github/workflows/commands.yml
vendored
4
.github/workflows/commands.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
reactions: '+1'
|
reactions: '+1'
|
||||||
|
|
||||||
- name: Checkout the latest code
|
- name: Checkout the latest code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: pull in script
|
- name: pull in script
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
repository: jellyfin/jellyfin-triage-script
|
repository: jellyfin/jellyfin-triage-script
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/issue-template-check.yml
vendored
2
.github/workflows/issue-template-check.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
steps:
|
steps:
|
||||||
- name: pull in script
|
- name: pull in script
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
repository: jellyfin/jellyfin-triage-script
|
repository: jellyfin/jellyfin-triage-script
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/openapi-generate.yml
vendored
2
.github/workflows/openapi-generate.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.ref }}
|
ref: ${{ inputs.ref }}
|
||||||
repository: ${{ inputs.repository }}
|
repository: ${{ inputs.repository }}
|
||||||
|
|||||||
2
.github/workflows/openapi-pull-request.yml
vendored
2
.github/workflows/openapi-pull-request.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
base_ref: ${{ steps.ancestor.outputs.base_ref }}
|
base_ref: ${{ steps.ancestor.outputs.base_ref }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
|||||||
11
.github/workflows/pull-request-conflict.yml
vendored
11
.github/workflows/pull-request-conflict.yml
vendored
@@ -5,18 +5,19 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
issue_comment:
|
types: [synchronize]
|
||||||
|
|
||||||
permissions: {}
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
label:
|
main:
|
||||||
name: Labeling
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository == 'jellyfin/jellyfin' && github.event.issue.pull_request }}
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
if: ${{ github.repository == 'jellyfin/jellyfin' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Apply label
|
- name: Apply label
|
||||||
uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0
|
uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0
|
||||||
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request_target'}}
|
|
||||||
with:
|
with:
|
||||||
dirtyLabel: 'merge conflict'
|
dirtyLabel: 'merge conflict'
|
||||||
commentOnDirty: 'This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.'
|
commentOnDirty: 'This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.'
|
||||||
|
|||||||
36
.github/workflows/pull-request-standards.yml
vendored
36
.github/workflows/pull-request-standards.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: Standards Check
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- '**/CLAUDE.md'
|
|
||||||
- '**/AGENTS.md'
|
|
||||||
- 'docs/superpowers/**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
close:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
body: 'This PR does not follow our contributing guidelines. https://jellyfin.org/docs/general/contributing/'
|
|
||||||
});
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
labels: ['invalid']
|
|
||||||
});
|
|
||||||
await github.rest.pulls.update({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
pull_number: context.issue.number,
|
|
||||||
state: 'closed'
|
|
||||||
});
|
|
||||||
|
|
||||||
4
.github/workflows/release-bump-version.yaml
vendored
4
.github/workflows/release-bump-version.yaml
vendored
@@ -33,7 +33,7 @@ jobs:
|
|||||||
yq-version: v4.9.8
|
yq-version: v4.9.8
|
||||||
|
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
ref: ${{ env.TAG_BRANCH }}
|
ref: ${{ env.TAG_BRANCH }}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
NEXT_VERSION: ${{ github.event.inputs.NEXT_VERSION }}
|
NEXT_VERSION: ${{ github.event.inputs.NEXT_VERSION }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
ref: ${{ env.TAG_BRANCH }}
|
ref: ${{ env.TAG_BRANCH }}
|
||||||
|
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ public class ItemUpdateController : BaseJellyfinApiController
|
|||||||
item.CustomRating = request.CustomRating;
|
item.CustomRating = request.CustomRating;
|
||||||
|
|
||||||
var currentTags = item.Tags;
|
var currentTags = item.Tags;
|
||||||
var newTags = request.Tags.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
|
var newTags = request.Tags.Select(t => t.Trim()).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
|
||||||
var removedTags = currentTags.Except(newTags).ToList();
|
var removedTags = currentTags.Except(newTags).ToList();
|
||||||
var addedTags = newTags.Except(currentTags).ToList();
|
var addedTags = newTags.Except(currentTags).ToList();
|
||||||
item.Tags = newTags;
|
item.Tags = newTags;
|
||||||
|
|||||||
@@ -318,9 +318,6 @@ public class ItemsController : BaseJellyfinApiController
|
|||||||
}
|
}
|
||||||
else if (folder is ICollectionFolder)
|
else if (folder is ICollectionFolder)
|
||||||
{
|
{
|
||||||
// When the client doesn't specify recursive/includeItemTypes, force the query
|
|
||||||
// through the database path where all filters (IsHD, genres, etc.) are applied.
|
|
||||||
recursive ??= true;
|
|
||||||
if (includeItemTypes.Length == 0)
|
if (includeItemTypes.Length == 0)
|
||||||
{
|
{
|
||||||
includeItemTypes = collectionType switch
|
includeItemTypes = collectionType switch
|
||||||
@@ -330,6 +327,13 @@ public class ItemsController : BaseJellyfinApiController
|
|||||||
_ => []
|
_ => []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When the client doesn't specify recursive/includeItemTypes, force the query
|
||||||
|
// through the database path where all filters (IsHD, genres, etc.) are applied.
|
||||||
|
if (includeItemTypes.Length > 0)
|
||||||
|
{
|
||||||
|
recursive ??= true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is not UserRootFolder
|
if (item is not UserRootFolder
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
throw new ArgumentNullException(nameof(name));
|
throw new ArgumentNullException(nameof(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = name.Trim();
|
||||||
var current = item.Tags;
|
var current = item.Tags;
|
||||||
|
|
||||||
if (!current.Contains(name, StringComparison.OrdinalIgnoreCase))
|
if (!current.Contains(name, StringComparison.OrdinalIgnoreCase))
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AsyncKeyedLock;
|
using AsyncKeyedLock;
|
||||||
@@ -102,13 +104,10 @@ namespace MediaBrowser.MediaEncoding.Attachments
|
|||||||
&& (a.FileName.Contains('/', StringComparison.OrdinalIgnoreCase) || a.FileName.Contains('\\', StringComparison.OrdinalIgnoreCase)));
|
&& (a.FileName.Contains('/', StringComparison.OrdinalIgnoreCase) || a.FileName.Contains('\\', StringComparison.OrdinalIgnoreCase)));
|
||||||
if (shouldExtractOneByOne && !inputFile.EndsWith(".mks", StringComparison.OrdinalIgnoreCase))
|
if (shouldExtractOneByOne && !inputFile.EndsWith(".mks", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
foreach (var attachment in mediaSource.MediaAttachments)
|
await ExtractAllAttachmentsIndividuallyInternal(
|
||||||
{
|
inputFile,
|
||||||
if (!string.Equals(attachment.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase))
|
mediaSource,
|
||||||
{
|
cancellationToken).ConfigureAwait(false);
|
||||||
await ExtractAttachment(inputFile, mediaSource, attachment, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -119,6 +118,140 @@ namespace MediaBrowser.MediaEncoding.Attachments
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ExtractAllAttachmentsIndividuallyInternal(
|
||||||
|
string inputFile,
|
||||||
|
MediaSourceInfo mediaSource,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var inputPath = _mediaEncoder.GetInputArgument(inputFile, mediaSource);
|
||||||
|
|
||||||
|
ArgumentException.ThrowIfNullOrEmpty(inputPath);
|
||||||
|
|
||||||
|
var outputFolder = _pathManager.GetAttachmentFolderPath(mediaSource.Id);
|
||||||
|
if (outputFolder is null)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Skipping attachment extraction for input {InputFile}: MediaSource Id is not a GUID.", inputFile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (await _semaphoreLocks.LockAsync(outputFolder, cancellationToken).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(outputFolder);
|
||||||
|
|
||||||
|
var dumpArgs = new StringBuilder();
|
||||||
|
var missingPaths = new List<string>();
|
||||||
|
foreach (var attachment in mediaSource.MediaAttachments)
|
||||||
|
{
|
||||||
|
if (string.Equals(attachment.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexName = attachment.Index.ToString(CultureInfo.InvariantCulture);
|
||||||
|
var attachmentPath = _pathManager.GetAttachmentPath(mediaSource.Id, attachment.FileName ?? indexName)
|
||||||
|
?? _pathManager.GetAttachmentPath(mediaSource.Id, indexName)!;
|
||||||
|
if (File.Exists(attachmentPath))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dumpArgs.AppendFormat(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
"-dump_attachment:{0} \"{1}\" ",
|
||||||
|
attachment.Index,
|
||||||
|
EncodingUtils.NormalizePath(attachmentPath));
|
||||||
|
missingPaths.Add(attachmentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingPaths.Count == 0)
|
||||||
|
{
|
||||||
|
// Skip extraction if all files already exist
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasVideoOrAudioStream = mediaSource.MediaStreams
|
||||||
|
.Any(s => s.Type == MediaStreamType.Video || s.Type == MediaStreamType.Audio);
|
||||||
|
var processArgs = string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
"{0}{1} -i {2} {3}",
|
||||||
|
dumpArgs,
|
||||||
|
inputPath.EndsWith(".concat\"", StringComparison.OrdinalIgnoreCase) ? "-f concat -safe 0" : string.Empty,
|
||||||
|
inputPath,
|
||||||
|
hasVideoOrAudioStream ? "-t 0 -f null null" : string.Empty);
|
||||||
|
|
||||||
|
int exitCode;
|
||||||
|
|
||||||
|
using (var process = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
Arguments = processArgs,
|
||||||
|
FileName = _mediaEncoder.EncoderPath,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
WindowStyle = ProcessWindowStyle.Hidden,
|
||||||
|
ErrorDialog = false
|
||||||
|
},
|
||||||
|
EnableRaisingEvents = true
|
||||||
|
})
|
||||||
|
{
|
||||||
|
_logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
exitCode = process.ExitCode;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
process.Kill(true);
|
||||||
|
exitCode = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var failed = false;
|
||||||
|
|
||||||
|
if (exitCode != 0 && (hasVideoOrAudioStream || exitCode != 1))
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
|
||||||
|
foreach (var path in missingPaths)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_fileSystem.DeleteFile(path);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error deleting extracted attachment {Path}", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!failed && missingPaths.Exists(p => !File.Exists(p)))
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failed)
|
||||||
|
{
|
||||||
|
_logger.LogError("ffmpeg attachment extraction failed for {InputPath} to {OutputPath}", inputPath, outputFolder);
|
||||||
|
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputFolder));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("ffmpeg attachment extraction completed for {InputPath} to {OutputPath}", inputPath, outputFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ExtractAllAttachmentsInternal(
|
private async Task ExtractAllAttachmentsInternal(
|
||||||
string inputFile,
|
string inputFile,
|
||||||
MediaSourceInfo mediaSource,
|
MediaSourceInfo mediaSource,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public class EncodingOptions
|
|||||||
VppTonemappingContrast = 1;
|
VppTonemappingContrast = 1;
|
||||||
H264Crf = 23;
|
H264Crf = 23;
|
||||||
H265Crf = 28;
|
H265Crf = 28;
|
||||||
|
EncoderPreset = EncoderPreset.auto;
|
||||||
DeinterlaceDoubleRate = false;
|
DeinterlaceDoubleRate = false;
|
||||||
DeinterlaceMethod = DeinterlaceMethod.yadif;
|
DeinterlaceMethod = DeinterlaceMethod.yadif;
|
||||||
EnableDecodingColorDepth10Hevc = true;
|
EnableDecodingColorDepth10Hevc = true;
|
||||||
@@ -217,7 +218,7 @@ public class EncodingOptions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the encoder preset.
|
/// Gets or sets the encoder preset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EncoderPreset? EncoderPreset { get; set; }
|
public EncoderPreset EncoderPreset { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the framerate is doubled when deinterlacing.
|
/// Gets or sets a value indicating whether the framerate is doubled when deinterlacing.
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
|||||||
var posters = movie.Images.Posters;
|
var posters = movie.Images.Posters;
|
||||||
var backdrops = movie.Images.Backdrops;
|
var backdrops = movie.Images.Backdrops;
|
||||||
var logos = movie.Images.Logos;
|
var logos = movie.Images.Logos;
|
||||||
var remoteImages = new List<RemoteImageInfo>(posters?.Count ?? 0 + backdrops?.Count ?? 0 + logos?.Count ?? 0);
|
var remoteImages = new List<RemoteImageInfo>((posters?.Count ?? 0) + (backdrops?.Count ?? 0) + (logos?.Count ?? 0));
|
||||||
|
|
||||||
if (posters is not null)
|
if (posters is not null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -210,18 +210,21 @@ public class SeriesMetadataService : MetadataService<Series, SeriesInfo>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not yet processed
|
// Episode has been processed and linked to a season, only needs a virtual season
|
||||||
if (episode.SeasonId.IsEmpty())
|
// if it isn't already linked to a known physical season by ID or path
|
||||||
|
if (!episode.SeasonId.IsEmpty())
|
||||||
{
|
{
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Episode has been processed, only needs a virtual season if it isn't
|
|
||||||
// already linked to a known physical season by ID or path
|
|
||||||
return !physicalSeasonIds.Contains(episode.SeasonId)
|
return !physicalSeasonIds.Contains(episode.SeasonId)
|
||||||
&& !physicalSeasonPaths.Contains(System.IO.Path.GetDirectoryName(episode.Path) ?? string.Empty);
|
&& !physicalSeasonPaths.Contains(System.IO.Path.GetDirectoryName(episode.Path) ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Episode not yet linked, check if it's in a physical season folder
|
||||||
|
// If yes then skip it, processing not finished
|
||||||
|
// If no then include it, needs Season Unknown
|
||||||
|
var episodeDirectory = System.IO.Path.GetDirectoryName(episode.Path) ?? string.Empty;
|
||||||
|
return !physicalSeasonPaths.Contains(episodeDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates seasons for all episodes if they don't exist.
|
/// Creates seasons for all episodes if they don't exist.
|
||||||
/// If no season number can be determined, a dummy season will be created.
|
/// If no season number can be determined, a dummy season will be created.
|
||||||
|
|||||||
Reference in New Issue
Block a user