Implement adding to the playback queue from lua

custom
jacqueline 1 year ago
parent b07bfbc6c7
commit 09c0e1608f
  1. 21
      lua/browser.lua
  2. BIN
      lua/img/bat/chg.png
  3. BIN
      lua/img/play_small.png
  4. 13
      lua/main_menu.lua
  5. 1
      lua/widgets.lua
  6. 31
      src/database/database.cpp
  7. 6
      src/database/include/database.hpp
  8. 4
      src/lua/include/lua_database.hpp
  9. 72
      src/lua/lua_database.cpp
  10. 21
      src/lua/lua_queue.cpp
  11. 19
      src/playlist/include/source.hpp
  12. 49
      src/playlist/source.cpp

@ -4,7 +4,7 @@ local legacy_ui = require("legacy_ui")
local database = require("database")
local backstack = require("backstack")
local font = require("font")
local playing = require("playing")
local queue = require("queue")
local browser = {}
@ -40,7 +40,6 @@ function browser.create(opts)
h = lvgl.SIZE_CONTENT,
pad_left = 4,
pad_right = 4,
pad_top = 2,
pad_bottom = 2,
bg_opa = lvgl.OPA(100),
bg_color = "#fafafa",
@ -64,9 +63,18 @@ function browser.create(opts)
h = lvgl.SIZE_CONTENT,
pad_column = 4,
})
local original_iterator = opts.iterator:clone()
local enqueue = widgets.IconBtn(buttons, "//lua/img/enqueue.png", "Enqueue")
enqueue:onClicked(function()
queue.add(original_iterator)
end)
-- enqueue:add_flag(lvgl.FLAG.HIDDEN)
local play = widgets.IconBtn(buttons, "//lua/img/play_small.png", "Play")
play:onClicked(function()
queue.clear()
queue.add(original_iterator)
end
)
end
screen.list = lvgl.List(screen.root, {
@ -88,7 +96,7 @@ function browser.create(opts)
local btn = screen.list:add_btn(nil, tostring(item))
btn:onClicked(function()
local contents = item:contents()
if type(contents) == "function" then
if type(contents) == "userdata" then
backstack.push(function()
return browser.create({
title = opts.title,
@ -97,7 +105,8 @@ function browser.create(opts)
})
end)
else
print("add", contents)
queue.clear()
queue.add(contents)
legacy_ui.open_now_playing()
-- backstack.push(playing)
end
@ -105,13 +114,13 @@ function browser.create(opts)
btn:onevent(lvgl.EVENT.FOCUSED, function()
screen.focused_item = this_item
if screen.last_item - 5 < this_item then
opts.iterator(screen.add_item)
opts.iterator:next(screen.add_item)
end
end)
end
for _ = 1, 8 do
opts.iterator(screen.add_item)
opts.iterator:next(screen.add_item)
end
return screen

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

@ -36,13 +36,12 @@ return function()
for id, idx in ipairs(indexes) do
local btn = menu.list:add_btn(nil, tostring(idx))
btn:onClicked(function()
legacy_ui.open_browse(id);
-- backstack.push(function()
-- return browser {
-- title = tostring(idx),
-- iterator = idx:iter()
-- }
-- end)
backstack.push(function()
return browser {
title = tostring(idx),
iterator = idx:iter()
}
end)
end)
end

@ -151,6 +151,7 @@ function widgets.IconBtn(parent, icon, text)
}
btn:Image { src = icon }
btn:Label { text = text, text_font = font.fusion_10 }
return btn
end
return widgets

@ -874,6 +874,12 @@ Iterator::Iterator(std::weak_ptr<Database> db, const IndexInfo& idx)
Iterator::Iterator(std::weak_ptr<Database> db, const Continuation& c)
: db_(db), pos_mutex_(), current_pos_(c), prev_pos_() {}
Iterator::Iterator(const Iterator& other)
: db_(other.db_),
pos_mutex_(),
current_pos_(other.current_pos_),
prev_pos_(other.prev_pos_) {}
auto Iterator::Next(Callback cb) -> void {
auto db = db_.lock();
if (!db) {
@ -899,6 +905,31 @@ auto Iterator::Next(Callback cb) -> void {
});
}
auto Iterator::NextSync() -> std::optional<IndexRecord> {
auto db = db_.lock();
if (!db) {
return {};
}
return db->worker_task_
->Dispatch<std::optional<IndexRecord>>(
[=]() -> std::optional<IndexRecord> {
std::lock_guard lock{pos_mutex_};
if (!current_pos_) {
return {};
}
std::unique_ptr<Result<IndexRecord>> res{
db->dbGetPage<IndexRecord>(*current_pos_)};
prev_pos_ = current_pos_;
current_pos_ = res->next_page();
if (!res || res->values().empty() || !res->values()[0]) {
ESP_LOGI(kTag, "dropping empty result");
return {};
}
return *res->values()[0];
})
.get();
}
auto Iterator::Prev(Callback cb) -> void {
auto db = db_.lock();
if (!db) {

@ -193,12 +193,18 @@ class Iterator {
public:
Iterator(std::weak_ptr<Database>, const IndexInfo&);
Iterator(std::weak_ptr<Database>, const Continuation&);
Iterator(const Iterator &);
auto database() const { return db_; }
using Callback = std::function<void(std::optional<IndexRecord>)>;
auto Next(Callback) -> void;
auto NextSync() -> std::optional<IndexRecord>;
auto Prev(Callback) -> void;
private:
auto InvokeNull(Callback) -> void;

@ -8,8 +8,12 @@
#include "lua.hpp"
#include "database.hpp"
namespace lua {
auto db_check_iterator(lua_State*, int stack_pos) -> database::Iterator*;
auto RegisterDatabaseModule(lua_State*) -> void;
} // namespace lua

@ -32,7 +32,7 @@ namespace lua {
static constexpr char kDbIndexMetatable[] = "db_index";
static constexpr char kDbRecordMetatable[] = "db_record";
static constexpr char kDbIteratorMetatable[] = "db_record";
static constexpr char kDbIteratorMetatable[] = "db_iterator";
static auto indexes(lua_State* state) -> int {
Bridge* instance = Bridge::Get(state);
@ -94,13 +94,37 @@ static auto push_lua_record(lua_State* L, const database::IndexRecord& r)
std::memcpy(record->text, text.data(), text.size());
}
auto db_check_iterator(lua_State* L, int stack_pos) -> database::Iterator* {
database::Iterator* it = *reinterpret_cast<database::Iterator**>(
luaL_checkudata(L, stack_pos, kDbIteratorMetatable));
return it;
}
static auto push_iterator(lua_State* state,
std::variant<database::Iterator*,
database::Continuation,
database::IndexInfo> val) -> void {
Bridge* instance = Bridge::Get(state);
database::Iterator** data = reinterpret_cast<database::Iterator**>(
lua_newuserdata(state, sizeof(uintptr_t)));
std::visit(
[&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, database::Iterator*>) {
*data = new database::Iterator(*arg);
} else {
*data = new database::Iterator(instance->services().database(), arg);
}
},
val);
luaL_setmetatable(state, kDbIteratorMetatable);
}
static auto db_iterate(lua_State* state) -> int {
luaL_checktype(state, 1, LUA_TFUNCTION);
database::Iterator* it = db_check_iterator(state, 1);
luaL_checktype(state, 2, LUA_TFUNCTION);
int callback_ref = luaL_ref(state, LUA_REGISTRYINDEX);
database::Iterator* it = *reinterpret_cast<database::Iterator**>(
lua_touserdata(state, lua_upvalueindex(1)));
it->Next([=](std::optional<database::IndexRecord> res) {
events::Ui().RunOnTask([=]() {
lua_rawgeti(state, LUA_REGISTRYINDEX, callback_ref);
@ -116,29 +140,22 @@ static auto db_iterate(lua_State* state) -> int {
return 0;
}
static auto db_iterator_gc(lua_State* state) -> int {
database::Iterator** it = reinterpret_cast<database::Iterator**>(
luaL_checkudata(state, 1, kDbIteratorMetatable));
if (it != NULL) {
delete *it;
static auto db_iterator_clone(lua_State* state) -> int {
database::Iterator* it = db_check_iterator(state, 1);
push_iterator(state, it);
return 1;
}
static auto db_iterator_gc(lua_State* state) -> int {
database::Iterator* it = db_check_iterator(state, 1);
delete it;
return 0;
}
static auto push_iterator(
lua_State* state,
std::variant<database::Continuation, database::IndexInfo> val) -> void {
Bridge* instance = Bridge::Get(state);
database::Iterator** data = reinterpret_cast<database::Iterator**>(
lua_newuserdata(state, sizeof(uintptr_t)));
std::visit(
[&](auto&& arg) {
*data = new database::Iterator(instance->services().database(), arg);
},
val);
luaL_setmetatable(state, kDbIteratorMetatable);
lua_pushcclosure(state, db_iterate, 1);
}
static const struct luaL_Reg kDbIteratorFuncs[] = {{"next", db_iterate},
{"clone", db_iterator_clone},
{"__gc", db_iterator_gc},
{NULL, NULL}};
static auto record_text(lua_State* state) -> int {
LuaRecord* data = reinterpret_cast<LuaRecord*>(
@ -219,9 +236,10 @@ static auto lua_database(lua_State* state) -> int {
luaL_setfuncs(state, kDbIndexFuncs, 0);
luaL_newmetatable(state, kDbIteratorMetatable);
lua_pushliteral(state, "__gc");
lua_pushcfunction(state, db_iterator_gc);
lua_settable(state, -3);
lua_pushliteral(state, "__index");
lua_pushvalue(state, -2);
lua_settable(state, -3); // metatable.__index = metatable
luaL_setfuncs(state, kDbIteratorFuncs, 0);
luaL_newmetatable(state, kDbRecordMetatable);
lua_pushliteral(state, "__index");

@ -21,6 +21,7 @@
#include "index.hpp"
#include "property.hpp"
#include "service_locator.hpp"
#include "source.hpp"
#include "ui_events.hpp"
namespace lua {
@ -28,10 +29,28 @@ namespace lua {
[[maybe_unused]] static constexpr char kTag[] = "lua_queue";
static auto queue_add(lua_State* state) -> int {
Bridge* instance = Bridge::Get(state);
if (lua_isinteger(state, 1)) {
instance->services().track_queue().AddLast(luaL_checkinteger(state, 1));
} else {
database::Iterator* it = db_check_iterator(state, 1);
instance->services().track_queue().IncludeLast(
std::make_shared<playlist::IteratorSource>(*it));
}
return 0;
}
static auto queue_clear(lua_State* state) -> int {
Bridge* instance = Bridge::Get(state);
instance->services().track_queue().Clear();
return 0;
}
static const struct luaL_Reg kQueueFuncs[] = {{"add", queue_add}, {NULL, NULL}};
static const struct luaL_Reg kQueueFuncs[] = {{"add", queue_add},
{"clear", queue_clear},
{NULL, NULL}};
static auto lua_queue(lua_State* state) -> int {
luaL_newlib(state, kQueueFuncs);

@ -9,6 +9,7 @@
#include <deque>
#include <memory>
#include <mutex>
#include <stack>
#include <variant>
#include <vector>
@ -73,6 +74,24 @@ class IResetableSource : public ISource {
virtual auto Reset() -> void = 0;
};
class IteratorSource : public IResetableSource {
public:
IteratorSource(const database::Iterator&);
auto Current() -> std::optional<database::TrackId> override;
auto Advance() -> std::optional<database::TrackId> override;
auto Peek(std::size_t n, std::vector<database::TrackId>*)
-> std::size_t override;
auto Previous() -> std::optional<database::TrackId> override;
auto Reset() -> void override;
private:
const database::Iterator& start_;
std::optional<database::TrackId> current_;
std::stack<database::Iterator, std::vector<database::Iterator>> next_;
};
auto CreateSourceFromResults(
std::weak_ptr<database::Database>,
std::shared_ptr<database::Result<database::IndexRecord>>)

@ -22,6 +22,55 @@
namespace playlist {
[[maybe_unused]] static constexpr char kTag[] = "queue_src";
IteratorSource::IteratorSource(const database::Iterator& it)
: start_(it), current_(), next_() {
Reset();
Advance();
}
auto IteratorSource::Current() -> std::optional<database::TrackId> {
return current_;
}
auto IteratorSource::Advance() -> std::optional<database::TrackId> {
ESP_LOGI(kTag, "advancing");
while (!next_.empty()) {
auto next = next_.top().NextSync();
if (!next) {
ESP_LOGI(kTag, "top was empty");
next_.pop();
continue;
}
if (next->track()) {
ESP_LOGI(kTag, "top held track %lu", next->track().value_or(0));
current_ = next->track();
return current_;
}
ESP_LOGI(kTag, "top held records");
next_.push(database::Iterator(start_.database(), next->Expand(1).value()));
}
ESP_LOGI(kTag, "exhausted");
return {};
}
auto IteratorSource::Peek(std::size_t n, std::vector<database::TrackId>*)
-> std::size_t {
return 0;
}
auto IteratorSource::Previous() -> std::optional<database::TrackId> {
return {};
}
auto IteratorSource::Reset() -> void {
while (!next_.empty()) {
next_.pop();
}
next_.push(start_);
}
auto CreateSourceFromResults(
std::weak_ptr<database::Database> db,
std::shared_ptr<database::Result<database::IndexRecord>> results)

Loading…
Cancel
Save