summaryrefslogtreecommitdiff
path: root/src/playlist/source.cpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-10-18 14:35:28 +1100
committerjacqueline <me@jacqueline.id.au>2023-10-18 14:35:28 +1100
commit782e8dc8c25402171fc4724075b998eae4fa2c76 (patch)
treef710f8fe8e84f5a201410520f3f88364a19f76d8 /src/playlist/source.cpp
parent2eb7eaa2a6a5d9ccfe7a0535858778dfb85997cb (diff)
downloadtangara-fw-782e8dc8c25402171fc4724075b998eae4fa2c76.tar.gz
Add better controls for queue manipulation
Diffstat (limited to 'src/playlist/source.cpp')
-rw-r--r--src/playlist/source.cpp163
1 files changed, 163 insertions, 0 deletions
diff --git a/src/playlist/source.cpp b/src/playlist/source.cpp
index 18a7887b..d51d97ab 100644
--- a/src/playlist/source.cpp
+++ b/src/playlist/source.cpp
@@ -22,6 +22,17 @@
namespace playlist {
+auto CreateSourceFromResults(
+ std::weak_ptr<database::Database> db,
+ std::shared_ptr<database::Result<database::IndexRecord>> results)
+ -> std::shared_ptr<IResetableSource> {
+ if (results->values()[0]->track()) {
+ return std::make_shared<IndexRecordSource>(db, results);
+ } else {
+ return std::make_shared<NestedSource>(db, results);
+ }
+}
+
IndexRecordSource::IndexRecordSource(
std::weak_ptr<database::Database> db,
std::shared_ptr<database::Result<database::IndexRecord>> initial)
@@ -142,4 +153,156 @@ auto IndexRecordSource::Reset() -> void {
current_item_ = initial_item_;
}
+NestedSource::NestedSource(
+ std::weak_ptr<database::Database> db,
+ std::shared_ptr<database::Result<database::IndexRecord>> initial)
+ : db_(db),
+ initial_page_(initial),
+ initial_item_(0),
+ current_page_(initial_page_),
+ current_item_(initial_item_),
+ current_child_(CreateChild(initial->values()[0])) {}
+
+auto NestedSource::Current() -> std::optional<database::TrackId> {
+ if (current_child_) {
+ return current_child_->Current();
+ }
+ return {};
+}
+
+auto NestedSource::Advance() -> std::optional<database::TrackId> {
+ if (!current_child_) {
+ return {};
+ }
+
+ auto child_next = current_child_->Advance();
+ if (child_next) {
+ return child_next;
+ }
+ // Our current child has run out of tracks. Move on to the next child.
+ current_item_++;
+ current_child_.reset();
+
+ if (current_item_ >= current_page_->values().size()) {
+ // We're even out of items in this page!
+ auto next_page = current_page_->next_page();
+ if (!next_page) {
+ current_item_--;
+ return {};
+ }
+
+ auto db = db_.lock();
+ if (!db) {
+ return {};
+ }
+
+ current_page_.reset(db->GetPage<database::IndexRecord>(&*next_page).get());
+ current_item_ = 0;
+ }
+ current_child_ = CreateChild(current_page_->values()[current_item_]);
+
+ return Current();
+}
+
+auto NestedSource::Previous() -> std::optional<database::TrackId> {
+ if (current_page_ == initial_page_ && current_item_ <= initial_item_) {
+ return {};
+ }
+
+ current_item_--;
+ current_child_.reset();
+
+ if (current_item_ < 0) {
+ auto prev_page = current_page_->prev_page();
+ if (!prev_page) {
+ return {};
+ }
+
+ auto db = db_.lock();
+ if (!db) {
+ return {};
+ }
+
+ current_page_.reset(db->GetPage<database::IndexRecord>(&*prev_page).get());
+ current_item_ = current_page_->values().size() - 1;
+ }
+ current_child_ = CreateChild(current_page_->values()[current_item_]);
+
+ return Current();
+}
+
+auto NestedSource::Peek(std::size_t n, std::vector<database::TrackId>* out)
+ -> std::size_t {
+ if (current_page_->values().size() <= current_item_) {
+ return {};
+ }
+
+ auto db = db_.lock();
+ if (!db) {
+ return 0;
+ }
+
+ std::size_t items_added = 0;
+
+ std::shared_ptr<database::Result<database::IndexRecord>> working_page =
+ current_page_;
+ std::size_t working_item = current_item_;
+ std::shared_ptr<IResetableSource> working_child = current_child_;
+
+ while (working_child) {
+ auto res = working_child->Peek(n, out);
+ n -= res;
+ items_added += res;
+
+ if (n == 0) {
+ break;
+ } else {
+ working_item++;
+ if (working_item < working_page->values().size()) {
+ working_child = CreateChild(working_page->values()[working_item]);
+ } else {
+ auto next_page = current_page_->next_page();
+ if (!next_page) {
+ break;
+ }
+ working_page.reset(
+ db->GetPage<database::IndexRecord>(&*next_page).get());
+ working_item = 0;
+ working_child = CreateChild(working_page->values()[0]);
+ }
+ }
+ }
+
+ return items_added;
+}
+
+auto NestedSource::Reset() -> void {
+ current_page_ = initial_page_;
+ current_item_ = initial_item_;
+ current_child_ = CreateChild(initial_page_->values()[initial_item_]);
+}
+
+auto NestedSource::CreateChild(std::shared_ptr<database::IndexRecord> record)
+ -> std::shared_ptr<IResetableSource> {
+ auto cont = record->Expand(10);
+ if (!cont) {
+ return {};
+ }
+ auto db = db_.lock();
+ if (!db) {
+ return {};
+ }
+ std::shared_ptr<database::Result<database::IndexRecord>> next_level{
+ db->GetPage<database::IndexRecord>(&*cont).get()};
+ if (!next_level) {
+ return {};
+ }
+ auto next_level_record = next_level->values()[0];
+ if (next_level_record->track()) {
+ return std::make_shared<IndexRecordSource>(db_, next_level);
+ } else {
+ return std::make_shared<NestedSource>(db_, next_level);
+ }
+}
+
} // namespace playlist