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