-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathRecentBooksStore.cpp
More file actions
140 lines (122 loc) · 4.99 KB
/
Copy pathRecentBooksStore.cpp
File metadata and controls
140 lines (122 loc) · 4.99 KB
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "RecentBooksStore.h"
#include <Epub.h>
#include <FsHelpers.h>
#include <HalStorage.h>
#include <Logging.h>
#include <Xtc.h>
#include <algorithm>
#include <iterator>
void RecentBooksStore::toJson(JsonDocument& doc) const {
JsonArray arr = doc["books"].to<JsonArray>();
for (const auto& book : recentBooks) {
JsonObject obj = arr.add<JsonObject>();
obj["path"] = book.path;
obj["title"] = book.title;
obj["author"] = book.author;
obj["coverBmpPath"] = book.coverBmpPath;
}
}
bool RecentBooksStore::fromJson(JsonVariantConst doc) {
// Tolerate a missing/invalid 'books' key (treat as empty list); only a
// JSON parse error is fatal. A null JsonArray iterates zero times.
recentBooks.clear();
JsonArrayConst arr = doc["books"].as<JsonArrayConst>();
recentBooks.reserve(std::min(arr.size(), static_cast<size_t>(MAX_RECENT_BOOKS)));
for (JsonObjectConst obj : arr) {
if (getCount() >= MAX_RECENT_BOOKS) break;
RecentBook book;
book.path = obj["path"] | "";
book.title = obj["title"] | "";
book.author = obj["author"] | "";
book.coverBmpPath = obj["coverBmpPath"] | "";
recentBooks.push_back(book);
}
LOG_DBG("RBS", "Recent books loaded from file (%d entries)", getCount());
return true;
}
void RecentBooksStore::addBook(const std::string& path, const std::string& title, const std::string& author,
const std::string& coverBmpPath) {
// Drop stale entries first so a new add can't evict a valid book in their stead.
pruneMissing();
// Remove existing entry if present
auto it =
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
if (it != recentBooks.end()) {
recentBooks.erase(it);
}
// Add to front
recentBooks.insert(recentBooks.begin(), {path, title, author, coverBmpPath});
// Trim to max size
if (recentBooks.size() > MAX_RECENT_BOOKS) {
recentBooks.resize(MAX_RECENT_BOOKS);
}
saveToFile();
}
void RecentBooksStore::updateBook(const std::string& path, const std::string& title, const std::string& author,
const std::string& coverBmpPath) {
auto it =
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
if (it != recentBooks.end()) {
RecentBook& book = *it;
book.title = title;
book.author = author;
book.coverBmpPath = coverBmpPath;
saveToFile();
}
}
bool RecentBooksStore::removeByPath(const std::string& path) {
auto it =
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
if (it == recentBooks.end()) {
return false;
}
recentBooks.erase(it);
if (!saveToFile()) {
LOG_ERR("RBS", "Failed to persist removal of recent book: %s", path.c_str());
}
return true;
}
void RecentBooksStore::updatePath(const std::string& oldPath, const std::string& newPath,
const std::string& oldCachePath, const std::string& newCachePath) {
auto it = std::find_if(recentBooks.begin(), recentBooks.end(),
[&](const RecentBook& book) { return book.path == oldPath; });
if (it == recentBooks.end()) {
return;
}
it->path = newPath;
if (!oldCachePath.empty() && !it->coverBmpPath.empty() && it->coverBmpPath.rfind(oldCachePath, 0) == 0) {
it->coverBmpPath = newCachePath + it->coverBmpPath.substr(oldCachePath.size());
}
saveToFile();
}
bool RecentBooksStore::isMissing(const RecentBook& book) { return !Storage.exists(book.path.c_str()); }
bool RecentBooksStore::pruneMissing() {
const size_t before = recentBooks.size();
recentBooks.erase(std::remove_if(recentBooks.begin(), recentBooks.end(), &isMissing), recentBooks.end());
return recentBooks.size() != before;
}
RecentBook RecentBooksStore::getDataFromBook(std::string path) const {
std::string lastBookFileName = "";
const size_t lastSlash = path.find_last_of('/');
if (lastSlash != std::string::npos) {
lastBookFileName = path.substr(lastSlash + 1);
}
LOG_DBG("RBS", "Loading recent book: %s", path.c_str());
// If epub, try to load the metadata for title/author and cover.
// Use buildIfMissing=false to avoid heavy epub loading on boot; getTitle()/getAuthor() may be
// blank until the book is opened, and entries with missing title are omitted from recent list.
if (FsHelpers::hasEpubExtension(lastBookFileName)) {
Epub epub(path, "/.crosspoint");
epub.load(false, true);
return RecentBook{path, epub.getTitle(), epub.getAuthor(), epub.getThumbBmpPath()};
} else if (FsHelpers::hasXtcExtension(lastBookFileName)) {
// Handle XTC file
Xtc xtc(path, "/.crosspoint");
if (xtc.load()) {
return RecentBook{path, xtc.getTitle(), xtc.getAuthor(), xtc.getThumbBmpPath()};
}
} else if (FsHelpers::hasTxtExtension(lastBookFileName) || FsHelpers::hasMarkdownExtension(lastBookFileName)) {
return RecentBook{path, lastBookFileName, "", ""};
}
return RecentBook{path, "", "", ""};
}