Added podcast support, only from subsonic API for now, ref #726

This commit is contained in:
emeric
2025-09-13 15:04:13 +02:00
parent 1d584ebcc6
commit 932e7715f5
111 changed files with 4717 additions and 188 deletions
@@ -153,14 +153,15 @@ namespace lms::scanner
} // namespace
std::unique_ptr<IScannerService> createScannerService(db::IDb& db)
std::unique_ptr<IScannerService> createScannerService(db::IDb& db, const std::filesystem::path& cachePath)
{
return std::make_unique<ScannerService>(db);
return std::make_unique<ScannerService>(db, cachePath);
}
ScannerService::ScannerService(db::IDb& db)
ScannerService::ScannerService(db::IDb& db, const std::filesystem::path& cachePath)
: _db{ db }
, _jobScheduler{ core::createJobScheduler("Scanner", getScannerThreadCount()) }
, _cachePath{ cachePath }
{
_ioService.setThreadCount(1);
@@ -496,6 +497,7 @@ namespace lms::scanner
.abortScan = _abortScan,
.db = _db,
.fileScanners = _fileScanners,
.cachePath = _cachePath
};
// Order is important: steps are sequential
@@ -31,11 +31,12 @@
#include <Wt/WIOService.h>
#include <Wt/WSignal.h>
#include "FileScanners.hpp"
#include "ScannerSettings.hpp"
#include "database/IDb.hpp"
#include "database/Session.hpp"
#include "services/scanner/IScannerService.hpp"
#include "FileScanners.hpp"
#include "ScannerSettings.hpp"
#include "steps/IScanStep.hpp"
namespace lms::core
@@ -53,7 +54,7 @@ namespace lms::scanner
class ScannerService : public IScannerService
{
public:
ScannerService(db::IDb& db);
ScannerService(db::IDb& db, const std::filesystem::path& cachePath);
~ScannerService() override;
ScannerService(const ScannerService&) = delete;
ScannerService& operator=(const ScannerService&) = delete;
@@ -89,6 +90,7 @@ namespace lms::scanner
db::IDb& _db;
std::unique_ptr<core::IJobScheduler> _jobScheduler;
const std::filesystem::path _cachePath;
FileScanners _fileScanners;
std::vector<std::unique_ptr<IScanStep>> _scanSteps;
@@ -30,6 +30,7 @@ namespace lms::scanner
, _db{ initParams.db }
, _jobScheduler{ initParams.jobScheduler }
, _fileScanners(initParams.fileScanners)
, _cachePath{ initParams.cachePath }
, _lastScanSettings{ initParams.lastScanSettings }
{
}
@@ -55,6 +55,7 @@ namespace lms::scanner
bool& abortScan;
db::IDb& db;
const FileScanners& fileScanners;
const std::filesystem::path& cachePath;
};
ScanStepBase(InitParams& initParams);
~ScanStepBase() override;
@@ -65,6 +66,7 @@ namespace lms::scanner
core::IJobScheduler& getJobScheduler() { return _jobScheduler; };
const ScannerSettings* getLastScanSettings() const { return _lastScanSettings; }
const FileScanners& getFileScanners() const { return _fileScanners; }
const std::filesystem::path& getCachePath() const { return _cachePath; }
void addError(ScanContext& context, std::shared_ptr<ScanError> error);
@@ -83,6 +85,7 @@ namespace lms::scanner
private:
core::IJobScheduler& _jobScheduler;
const FileScanners& _fileScanners;
const std::filesystem::path& _cachePath;
const ScannerSettings* _lastScanSettings{};
ScanErrorLogger _scanErrorLogger;
@@ -63,6 +63,9 @@ namespace lms::scanner
{
}
CheckForRemovedFilesJob(const CheckForRemovedFilesJob&) = delete;
CheckForRemovedFilesJob& operator=(const CheckForRemovedFilesJob&) = delete;
std::size_t getProcessedCount() const { return _processedCount; }
std::span<const IdType> getObjectsToRemove() const { return _objectsToRemove; }
@@ -90,8 +93,7 @@ namespace lms::scanner
return false;
}
// For each track, make sure the the file still exists
// and still belongs to a media directory
// For file, make sure the the file still exists, is a regular file, is in a media directory and is of a supported format
if (!fileEntry.exists() || !fileEntry.is_regular_file())
{
LMS_LOG(DBUPDATER, DEBUG, "Removing " << p << ": missing");
@@ -151,7 +153,7 @@ namespace lms::scanner
}
template<typename Object>
bool fetchNextFilesToCheck(db::Session& session, typename Object::IdType& lastCheckedId, std::vector<FileToCheck<typename Object::IdType>>& filesToCheck)
bool fetchNextFilesToCheck(db::Session& session, typename Object::IdType& lastCheckedId, const std::filesystem::path& cachepath, std::vector<FileToCheck<typename Object::IdType>>& filesToCheck)
{
constexpr std::size_t batchSize{ 200 };
@@ -164,6 +166,10 @@ namespace lms::scanner
{
const typename Object::IdType previousLastCheckedId{ lastCheckedId };
Object::findAbsoluteFilePath(session, lastCheckedId, batchSize, [&](Object::IdType objectId, const std::filesystem::path& filePath) {
// Do not consider files in the cache directory as they are not managed by the scanner itself
if (core::pathUtils::isPathInRootPath(filePath, cachepath))
return;
// special case for track lyrics, only check external lyrics
if constexpr (std::is_same_v<Object, db::TrackLyrics>)
{
@@ -242,12 +248,11 @@ namespace lms::scanner
ObjectIdType lastCheckedId;
std::vector<FileToCheck<ObjectIdType>> filesToCheck;
while (fetchNextFilesToCheck<Object>(session, lastCheckedId, filesToCheck))
while (fetchNextFilesToCheck<Object>(session, lastCheckedId, getCachePath(), filesToCheck))
queue.push(std::make_unique<CheckForRemovedFilesJob<ObjectIdType>>(_settings, getFileScanners(), filesToCheck));
}
// process all remaining objects
context.stats.deletions += removeObjects<Object>(session, objectIdsToRemove, false);
}
} // namespace lms::scanner