mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-04 06:48:35 +01:00
Compare commits
1 Commits
master
...
standards-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
627c1b977c |
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- 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@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
|
uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
queries: +security-extended
|
queries: +security-extended
|
||||||
|
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
|
uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
|
uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
||||||
|
|||||||
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- 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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- 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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
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 }}
|
||||||
|
|||||||
36
.github/workflows/pull-request-standards.yml
vendored
Normal file
36
.github/workflows/pull-request-standards.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
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.Select(t => t.Trim()).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
|
var newTags = request.Tags.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,6 +318,9 @@ 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
|
||||||
@@ -327,13 +330,6 @@ 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,7 +15,6 @@ 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,10 +1,8 @@
|
|||||||
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;
|
||||||
@@ -104,10 +102,13 @@ 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))
|
||||||
{
|
{
|
||||||
await ExtractAllAttachmentsIndividuallyInternal(
|
foreach (var attachment in mediaSource.MediaAttachments)
|
||||||
inputFile,
|
{
|
||||||
mediaSource,
|
if (!string.Equals(attachment.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase))
|
||||||
cancellationToken).ConfigureAwait(false);
|
{
|
||||||
|
await ExtractAttachment(inputFile, mediaSource, attachment, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -118,140 +119,6 @@ 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,7 +43,6 @@ 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;
|
||||||
@@ -218,7 +217,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,19 +210,16 @@ public class SeriesMetadataService : MetadataService<Series, SeriesInfo>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Episode has been processed and linked to a season, only needs a virtual season
|
// Not yet processed
|
||||||
// if it isn't already linked to a known physical season by ID or path
|
if (episode.SeasonId.IsEmpty())
|
||||||
if (!episode.SeasonId.IsEmpty())
|
|
||||||
{
|
{
|
||||||
return !physicalSeasonIds.Contains(episode.SeasonId)
|
return false;
|
||||||
&& !physicalSeasonPaths.Contains(System.IO.Path.GetDirectoryName(episode.Path) ?? string.Empty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Episode not yet linked, check if it's in a physical season folder
|
// Episode has been processed, only needs a virtual season if it isn't
|
||||||
// If yes then skip it, processing not finished
|
// already linked to a known physical season by ID or path
|
||||||
// If no then include it, needs Season Unknown
|
return !physicalSeasonIds.Contains(episode.SeasonId)
|
||||||
var episodeDirectory = System.IO.Path.GetDirectoryName(episode.Path) ?? string.Empty;
|
&& !physicalSeasonPaths.Contains(System.IO.Path.GetDirectoryName(episode.Path) ?? string.Empty);
|
||||||
return !physicalSeasonPaths.Contains(episodeDirectory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user