1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "index.hpp"
#include <stdint.h>
#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},
};
static auto missing_component_text(Tag tag) -> std::optional<std::string> {
switch (tag) {
case Tag::kArtist:
return "Unknown Artist";
case Tag::kAlbum:
return "Unknown Album";
case Tag::kGenre:
return "Unknown Genre";
case Tag::kAlbumTrack:
case Tag::kDuration:
case Tag::kTitle:
default:
return {};
}
}
auto Index(const IndexInfo& info, const Track& t, leveldb::WriteBatch* batch)
-> bool {
IndexKey key{
.header{
.id = info.id,
.depth = 0,
.components_hash = 0,
},
.item = {},
.track = {},
};
std::optional<std::string> value;
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));
if (text) {
key.item = *text;
} else {
key.item = {};
value = missing_component_text(info.components.at(i));
}
// 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();
if (info.components.at(i) != Tag::kTitle) {
value = t.TitleOrFilename();
}
} else {
key.track = {};
}
auto encoded = EncodeIndexKey(key);
batch->Put(encoded.slice, value.value_or(""));
// 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 true;
}
auto ExpandHeader(const IndexKey::Header& header,
const std::optional<std::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
|