/*
* Copyright (C) 2013 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see .
*/
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include "database/ScanSettings.hpp"
#include "database/Session.hpp"
#include "metadata/TagLibParser.hpp"
#include "MediaScannerAddon.hpp"
namespace Scanner {
class MediaScanner
{
public:
MediaScanner(std::unique_ptr dbSession);
void setAddon(MediaScannerAddon& addon);
void start();
void stop();
void restart();
// Async requests
void requestImmediateScan();
void requestReschedule();
struct Stats
{
Wt::WDateTime startTime;
Wt::WDateTime stopTime;
std::size_t skips = 0; // no change since last scan
std::size_t scans = 0; // actually scanned filed
std::size_t scanErrors = 0; // cannot scan file
std::size_t incompleteScans = 0; // Scanned, but not imported (criteria not filled)
std::size_t additions = 0; // Added in DB
std::size_t deletions = 0; // removed from DB
std::size_t updates = 0; // updated file in DB
std::size_t duplicateHashes = 0; // Same file hashes
std::size_t duplicateMBID = 0; // Same MBID
std::size_t totalFiles = 0; // Total number of files to be scanned
std::size_t nbFiles() const { return skips + additions + updates; }
std::size_t nbChanges() const { return additions + deletions + updates; }
std::size_t nbErrors() const { return scanErrors + incompleteScans; }
std::size_t nbDuplicates() const { return duplicateHashes + duplicateMBID; }
float progress() const { return (nbFiles() - totalFiles) / static_cast(totalFiles); }
};
enum class State
{
NotScheduled,
Scheduled,
InProgress,
};
struct Status
{
State currentState {State::NotScheduled};
Wt::WDateTime nextScheduledScan;
std::optional lastScanStats;
std::optional inProgressStats;
};
Status getStatus();
// Called just after scan complete
Wt::Signal& scanComplete() { return _sigScanComplete; }
// Called during scan in progress
Wt::Signal& scanInProgress() { return _sigScanInProgress; }
// Called after a schedule
Wt::Signal& scheduled() { return _sigScheduled; }
private:
// Job handling
void scheduleNextScan();
void scheduleScan(const Wt::WDateTime& dateTime = {});
// Update database (scheduled callback)
void scan(boost::system::error_code ec);
void scanMediaDirectory( const std::filesystem::path& mediaDirectory, bool forceScan, Stats& stats);
// Helpers
void refreshScanSettings();
void countAllFiles(Stats& stats);
void removeMissingTracks(Stats& stats);
void removeOrphanEntries();
void checkDuplicatedAudioFiles(Stats& stats);
void scanAudioFile(const std::filesystem::path& file, bool forceScan, Stats& stats);
Database::IdType doScanAudioFile(const std::filesystem::path& file, Stats& stats);
void notifyInProgressIfNeeded(Stats& stats);
void notifyInProgress(Stats& stats);
bool _running {false};
Wt::WIOService _ioService;
boost::asio::system_timer _scheduleTimer {_ioService};
Wt::Signal _sigScanComplete;
Wt::Signal _sigScanInProgress;
std::chrono::system_clock::time_point _lastScanInProgressEmit {};
Wt::Signal _sigScheduled;
std::unique_ptr _dbSession;
MetaData::TagLibParser _metadataParser;
std::vector _addons;
std::mutex _statusMutex;
State _curState {State::NotScheduled};
std::optional _inProgressStats;
std::optional _lastScanStats;
Wt::WDateTime _nextScheduledScan;
// Current scan settings
std::size_t _scanVersion {};
Wt::WTime _startTime;
Database::ScanSettings::UpdatePeriod _updatePeriod {Database::ScanSettings::UpdatePeriod::Never};
std::set _fileExtensions;
std::filesystem::path _mediaDirectory;
}; // class MediaScanner
} // Scanner