Subsonic API: added the getScanStatus and startScan commands. fixes #62
This commit is contained in:
@@ -310,7 +310,7 @@ MediaScanner::getStatus()
|
|||||||
{
|
{
|
||||||
Status res;
|
Status res;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock {_statusMutex};
|
std::shared_lock lock {_statusMutex};
|
||||||
|
|
||||||
res.currentState = _curState;
|
res.currentState = _curState;
|
||||||
res.nextScheduledScan = _nextScheduledScan;
|
res.nextScheduledScan = _nextScheduledScan;
|
||||||
@@ -367,7 +367,7 @@ MediaScanner::scheduleNextScan()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock {_statusMutex};
|
std::unique_lock lock {_statusMutex};
|
||||||
_curState = nextScanDateTime.isValid() ? State::Scheduled : State::NotScheduled;
|
_curState = nextScanDateTime.isValid() ? State::Scheduled : State::NotScheduled;
|
||||||
_nextScheduledScan = nextScanDateTime;
|
_nextScheduledScan = nextScanDateTime;
|
||||||
}
|
}
|
||||||
@@ -421,8 +421,10 @@ MediaScanner::scan(boost::system::error_code err)
|
|||||||
if (err)
|
if (err)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
scanStarted().emit();
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock {_statusMutex};
|
std::unique_lock lock {_statusMutex};
|
||||||
_curState = State::InProgress;
|
_curState = State::InProgress;
|
||||||
_nextScheduledScan = {};
|
_nextScheduledScan = {};
|
||||||
}
|
}
|
||||||
@@ -464,7 +466,7 @@ MediaScanner::scan(boost::system::error_code err)
|
|||||||
{
|
{
|
||||||
stats.stopTime = Wt::WLocalDateTime::currentDateTime().toUTC();
|
stats.stopTime = Wt::WLocalDateTime::currentDateTime().toUTC();
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock {_statusMutex};
|
std::unique_lock lock {_statusMutex};
|
||||||
|
|
||||||
_lastCompleteScanStats = std::move(stats);
|
_lastCompleteScanStats = std::move(stats);
|
||||||
_inProgressScanStats.reset();
|
_inProgressScanStats.reset();
|
||||||
@@ -476,7 +478,7 @@ MediaScanner::scan(boost::system::error_code err)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock {_statusMutex};
|
std::unique_lock lock {_statusMutex};
|
||||||
|
|
||||||
_curState = State::NotScheduled;
|
_curState = State::NotScheduled;
|
||||||
_inProgressScanStats.reset();
|
_inProgressScanStats.reset();
|
||||||
@@ -584,13 +586,15 @@ MediaScanner::refreshScanSettings()
|
|||||||
void
|
void
|
||||||
MediaScanner::notifyInProgress(const ScanStats& stats)
|
MediaScanner::notifyInProgress(const ScanStats& stats)
|
||||||
{
|
{
|
||||||
|
const ScanProgressStats progressStats {stats.toProgressStats()};
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock {_statusMutex};
|
std::unique_lock lock {_statusMutex};
|
||||||
_inProgressScanStats = stats.toProgressStats();
|
_inProgressScanStats = progressStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::system_clock::time_point now {std::chrono::system_clock::now()};
|
const std::chrono::system_clock::time_point now {std::chrono::system_clock::now()};
|
||||||
_sigScanInProgress(*_inProgressScanStats);
|
_sigScanInProgress(progressStats);
|
||||||
_lastScanInProgressEmit = now;
|
_lastScanInProgressEmit = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <shared_mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
@@ -59,6 +59,7 @@ class MediaScanner : public IMediaScanner
|
|||||||
|
|
||||||
Status getStatus() override;
|
Status getStatus() override;
|
||||||
|
|
||||||
|
Wt::Signal<>& scanStarted() override { return _sigScanStarted; }
|
||||||
Wt::Signal<>& scanComplete() override { return _sigScanComplete; }
|
Wt::Signal<>& scanComplete() override { return _sigScanComplete; }
|
||||||
Wt::Signal<ScanProgressStats>& scanInProgress() override { return _sigScanInProgress; }
|
Wt::Signal<ScanProgressStats>& scanInProgress() override { return _sigScanInProgress; }
|
||||||
Wt::Signal<Wt::WDateTime>& scheduled() override { return _sigScheduled; }
|
Wt::Signal<Wt::WDateTime>& scheduled() override { return _sigScheduled; }
|
||||||
@@ -88,21 +89,22 @@ class MediaScanner : public IMediaScanner
|
|||||||
void notifyInProgressIfNeeded(const ScanStats& stats);
|
void notifyInProgressIfNeeded(const ScanStats& stats);
|
||||||
void notifyInProgress(const ScanStats& stats);
|
void notifyInProgress(const ScanStats& stats);
|
||||||
|
|
||||||
bool _running {};
|
bool _running {};
|
||||||
Wt::WIOService _ioService;
|
Wt::WIOService _ioService;
|
||||||
boost::asio::system_timer _scheduleTimer {_ioService};
|
boost::asio::system_timer _scheduleTimer {_ioService};
|
||||||
Wt::Signal<> _sigScanComplete;
|
Wt::Signal<> _sigScanStarted;
|
||||||
Wt::Signal<ScanProgressStats> _sigScanInProgress;
|
Wt::Signal<> _sigScanComplete;
|
||||||
|
Wt::Signal<ScanProgressStats> _sigScanInProgress;
|
||||||
std::chrono::system_clock::time_point _lastScanInProgressEmit {};
|
std::chrono::system_clock::time_point _lastScanInProgressEmit {};
|
||||||
Wt::Signal<Wt::WDateTime> _sigScheduled;
|
Wt::Signal<Wt::WDateTime> _sigScheduled;
|
||||||
Database::Session _dbSession;
|
Database::Session _dbSession;
|
||||||
std::unique_ptr<MetaData::IParser> _metadataParser;
|
std::unique_ptr<MetaData::IParser> _metadataParser;
|
||||||
|
|
||||||
std::mutex _statusMutex;
|
std::shared_mutex _statusMutex;
|
||||||
State _curState {State::NotScheduled};
|
State _curState {State::NotScheduled};
|
||||||
std::optional<ScanStats> _lastCompleteScanStats;
|
std::optional<ScanStats> _lastCompleteScanStats;
|
||||||
std::optional<ScanProgressStats> _inProgressScanStats;
|
std::optional<ScanProgressStats> _inProgressScanStats;
|
||||||
Wt::WDateTime _nextScheduledScan;
|
Wt::WDateTime _nextScheduledScan;
|
||||||
|
|
||||||
// Current scan settings
|
// Current scan settings
|
||||||
std::size_t _scanVersion {};
|
std::size_t _scanVersion {};
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ class IMediaScanner
|
|||||||
|
|
||||||
virtual Status getStatus() = 0;
|
virtual Status getStatus() = 0;
|
||||||
|
|
||||||
|
// Called just after scan start
|
||||||
|
virtual Wt::Signal<>& scanStarted() = 0;
|
||||||
|
|
||||||
// Called just after scan complete
|
// Called just after scan complete
|
||||||
virtual Wt::Signal<>& scanComplete() = 0;
|
virtual Wt::Signal<>& scanComplete() = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
add_library(lmssubsonic SHARED
|
add_library(lmssubsonic SHARED
|
||||||
impl/ParameterParsing.cpp
|
impl/ParameterParsing.cpp
|
||||||
|
impl/Scan.cpp
|
||||||
impl/Stream.cpp
|
impl/Stream.cpp
|
||||||
impl/SubsonicId.cpp
|
impl/SubsonicId.cpp
|
||||||
impl/SubsonicResource.cpp
|
impl/SubsonicResource.cpp
|
||||||
@@ -26,6 +27,7 @@ target_link_libraries(lmssubsonic PRIVATE
|
|||||||
|
|
||||||
target_link_libraries(lmssubsonic PUBLIC
|
target_link_libraries(lmssubsonic PUBLIC
|
||||||
lmsdatabase
|
lmsdatabase
|
||||||
|
lmsscanner
|
||||||
wt
|
wt
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Scan.hpp"
|
||||||
|
|
||||||
|
#include "scanner/IMediaScanner.hpp"
|
||||||
|
#include "utils/Service.hpp"
|
||||||
|
|
||||||
|
namespace API::Subsonic::Scan
|
||||||
|
{
|
||||||
|
using namespace Scanner;
|
||||||
|
|
||||||
|
static
|
||||||
|
Response::Node
|
||||||
|
createStatusResponseNode()
|
||||||
|
{
|
||||||
|
Response::Node statusResponse;
|
||||||
|
|
||||||
|
const IMediaScanner::Status scanStatus {ServiceProvider<IMediaScanner>::get()->getStatus()};
|
||||||
|
|
||||||
|
statusResponse.setAttribute("scanning", scanStatus.currentState == IMediaScanner::State::InProgress ? "true" : "false");
|
||||||
|
if (scanStatus.currentState == IMediaScanner::State::InProgress && scanStatus.inProgressScanStats)
|
||||||
|
statusResponse.setAttribute("count", std::to_string(scanStatus.inProgressScanStats->processedFiles));
|
||||||
|
|
||||||
|
return statusResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Response
|
||||||
|
handleGetScanStatus(RequestContext&)
|
||||||
|
{
|
||||||
|
Response response {Response::createOkResponse()};
|
||||||
|
response.addNode("scanStatus", createStatusResponseNode());
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response
|
||||||
|
handleStartScan(RequestContext&)
|
||||||
|
{
|
||||||
|
ServiceProvider<IMediaScanner>::get()->requestImmediateScan();
|
||||||
|
|
||||||
|
Response response {Response::createOkResponse()};
|
||||||
|
response.addNode("scanStatus", createStatusResponseNode());
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "RequestContext.hpp"
|
||||||
|
#include "SubsonicResponse.hpp"
|
||||||
|
|
||||||
|
namespace API::Subsonic::Scan
|
||||||
|
{
|
||||||
|
Response handleGetScanStatus(RequestContext& context);
|
||||||
|
Response handleStartScan(RequestContext& context);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
#include "utils/Utils.hpp"
|
#include "utils/Utils.hpp"
|
||||||
#include "ParameterParsing.hpp"
|
#include "ParameterParsing.hpp"
|
||||||
#include "RequestContext.hpp"
|
#include "RequestContext.hpp"
|
||||||
|
#include "Scan.hpp"
|
||||||
#include "Stream.hpp"
|
#include "Stream.hpp"
|
||||||
#include "SubsonicResponse.hpp"
|
#include "SubsonicResponse.hpp"
|
||||||
|
|
||||||
@@ -1860,8 +1861,8 @@ static std::unordered_map<std::string, RequestEntryPointInfo> requestEntryPoints
|
|||||||
{"savePlayQueue", {handleNotImplemented, false}},
|
{"savePlayQueue", {handleNotImplemented, false}},
|
||||||
|
|
||||||
// Media library scanning
|
// Media library scanning
|
||||||
{"getScanStatus", {handleNotImplemented, true}},
|
{"getScanStatus", {Scan::handleGetScanStatus, true}},
|
||||||
{"startScan", {handleNotImplemented, true}},
|
{"startScan", {Scan::handleStartScan, true}},
|
||||||
};
|
};
|
||||||
|
|
||||||
using MediaRetrievalHandlerFunc = std::function<void(RequestContext&, const Wt::Http::Request&, Wt::Http::Response&)>;
|
using MediaRetrievalHandlerFunc = std::function<void(RequestContext&, const Wt::Http::Request&, Wt::Http::Response&)>;
|
||||||
|
|||||||
@@ -523,6 +523,16 @@ LmsApplication::createHome()
|
|||||||
// Events from MediaScanner
|
// Events from MediaScanner
|
||||||
{
|
{
|
||||||
const std::string sessionId {LmsApp->sessionId()};
|
const std::string sessionId {LmsApp->sessionId()};
|
||||||
|
|
||||||
|
ServiceProvider<Scanner::IMediaScanner>::get()->scanStarted().connect(this, [=] ()
|
||||||
|
{
|
||||||
|
Wt::WServer::instance()->post(sessionId, [=]
|
||||||
|
{
|
||||||
|
_events.dbScanStarted.emit();
|
||||||
|
triggerUpdate();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
ServiceProvider<Scanner::IMediaScanner>::get()->scanComplete().connect(this, [=] ()
|
ServiceProvider<Scanner::IMediaScanner>::get()->scanComplete().connect(this, [=] ()
|
||||||
{
|
{
|
||||||
Wt::WServer::instance()->post(sessionId, [=]
|
Wt::WServer::instance()->post(sessionId, [=]
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ struct Events
|
|||||||
Wt::Signal<LmsApplicationInfo> appClosed;
|
Wt::Signal<LmsApplicationInfo> appClosed;
|
||||||
|
|
||||||
// Database events
|
// Database events
|
||||||
|
Wt::Signal<> dbScanStarted;
|
||||||
Wt::Signal<> dbScanned;
|
Wt::Signal<> dbScanned;
|
||||||
Wt::Signal<Scanner::ScanProgressStats> dbScanInProgress;
|
Wt::Signal<Scanner::ScanProgressStats> dbScanInProgress;
|
||||||
Wt::Signal<Wt::WDateTime> dbScanScheduled;
|
Wt::Signal<Wt::WDateTime> dbScanScheduled;
|
||||||
|
|||||||
@@ -248,7 +248,6 @@ DatabaseSettingsView::refreshView()
|
|||||||
immScanBtn->clicked().connect([=] ()
|
immScanBtn->clicked().connect([=] ()
|
||||||
{
|
{
|
||||||
ServiceProvider<Scanner::IMediaScanner>::get()->requestImmediateScan();
|
ServiceProvider<Scanner::IMediaScanner>::get()->requestImmediateScan();
|
||||||
LmsApp->notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-launched"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
t->updateView(model.get());
|
t->updateView(model.get());
|
||||||
|
|||||||
@@ -121,6 +121,10 @@ DatabaseStatus::DatabaseStatus()
|
|||||||
|
|
||||||
auto onDbEvent = [&]() { refreshContents(); };
|
auto onDbEvent = [&]() { refreshContents(); };
|
||||||
|
|
||||||
|
LmsApp->getEvents().dbScanStarted.connect(this, []()
|
||||||
|
{
|
||||||
|
LmsApp->notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-launched"));
|
||||||
|
});
|
||||||
LmsApp->getEvents().dbScanned.connect(this, onDbEvent);
|
LmsApp->getEvents().dbScanned.connect(this, onDbEvent);
|
||||||
LmsApp->getEvents().dbScanInProgress.connect(this, onDbEvent);
|
LmsApp->getEvents().dbScanInProgress.connect(this, onDbEvent);
|
||||||
LmsApp->getEvents().dbScanScheduled.connect(this, onDbEvent);
|
LmsApp->getEvents().dbScanScheduled.connect(this, onDbEvent);
|
||||||
|
|||||||
Reference in New Issue
Block a user