db/upnp: eliminate the std::forward_list, use IterableSplitString()
This commit is contained in:
parent
cc41e95806
commit
95842e7984
|
@ -21,7 +21,7 @@
|
|||
#include "tag/Table.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "util/RecursiveMap.hxx"
|
||||
#include "util/SplitString.hxx"
|
||||
#include "util/StringSplit.hxx"
|
||||
#include "config/Block.hxx"
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
@ -35,6 +35,17 @@
|
|||
static constexpr const char *rootid = "0";
|
||||
static constexpr std::string_view rootid_sv{rootid};
|
||||
|
||||
static std::string_view
|
||||
AfterRootIdSegment(std::string_view &uri) noexcept
|
||||
{
|
||||
if (uri.size() >= rootid_sv.size() + 2 &&
|
||||
uri.starts_with(rootid_sv) &&
|
||||
uri[rootid_sv.size()] == '/')
|
||||
return uri.substr(rootid_sv.size() + 1);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
class UpnpSongData {
|
||||
protected:
|
||||
std::string uri;
|
||||
|
@ -97,7 +108,7 @@ public:
|
|||
|
||||
private:
|
||||
void VisitServer(const ContentDirectoryService &server,
|
||||
std::forward_list<std::string_view> &&vpath,
|
||||
std::string_view uri,
|
||||
const DatabaseSelection &selection,
|
||||
const VisitDirectory& visit_directory,
|
||||
const VisitSong& visit_song,
|
||||
|
@ -117,7 +128,7 @@ private:
|
|||
const DatabaseSelection &selection) const;
|
||||
|
||||
UPnPDirObject Namei(const ContentDirectoryService &server,
|
||||
std::forward_list<std::string_view> &&vpath) const;
|
||||
std::string_view uri) const;
|
||||
|
||||
/**
|
||||
* Take server and objid, return metadata.
|
||||
|
@ -178,28 +189,19 @@ UpnpDatabase::ReturnSong(const LightSong *_song) const noexcept
|
|||
const LightSong *
|
||||
UpnpDatabase::GetSong(std::string_view uri) const
|
||||
{
|
||||
auto vpath = SplitString(uri, '/');
|
||||
if (vpath.empty())
|
||||
auto [server_name, uri_in_server] = Split(uri, '/');
|
||||
if (server_name.empty() || uri_in_server.empty())
|
||||
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
||||
"No such song");
|
||||
|
||||
auto server = discovery->GetServer(vpath.front());
|
||||
vpath.pop_front();
|
||||
|
||||
if (vpath.empty())
|
||||
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
||||
"No such song");
|
||||
auto server = discovery->GetServer(server_name);
|
||||
|
||||
UPnPDirObject dirent;
|
||||
if (vpath.front() != rootid_sv) {
|
||||
dirent = Namei(server, std::move(vpath));
|
||||
if (const auto id = AfterRootIdSegment(uri_in_server);
|
||||
id.data() == nullptr) {
|
||||
dirent = Namei(server, uri_in_server);
|
||||
} else {
|
||||
vpath.pop_front();
|
||||
if (vpath.empty())
|
||||
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
||||
"No such song");
|
||||
|
||||
dirent = ReadNode(server, std::string(vpath.front()).c_str());
|
||||
dirent = ReadNode(server, std::string{id}.c_str());
|
||||
}
|
||||
|
||||
return new UpnpSong(std::move(dirent), uri);
|
||||
|
@ -398,9 +400,9 @@ UpnpDatabase::BuildPath(const ContentDirectoryService &server,
|
|||
// Take server and internal title pathname and return objid and metadata.
|
||||
UPnPDirObject
|
||||
UpnpDatabase::Namei(const ContentDirectoryService &server,
|
||||
std::forward_list<std::string_view> &&vpath) const
|
||||
std::string_view uri) const
|
||||
{
|
||||
if (vpath.empty())
|
||||
if (uri.empty())
|
||||
// looking for root info
|
||||
return ReadNode(server, rootid);
|
||||
|
||||
|
@ -410,14 +412,16 @@ UpnpDatabase::Namei(const ContentDirectoryService &server,
|
|||
while (true) {
|
||||
auto dirbuf = server.readDir(handle, objid.c_str());
|
||||
|
||||
const auto [name, rest] = Split(uri, '/');
|
||||
|
||||
// Look for the name in the sub-container list
|
||||
UPnPDirObject *child = dirbuf.FindObject(vpath.front());
|
||||
UPnPDirObject *child = dirbuf.FindObject(name);
|
||||
if (child == nullptr)
|
||||
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
||||
"No such object");
|
||||
|
||||
vpath.pop_front();
|
||||
if (vpath.empty())
|
||||
uri = rest;
|
||||
if (uri.empty())
|
||||
return std::move(*child);
|
||||
|
||||
if (child->type != UPnPDirObject::Type::CONTAINER)
|
||||
|
@ -486,7 +490,7 @@ VisitObject(const UPnPDirObject &object, const char *uri,
|
|||
// really just one path parameter.
|
||||
void
|
||||
UpnpDatabase::VisitServer(const ContentDirectoryService &server,
|
||||
std::forward_list<std::string_view> &&vpath,
|
||||
std::string_view uri,
|
||||
const DatabaseSelection &selection,
|
||||
const VisitDirectory& visit_directory,
|
||||
const VisitSong& visit_song,
|
||||
|
@ -500,19 +504,12 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server,
|
|||
/* !Note: this *can't* be handled by Namei further down,
|
||||
because the path is not valid for traversal. Besides, it's
|
||||
just faster to access the target node directly */
|
||||
if (!vpath.empty() && vpath.front() == rootid_sv) {
|
||||
vpath.pop_front();
|
||||
if (vpath.empty())
|
||||
return;
|
||||
|
||||
const std::string objid(vpath.front());
|
||||
vpath.pop_front();
|
||||
if (!vpath.empty())
|
||||
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
||||
"Not found");
|
||||
if (uri == rootid_sv)
|
||||
return;
|
||||
|
||||
if (const auto id = AfterRootIdSegment(uri); id.data() != nullptr) {
|
||||
if (visit_song) {
|
||||
auto dirent = ReadNode(server, objid.c_str());
|
||||
auto dirent = ReadNode(server, std::string{id}.c_str());
|
||||
|
||||
if (dirent.type != UPnPDirObject::Type::ITEM ||
|
||||
dirent.item_class != UPnPDirObject::ItemClass::MUSIC)
|
||||
|
@ -529,7 +526,7 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server,
|
|||
}
|
||||
|
||||
// Translate the target path into an object id and the associated metadata.
|
||||
const auto tdirent = Namei(server, std::move(vpath));
|
||||
const auto tdirent = Namei(server, uri);
|
||||
|
||||
/* If recursive is set, this is a search... No use sending it
|
||||
if the filter is empty. In this case, we implement limited
|
||||
|
@ -556,9 +553,9 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server,
|
|||
data to the client when we're done anyway. */
|
||||
const auto contents = server.readDir(handle, tdirent.id.c_str());
|
||||
for (const auto &dirent : contents.objects) {
|
||||
const std::string uri = PathTraitsUTF8::Build(base_uri,
|
||||
dirent.name.c_str());
|
||||
VisitObject(dirent, uri.c_str(),
|
||||
const std::string child_uri = PathTraitsUTF8::Build(base_uri,
|
||||
dirent.name.c_str());
|
||||
VisitObject(dirent, child_uri.c_str(),
|
||||
selection,
|
||||
visit_directory,
|
||||
visit_song, visit_playlist);
|
||||
|
@ -583,8 +580,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
|
|||
{
|
||||
DatabaseVisitorHelper helper(CheckSelection(selection), visit_song);
|
||||
|
||||
auto vpath = SplitString(selection.uri, '/');
|
||||
if (vpath.empty()) {
|
||||
if (selection.uri.empty()) {
|
||||
for (const auto &server : discovery->GetDirectories()) {
|
||||
if (visit_directory) {
|
||||
const LightDirectory d(server.GetFriendlyName().c_str(),
|
||||
|
@ -593,7 +589,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
|
|||
}
|
||||
|
||||
if (selection.recursive)
|
||||
VisitServer(server, std::move(vpath), selection,
|
||||
VisitServer(server, {}, selection,
|
||||
visit_directory, visit_song,
|
||||
visit_playlist);
|
||||
}
|
||||
|
@ -603,10 +599,10 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
|
|||
}
|
||||
|
||||
// We do have a path: the first element selects the server
|
||||
auto server = discovery->GetServer(vpath.front());
|
||||
vpath.pop_front();
|
||||
const auto [server_name, uri_in_server] = Split(std::string_view{selection.uri}, '/');
|
||||
auto server = discovery->GetServer(server_name);
|
||||
|
||||
VisitServer(server, std::move(vpath), selection,
|
||||
VisitServer(server, uri_in_server, selection,
|
||||
visit_directory, visit_song, visit_playlist);
|
||||
helper.Commit();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue