mirror of
https://github.com/Lidarr/Lidarr
synced 2026-05-08 04:21:32 +02:00
Add GHCR Docker workflow for new-multithreaded-import; multithread scan changes
- Dockerfile.multithread: build from repo root for CI overlay on linuxserver/lidarr:nightly - docker-ghcr-multithread.yml: push to ghcr.io on branch push + workflow_dispatch - MediaFiles / RefreshArtist: fork behavior for multithreaded import Made-with: Cursor
This commit is contained in:
parent
6f97d739fe
commit
01d26cf02d
4 changed files with 86 additions and 14 deletions
43
.github/workflows/docker-ghcr-multithread.yml
vendored
Normal file
43
.github/workflows/docker-ghcr-multithread.yml
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Build custom Lidarr overlay image and push to GHCR when this branch updates.
|
||||
name: Docker (multithread) → GHCR
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- new-multithreaded-import
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/lidarr
|
||||
|
||||
jobs:
|
||||
build-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.multithread
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:new-multithreaded-import
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:git-${{ github.sha }}
|
||||
19
Dockerfile.multithread
Normal file
19
Dockerfile.multithread
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# CI / context = repo root (lidarr-src). Local builds from parent folder use ../Dockerfile instead.
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
|
||||
WORKDIR /src
|
||||
|
||||
COPY . ./
|
||||
|
||||
RUN dotnet publish src/NzbDrone.Console/Lidarr.Console.csproj \
|
||||
-c Release \
|
||||
-f net8.0 \
|
||||
-r linux-musl-x64 \
|
||||
--self-contained true \
|
||||
-p:RunAnalyzers=false \
|
||||
-p:EnforceCodeStyleInBuild=false \
|
||||
-p:TreatWarningsAsErrors=false \
|
||||
-o /out
|
||||
|
||||
FROM ghcr.io/linuxserver/lidarr:nightly
|
||||
|
||||
COPY --from=builder /out/ /app/lidarr/bin/
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Disk;
|
||||
|
|
@ -84,23 +85,19 @@ public void Scan(List<string> folders = null, FilterFilesType filter = FilterFil
|
|||
}
|
||||
|
||||
var mediaFileList = new List<IFileInfo>();
|
||||
var mediaFileListLock = new object();
|
||||
|
||||
var musicFilesStopwatch = Stopwatch.StartNew();
|
||||
|
||||
// Validate folders first (early exit on error like original behaviour)
|
||||
var foldersToScan = new List<string>();
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
// We could be scanning a root folder or a subset of a root folder. If it's a subset,
|
||||
// check if the root folder exists before cleaning.
|
||||
var rootFolder = _rootFolderService.GetBestRootFolder(folder);
|
||||
|
||||
if (rootFolder == null)
|
||||
{
|
||||
_logger.Error("Not scanning {0}, it's not a subdirectory of a defined root folder", folder);
|
||||
return;
|
||||
}
|
||||
|
||||
var folderExists = _diskProvider.FolderExists(folder);
|
||||
|
||||
if (!folderExists)
|
||||
{
|
||||
if (!_diskProvider.FolderExists(rootFolder.Path))
|
||||
|
|
@ -110,7 +107,6 @@ public void Scan(List<string> folders = null, FilterFilesType filter = FilterFil
|
|||
skippedArtists.ForEach(x => _eventAggregator.PublishEvent(new ArtistScanSkippedEvent(x, ArtistScanSkippedReason.RootFolderDoesNotExist)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_diskProvider.FolderEmpty(rootFolder.Path))
|
||||
{
|
||||
_logger.Warn("Artists' root folder ({0}) is empty.", rootFolder.Path);
|
||||
|
|
@ -119,28 +115,35 @@ public void Scan(List<string> folders = null, FilterFilesType filter = FilterFil
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!folderExists)
|
||||
{
|
||||
_logger.Debug("Specified scan folder ({0}) doesn't exist.", folder);
|
||||
|
||||
CleanMediaFiles(folder, new List<string>());
|
||||
continue;
|
||||
}
|
||||
foldersToScan.Add(folder);
|
||||
}
|
||||
|
||||
var musicFilesStopwatch = Stopwatch.StartNew();
|
||||
|
||||
Parallel.ForEach(foldersToScan, folder =>
|
||||
{
|
||||
_logger.ProgressInfo("Scanning {0}", folder);
|
||||
|
||||
var files = FilterFiles(folder, GetAudioFiles(folder));
|
||||
var files = FilterFiles(folder, GetAudioFiles(folder)).ToList();
|
||||
|
||||
if (!files.Any())
|
||||
{
|
||||
_logger.Warn("Scan folder {0} is empty.", folder);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
CleanMediaFiles(folder, files.Select(x => x.FullName).ToList());
|
||||
mediaFileList.AddRange(files);
|
||||
}
|
||||
lock (mediaFileListLock)
|
||||
{
|
||||
mediaFileList.AddRange(files);
|
||||
}
|
||||
});
|
||||
|
||||
var artists = _artistService.GetArtists(artistIds);
|
||||
|
||||
|
|
|
|||
|
|
@ -305,6 +305,13 @@ private void RescanArtists(List<Artist> artists, bool isNew, CommandTrigger trig
|
|||
// badly organized / partly matched libraries
|
||||
folders = artists.Select(x => x.Path).ToList();
|
||||
}
|
||||
else if (trigger == CommandTrigger.Manual && artists.Any())
|
||||
{
|
||||
// Manual refresh of specific artist(s): only scan those artists' folders,
|
||||
// never the entire library (avoids 60k+ file scan when refreshing e.g. Various Artists).
|
||||
folders = artists.Select(x => x.Path).ToList();
|
||||
_logger.Trace("Manual refresh: rescanning only {0} artist folder(s)", folders.Count);
|
||||
}
|
||||
else if (rescanAfterRefresh == RescanAfterRefreshType.Never)
|
||||
{
|
||||
_logger.Trace("Skipping rescan. Reason: never rescan after refresh");
|
||||
|
|
|
|||
Loading…
Reference in a new issue