daniel/playlist-queue (#84)

Support for playlist files being opened along side the queue's own playlist. Playlists can be opened from the file browser, if the file ends in ".playlist" (will add support for .m3u as well eventually)

Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/84
Co-authored-by: ailurux <ailuruxx@gmail.com>
Co-committed-by: ailurux <ailuruxx@gmail.com>
custom
ailurux 9 months ago committed by cooljqln
parent 64c8496a91
commit b349599174
  1. 8
      lua/file_browser.lua
  2. 8
      src/tangara/audio/playlist.cpp
  3. 15
      src/tangara/audio/playlist.hpp
  4. 115
      src/tangara/audio/track_queue.cpp
  5. 9
      src/tangara/audio/track_queue.hpp
  6. 41
      src/tangara/lua/file_iterator.cpp
  7. 4
      src/tangara/lua/file_iterator.hpp
  8. 68
      src/tangara/lua/lua_filesystem.cpp
  9. 14
      src/tangara/lua/lua_queue.cpp

@ -62,10 +62,14 @@ return screen:new{
if is_dir then if is_dir then
backstack.push(require("file_browser"):new{ backstack.push(require("file_browser"):new{
title = self.title, title = self.title,
iterator = filesystem.iterator(tostring(item)), iterator = filesystem.iterator(item:filepath()),
breadcrumb = tostring(item) breadcrumb = item:filepath()
}) })
end end
if item:filepath():match("%.playlist$") then
queue.open_playlist(item:filepath())
backstack.push(playing:new())
end
end end
end end
}) })

@ -15,7 +15,7 @@
namespace audio { namespace audio {
[[maybe_unused]] static constexpr char kTag[] = "playlist"; [[maybe_unused]] static constexpr char kTag[] = "playlist";
Playlist::Playlist(std::string playlistFilepath) Playlist::Playlist(const std::string& playlistFilepath)
: filepath_(playlistFilepath), : filepath_(playlistFilepath),
mutex_(), mutex_(),
total_size_(0), total_size_(0),
@ -49,7 +49,7 @@ auto Playlist::size() const -> size_t {
return total_size_; return total_size_;
} }
auto Playlist::append(Item i) -> void { auto MutablePlaylist::append(Item i) -> void {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
auto offset = f_tell(&file_); auto offset = f_tell(&file_);
bool first_entry = current_value_.empty(); bool first_entry = current_value_.empty();
@ -126,7 +126,9 @@ auto Playlist::value() const -> std::string {
return current_value_; return current_value_;
} }
auto Playlist::clear() -> bool { MutablePlaylist::MutablePlaylist(const std::string& playlistFilepath) : Playlist(playlistFilepath) {}
auto MutablePlaylist::clear() -> bool {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
auto res = f_close(&file_); auto res = f_close(&file_);
if (res != FR_OK) { if (res != FR_OK) {

@ -26,23 +26,21 @@ namespace audio {
*/ */
class Playlist { class Playlist {
public: public:
Playlist(std::string playlistFilepath); Playlist(const std::string& playlistFilepath);
~Playlist(); virtual ~Playlist();
using Item = using Item =
std::variant<database::TrackId, database::TrackIterator, std::string>; std::variant<database::TrackId, database::TrackIterator, std::string>;
auto open() -> bool; auto open() -> bool;
auto currentPosition() const -> size_t; auto currentPosition() const -> size_t;
auto size() const -> size_t; auto size() const -> size_t;
auto append(Item i) -> void;
auto skipTo(size_t position) -> void; auto skipTo(size_t position) -> void;
auto next() -> void; auto next() -> void;
auto prev() -> void; auto prev() -> void;
auto value() const -> std::string; auto value() const -> std::string;
auto clear() -> bool;
auto atEnd() -> bool; auto atEnd() -> bool;
auto filepath() -> std::string; auto filepath() -> std::string;
private: protected:
std::string filepath_; std::string filepath_;
std::mutex mutex_; std::mutex mutex_;
size_t total_size_; size_t total_size_;
@ -62,4 +60,11 @@ class Playlist {
auto advanceBy(ssize_t amt) -> bool; auto advanceBy(ssize_t amt) -> bool;
}; };
class MutablePlaylist : public Playlist {
public:
MutablePlaylist(const std::string& playlistFilepath);
auto clear() -> bool;
auto append(Item i) -> void;
};
} // namespace audio } // namespace audio

@ -84,18 +84,25 @@ auto notifyChanged(bool current_changed, Reason reason) -> void {
events::Audio().Dispatch(ev); events::Audio().Dispatch(ev);
} }
TrackQueue::TrackQueue(tasks::WorkerPool& bg_worker, database::Handle db) TrackQueue::TrackQueue(tasks::WorkerPool& bg_worker, database::Handle db)
: mutex_(), : mutex_(),
bg_worker_(bg_worker), bg_worker_(bg_worker),
db_(db), db_(db),
playlist_("queue.playlist"), // TODO playlist_(".queue.playlist"),
position_(0),
shuffle_(), shuffle_(),
repeat_(false), repeat_(false),
replay_(false) {} replay_(false) {}
auto TrackQueue::current() const -> TrackItem { auto TrackQueue::current() const -> TrackItem {
const std::shared_lock<std::shared_mutex> lock(mutex_); const std::shared_lock<std::shared_mutex> lock(mutex_);
std::string val = playlist_.value(); std::string val;
if (opened_playlist_ && position_ < opened_playlist_->size()) {
val = opened_playlist_->value();
} else {
val = playlist_.value();
}
if (val.empty()) { if (val.empty()) {
return {}; return {};
} }
@ -104,12 +111,21 @@ auto TrackQueue::current() const -> TrackItem {
auto TrackQueue::currentPosition() const -> size_t { auto TrackQueue::currentPosition() const -> size_t {
const std::shared_lock<std::shared_mutex> lock(mutex_); const std::shared_lock<std::shared_mutex> lock(mutex_);
return playlist_.currentPosition(); return position_;
} }
auto TrackQueue::totalSize() const -> size_t { auto TrackQueue::totalSize() const -> size_t {
const std::shared_lock<std::shared_mutex> lock(mutex_); size_t sum = playlist_.size();
return playlist_.size(); if (opened_playlist_) {
sum += opened_playlist_->size();
}
return sum;
}
auto TrackQueue::updateShuffler() -> void {
if (shuffle_) {
shuffle_->resize(totalSize());
}
} }
auto TrackQueue::open() -> bool { auto TrackQueue::open() -> bool {
@ -118,6 +134,17 @@ auto TrackQueue::open() -> bool {
return playlist_.open(); return playlist_.open();
} }
auto TrackQueue::openPlaylist(const std::string& playlist_file) -> bool {
opened_playlist_.emplace(playlist_file);
auto res = opened_playlist_->open();
if (!res) {
return false;
}
updateShuffler();
notifyChanged(true, Reason::kExplicitUpdate);
return true;
}
auto TrackQueue::getFilepath(database::TrackId id) -> std::optional<std::string> { auto TrackQueue::getFilepath(database::TrackId id) -> std::optional<std::string> {
auto db = db_.lock(); auto db = db_.lock();
if (!db) { if (!db) {
@ -142,20 +169,15 @@ auto TrackQueue::append(Item i) -> void {
current_changed = was_queue_empty; // Dont support inserts yet current_changed = was_queue_empty; // Dont support inserts yet
} }
auto update_shuffler = [=, this]() { // If there wasn't anything already playing, then we should make sure we
if (shuffle_) { // begin playback at a random point, instead of always starting with
shuffle_->resize(playlist_.size()); // whatever was inserted first and *then* shuffling.
// If there wasn't anything already playing, then we should make sure we // We don't base this purely off of current_changed because we would like
// begin playback at a random point, instead of always starting with // 'play this track now' (by inserting at the current pos) to work even
// whatever was inserted first and *then* shuffling. // when shuffling is enabled.
// We don't base this purely off of current_changed because we would like if (was_queue_empty && shuffle_) {
// 'play this track now' (by inserting at the current pos) to work even playlist_.skipTo(shuffle_->current());
// when shuffling is enabled. }
if (was_queue_empty) {
playlist_.skipTo(shuffle_->current());
}
}
};
if (std::holds_alternative<database::TrackId>(i)) { if (std::holds_alternative<database::TrackId>(i)) {
{ {
@ -164,7 +186,7 @@ auto TrackQueue::append(Item i) -> void {
if (!filename.empty()) { if (!filename.empty()) {
playlist_.append(filename); playlist_.append(filename);
} }
update_shuffler(); updateShuffler();
} }
notifyChanged(current_changed, Reason::kExplicitUpdate); notifyChanged(current_changed, Reason::kExplicitUpdate);
} else if (std::holds_alternative<database::TrackIterator>(i)) { } else if (std::holds_alternative<database::TrackIterator>(i)) {
@ -191,7 +213,7 @@ auto TrackQueue::append(Item i) -> void {
} }
{ {
const std::unique_lock<std::shared_mutex> lock(mutex_); const std::unique_lock<std::shared_mutex> lock(mutex_);
update_shuffler(); updateShuffler();
} }
notifyChanged(current_changed, Reason::kExplicitUpdate); notifyChanged(current_changed, Reason::kExplicitUpdate);
}); });
@ -202,6 +224,20 @@ auto TrackQueue::next() -> void {
next(Reason::kExplicitUpdate); next(Reason::kExplicitUpdate);
} }
auto TrackQueue::goTo(size_t position) {
position_ = position;
if (opened_playlist_) {
if (position_ < opened_playlist_->size()) {
opened_playlist_->skipTo(position_);
} else {
playlist_.skipTo(position_ - opened_playlist_->size());
}
} else {
playlist_.skipTo(position_);
}
}
auto TrackQueue::next(Reason r) -> void { auto TrackQueue::next(Reason r) -> void {
bool changed = true; bool changed = true;
@ -209,18 +245,13 @@ auto TrackQueue::next(Reason r) -> void {
const std::unique_lock<std::shared_mutex> lock(mutex_); const std::unique_lock<std::shared_mutex> lock(mutex_);
if (shuffle_) { if (shuffle_) {
shuffle_->next(); shuffle_->next();
playlist_.skipTo(shuffle_->current()); position_ = shuffle_->current();
} else { } else {
if (playlist_.atEnd()) { if (position_ + 1 < totalSize()) {
if (replay_) { position_++;
playlist_.skipTo(0);
} else {
changed = false;
}
} else {
playlist_.next();
} }
} }
goTo(position_);
} }
notifyChanged(changed, r); notifyChanged(changed, r);
@ -233,18 +264,13 @@ auto TrackQueue::previous() -> void {
const std::unique_lock<std::shared_mutex> lock(mutex_); const std::unique_lock<std::shared_mutex> lock(mutex_);
if (shuffle_) { if (shuffle_) {
shuffle_->prev(); shuffle_->prev();
playlist_.skipTo(shuffle_->current()); position_ = shuffle_->current();
} else { } else {
if (playlist_.currentPosition() == 0) { if (position_ > 0) {
if (repeat_) { position_--;
playlist_.skipTo(playlist_.size()-1);
} else {
changed = false;
}
} else {
playlist_.prev();
} }
} }
goTo(position_);
} }
notifyChanged(changed, Reason::kExplicitUpdate); notifyChanged(changed, Reason::kExplicitUpdate);
@ -262,6 +288,7 @@ auto TrackQueue::clear() -> void {
{ {
const std::unique_lock<std::shared_mutex> lock(mutex_); const std::unique_lock<std::shared_mutex> lock(mutex_);
playlist_.clear(); playlist_.clear();
opened_playlist_.reset();
if (shuffle_) { if (shuffle_) {
shuffle_->resize(0); shuffle_->resize(0);
} }
@ -274,7 +301,7 @@ auto TrackQueue::random(bool en) -> void {
{ {
const std::unique_lock<std::shared_mutex> lock(mutex_); const std::unique_lock<std::shared_mutex> lock(mutex_);
if (en) { if (en) {
shuffle_.emplace(playlist_.size()); shuffle_.emplace(totalSize());
shuffle_->replay(replay_); shuffle_->replay(replay_);
} else { } else {
shuffle_.reset(); shuffle_.reset();
@ -326,7 +353,8 @@ auto TrackQueue::serialise() -> std::string {
encoded.add(cppbor::Uint{0}, cppbor::Array{ encoded.add(cppbor::Uint{0}, cppbor::Array{
cppbor::Bool{repeat_}, cppbor::Bool{repeat_},
cppbor::Bool{replay_}, cppbor::Bool{replay_},
cppbor::Uint{playlist_.currentPosition()}, cppbor::Uint{position_},
cppbor::Tstr{opened_playlist_->filepath()}
}); });
if (shuffle_) { if (shuffle_) {
encoded.add(cppbor::Uint{1}, cppbor::Array{ encoded.add(cppbor::Uint{1}, cppbor::Array{
@ -368,7 +396,10 @@ cppbor::ParseClient* TrackQueue::QueueParseClient::item(
i_ = 0; i_ = 0;
} else if (item->type() == cppbor::UINT) { } else if (item->type() == cppbor::UINT) {
auto val = item->asUint()->unsignedValue(); auto val = item->asUint()->unsignedValue();
queue_.playlist_.skipTo(val); queue_.goTo(val);
} else if (item->type() == cppbor::TSTR) {
auto val = item->asTstr();
queue_.openPlaylist(val->value());
} else if (item->type() == cppbor::SIMPLE) { } else if (item->type() == cppbor::SIMPLE) {
bool val = item->asBool()->value(); bool val = item->asBool()->value();
if (i_ == 0) { if (i_ == 0) {

@ -74,11 +74,14 @@ class TrackQueue {
auto currentPosition() const -> size_t; auto currentPosition() const -> size_t;
auto totalSize() const -> size_t; auto totalSize() const -> size_t;
auto open() -> bool; auto open() -> bool;
auto openPlaylist(const std::string& playlist_file) -> bool;
using Item = std::variant<database::TrackId, database::TrackIterator>; using Item = std::variant<database::TrackId, database::TrackIterator>;
auto insert(Item, size_t index = 0) -> void; auto insert(Item, size_t index = 0) -> void;
auto append(Item i) -> void; auto append(Item i) -> void;
auto updateShuffler() -> void;
/* /*
* Advances to the next track in the queue, placing the current track at the * Advances to the next track in the queue, placing the current track at the
* front of the 'played' queue. * front of the 'played' queue.
@ -114,6 +117,7 @@ class TrackQueue {
private: private:
auto next(QueueUpdate::Reason r) -> void; auto next(QueueUpdate::Reason r) -> void;
auto goTo(size_t position);
auto getFilepath(database::TrackId id) -> std::optional<std::string>; auto getFilepath(database::TrackId id) -> std::optional<std::string>;
mutable std::shared_mutex mutex_; mutable std::shared_mutex mutex_;
@ -121,7 +125,10 @@ class TrackQueue {
tasks::WorkerPool& bg_worker_; tasks::WorkerPool& bg_worker_;
database::Handle db_; database::Handle db_;
Playlist playlist_; MutablePlaylist playlist_;
std::optional<Playlist> opened_playlist_;
size_t position_;
std::optional<RandomIterator> shuffle_; std::optional<RandomIterator> shuffle_;
bool repeat_; bool repeat_;

@ -15,8 +15,8 @@ namespace lua {
[[maybe_unused]] static const char* kTag = "FileIterator"; [[maybe_unused]] static const char* kTag = "FileIterator";
FileIterator::FileIterator(std::string filepath) FileIterator::FileIterator(std::string filepath, bool showHidden)
: original_path_(filepath), current_(), offset_(-1) { : original_path_(filepath), show_hidden_(showHidden), current_(), offset_(-1) {
const TCHAR* path = static_cast<const TCHAR*>(filepath.c_str()); const TCHAR* path = static_cast<const TCHAR*>(filepath.c_str());
FRESULT res = f_opendir(&dir_, path); FRESULT res = f_opendir(&dir_, path);
if (res != FR_OK) { if (res != FR_OK) {
@ -33,7 +33,16 @@ auto FileIterator::value() const -> const std::optional<FileEntry>& {
} }
auto FileIterator::next() -> void { auto FileIterator::next() -> void {
iterate(false); size_t prev_index = -1;
if (current_) {
prev_index = current_->index;
}
do {
bool res = iterate(show_hidden_);
if (!res) {
break;
}
} while (!current_ || current_->index == prev_index);
} }
auto FileIterator::prev() -> void { auto FileIterator::prev() -> void {
@ -45,11 +54,11 @@ auto FileIterator::prev() -> void {
auto new_offset = offset_ - 1; auto new_offset = offset_ - 1;
offset_ = -1; offset_ = -1;
for (int i = 0; i <= new_offset; i++) { for (int i = 0; i <= new_offset; i++) {
iterate(false); iterate(show_hidden_);
} }
} }
auto FileIterator::iterate(bool reverse) -> bool { auto FileIterator::iterate(bool show_hidden) -> bool {
FILINFO info; FILINFO info;
auto res = f_readdir(&dir_, &info); auto res = f_readdir(&dir_, &info);
if (res != FR_OK) { if (res != FR_OK) {
@ -60,18 +69,22 @@ auto FileIterator::iterate(bool reverse) -> bool {
// End of directory // End of directory
// Set value to nil // Set value to nil
current_.reset(); current_.reset();
return false;
} else { } else {
// Update current value // Update current value
offset_++; offset_++;
current_ = FileEntry{ bool hidden = (info.fattrib & AM_HID) > 0 || info.fname[0] == '.';
.index = offset_, if (!hidden || show_hidden) {
.isHidden = (info.fattrib & AM_HID) > 0, current_ = FileEntry{
.isDirectory = (info.fattrib & AM_DIR) > 0, .index = offset_,
.isTrack = false, // TODO .isHidden = hidden,
.filepath = original_path_ + (original_path_.size() > 0 ? "/" : "") + .isDirectory = (info.fattrib & AM_DIR) > 0,
info.fname, .isTrack = false, // TODO
.filepath = original_path_ + (original_path_.size() > 0 ? "/" : "") +
}; info.fname,
.name = info.fname,
};
}
} }
return true; return true;
} }

@ -21,11 +21,12 @@ struct FileEntry {
bool isDirectory; bool isDirectory;
bool isTrack; bool isTrack;
std::string filepath; std::string filepath;
std::string name;
}; };
class FileIterator { class FileIterator {
public: public:
FileIterator(std::string filepath); FileIterator(std::string filepath, bool showHidden);
~FileIterator(); ~FileIterator();
auto value() const -> const std::optional<FileEntry>&; auto value() const -> const std::optional<FileEntry>&;
@ -35,6 +36,7 @@ class FileIterator {
private: private:
FF_DIR dir_; FF_DIR dir_;
std::string original_path_; std::string original_path_;
bool show_hidden_;
std::optional<FileEntry> current_; std::optional<FileEntry> current_;
int offset_; int offset_;

@ -21,29 +21,21 @@ struct LuaFileEntry {
bool isHidden; bool isHidden;
bool isDirectory; bool isDirectory;
bool isTrack; bool isTrack;
size_t path_size; std::string path;
char path[]; std::string name;
}; };
static_assert(std::is_trivially_destructible<LuaFileEntry>());
static_assert(std::is_trivially_copy_assignable<LuaFileEntry>());
static auto push_lua_file_entry(lua_State* L, const lua::FileEntry& r) -> void { static auto push_lua_file_entry(lua_State* L, const lua::FileEntry& r) -> void {
// Create and init the userdata. lua::FileEntry** entry = reinterpret_cast<lua::FileEntry**>(
LuaFileEntry* file_entry = reinterpret_cast<LuaFileEntry*>( lua_newuserdata(L, sizeof(uintptr_t)));
lua_newuserdata(L, sizeof(LuaFileEntry) + r.filepath.size())); *entry = new lua::FileEntry(r);
luaL_setmetatable(L, kFileEntryMetatable); luaL_setmetatable(L, kFileEntryMetatable);
}
// Init all the fields auto check_file_entry(lua_State* L, int stack_pos) -> lua::FileEntry* {
*file_entry = { lua::FileEntry* entry = *reinterpret_cast<lua::FileEntry**>(
.isHidden = r.isHidden, luaL_checkudata(L, stack_pos, kFileEntryMetatable));
.isDirectory = r.isDirectory, return entry;
.isTrack = r.isTrack,
.path_size = r.filepath.size(),
};
// Copy the string data across.
std::memcpy(file_entry->path, r.filepath.data(), r.filepath.size());
} }
auto check_file_iterator(lua_State* L, int stack_pos) -> lua::FileIterator* { auto check_file_iterator(lua_State* L, int stack_pos) -> lua::FileIterator* {
@ -56,7 +48,7 @@ static auto push_iterator(lua_State* state, const lua::FileIterator& it)
-> void { -> void {
lua::FileIterator** data = reinterpret_cast<lua::FileIterator**>( lua::FileIterator** data = reinterpret_cast<lua::FileIterator**>(
lua_newuserdata(state, sizeof(uintptr_t))); lua_newuserdata(state, sizeof(uintptr_t)));
*data = new lua::FileIterator(it); // TODO... *data = new lua::FileIterator(it);
luaL_setmetatable(state, kFileIteratorMetatable); luaL_setmetatable(state, kFileIteratorMetatable);
} }
@ -108,45 +100,55 @@ static const struct luaL_Reg kFileIteratorFuncs[] = {{"next", fs_iterate},
{NULL, NULL}}; {NULL, NULL}};
static auto file_entry_path(lua_State* state) -> int { static auto file_entry_path(lua_State* state) -> int {
LuaFileEntry* data = reinterpret_cast<LuaFileEntry*>( lua::FileEntry* entry = check_file_entry(state, 1);
luaL_checkudata(state, 1, kFileEntryMetatable)); lua_pushlstring(state, entry->filepath.c_str(), entry->filepath.size());
lua_pushlstring(state, data->path, data->path_size);
return 1; return 1;
} }
static auto file_entry_is_dir(lua_State* state) -> int { static auto file_entry_is_dir(lua_State* state) -> int {
LuaFileEntry* data = reinterpret_cast<LuaFileEntry*>( lua::FileEntry* entry = check_file_entry(state, 1);
luaL_checkudata(state, 1, kFileEntryMetatable)); lua_pushboolean(state, entry->isDirectory);
lua_pushboolean(state, data->isDirectory);
return 1; return 1;
} }
static auto file_entry_is_hidden(lua_State* state) -> int { static auto file_entry_is_hidden(lua_State* state) -> int {
LuaFileEntry* data = reinterpret_cast<LuaFileEntry*>( lua::FileEntry* entry = check_file_entry(state, 1);
luaL_checkudata(state, 1, kFileEntryMetatable)); lua_pushboolean(state, entry->isHidden);
lua_pushboolean(state, data->isHidden);
return 1; return 1;
} }
static auto file_entry_is_track(lua_State* state) -> int { static auto file_entry_is_track(lua_State* state) -> int {
LuaFileEntry* data = reinterpret_cast<LuaFileEntry*>( lua::FileEntry* entry = check_file_entry(state, 1);
luaL_checkudata(state, 1, kFileEntryMetatable)); lua_pushboolean(state, entry->isTrack);
lua_pushboolean(state, data->isTrack); return 1;
}
static auto file_entry_name(lua_State* state) -> int {
lua::FileEntry* entry = check_file_entry(state, 1);
lua_pushlstring(state, entry->name.c_str(), entry->name.size());
return 1;
}
static auto file_entry_gc(lua_State* state) -> int {
lua::FileEntry* entry = check_file_entry(state, 1);
delete entry;
return 1; return 1;
} }
static const struct luaL_Reg kFileEntryFuncs[] = {{"filepath", file_entry_path}, static const struct luaL_Reg kFileEntryFuncs[] = {{"filepath", file_entry_path},
{"name", file_entry_name},
{"is_directory", file_entry_is_dir}, {"is_directory", file_entry_is_dir},
{"is_hidden", file_entry_is_hidden}, {"is_hidden", file_entry_is_hidden},
{"is_track", file_entry_is_track}, {"is_track", file_entry_is_track},
{"__tostring", file_entry_path}, {"__tostring", file_entry_name},
{"__gc", file_entry_gc},
{NULL, NULL}}; {NULL, NULL}};
static auto fs_new_iterator(lua_State* state) -> int { static auto fs_new_iterator(lua_State* state) -> int {
// Takes a filepath as a string and returns a new FileIterator // Takes a filepath as a string and returns a new FileIterator
// on that directory // on that directory
std::string filepath = luaL_checkstring(state, -1); std::string filepath = luaL_checkstring(state, -1);
lua::FileIterator iter(filepath); lua::FileIterator iter(filepath, false);
push_iterator(state, iter); push_iterator(state, iter);
return 1; return 1;
} }

@ -57,8 +57,22 @@ static auto queue_clear(lua_State* state) -> int {
return 0; return 0;
} }
static auto queue_open_playlist(lua_State* state) -> int {
Bridge* instance = Bridge::Get(state);
audio::TrackQueue& queue = instance->services().track_queue();
size_t len = 0;
const char* str = luaL_checklstring(state, 1, &len);
if (!str) {
return 0;
}
queue.clear();
queue.openPlaylist(str);
return 0;
}
static const struct luaL_Reg kQueueFuncs[] = {{"add", queue_add}, static const struct luaL_Reg kQueueFuncs[] = {{"add", queue_add},
{"clear", queue_clear}, {"clear", queue_clear},
{"open_playlist", queue_open_playlist},
{NULL, NULL}}; {NULL, NULL}};
static auto lua_queue(lua_State* state) -> int { static auto lua_queue(lua_State* state) -> int {

Loading…
Cancel
Save