Fork of Tangara with customizations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tangara-fw/src/database/index.cpp

123 lines
2.9 KiB

/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "index.hpp"
#include <cstdint>
#include <variant>
#include "komihash.h"
#include "leveldb/write_batch.h"
#include "records.hpp"
namespace database {
const IndexInfo kAlbumsByArtist{
.id = 1,
.name = "Albums by Artist",
.components = {Tag::kArtist, Tag::kAlbum, Tag::kAlbumTrack},
};
const IndexInfo kTracksByGenre{
.id = 2,
.name = "Tracks by Genre",
.components = {Tag::kGenre, Tag::kTitle},
};
const IndexInfo kAllTracks{
.id = 3,
.name = "All Tracks",
.components = {Tag::kTitle},
};
const IndexInfo kAllAlbums{
.id = 4,
.name = "All Albums",
.components = {Tag::kAlbum, Tag::kAlbumTrack},
};
static auto missing_component_text(const Track& track, Tag tag)
-> std::optional<std::pmr::string> {
switch (tag) {
case Tag::kArtist:
return "Unknown Artist";
case Tag::kAlbum:
return "Unknown Album";
case Tag::kGenre:
return "Unknown Genre";
case Tag::kTitle:
return track.TitleOrFilename();
case Tag::kAlbumTrack:
return "0000";
case Tag::kDuration:
default:
return {};
}
}
auto Index(const IndexInfo& info, const Track& t)
-> std::vector<std::pair<IndexKey, std::pmr::string>> {
std::vector<std::pair<IndexKey, std::pmr::string>> out;
IndexKey key{
.header{
.id = info.id,
.depth = 0,
.components_hash = 0,
},
.item = {},
.track = {},
};
auto& col = std::use_facet<std::collate<char>>(std::locale());
for (std::uint8_t i = 0; i < info.components.size(); i++) {
// Fill in the text for this depth.
auto text = t.tags().at(info.components.at(i));
std::pmr::string value;
if (text) {
std::pmr::string orig = *text;
key.item = col.transform(&orig[0], &orig[0] + orig.size());
value = *text;
} else {
key.item = {};
value = missing_component_text(t, info.components.at(i)).value_or("");
}
// If this is the last component, then we should also fill in the track id
// and title.
if (i == info.components.size() - 1) {
key.track = t.data().id;
value = t.TitleOrFilename();
}
out.push_back(std::make_pair(key, value));
// If there are more components after this, then we need to finish by
// narrowing the header with the current title.
if (i < info.components.size() - 1) {
key.header = ExpandHeader(key.header, key.item);
}
}
return out;
}
auto ExpandHeader(const IndexKey::Header& header,
const std::optional<std::pmr::string>& component)
-> IndexKey::Header {
IndexKey::Header ret{header};
ret.depth++;
if (component) {
ret.components_hash =
komihash(component->data(), component->size(), ret.components_hash);
} else {
ret.components_hash = komihash(NULL, 0, ret.components_hash);
}
return ret;
}
} // namespace database