refactor: extract shared mixins and helpers, drop dead abstractions

Introduces shared seams for paginated views, D-pad reorder, media control
routing, async singletons and the device method channel, then points the
open-coded copies at them.

Also removes unused models and duplicated provider/server plumbing, folds
the twice-implemented artifact store in the server, and factors the
repeated Flutter toolchain prologue in CI into a composite action.
This commit is contained in:
edde746
2026-07-26 06:09:48 +02:00
parent 61344f7862
commit 352b88109b
217 changed files with 6813 additions and 8773 deletions
+43 -168
View File
@@ -617,20 +617,7 @@ void MpvPlayer::CommandAsync(const std::vector<std::string>& args, CommandCallba
return;
}
std::vector<const char*> c_args;
c_args.reserve(args.size() + 1);
for (const auto& arg : args) {
c_args.push_back(arg.c_str());
}
c_args.push_back(nullptr);
uint64_t request_id = callback ? pending_requests_.RegisterStatus(std::move(callback)) : 0;
int result = mpv_command_async(mpv_, request_id, c_args.data());
if (result < 0) {
auto cb = pending_requests_.TakeStatus(request_id);
if (cb) cb(result);
}
plezy::mpv_common::SubmitCommandAsync(mpv_, pending_requests_, args, std::move(callback));
}
void MpvPlayer::SetProperty(const std::string& name, const std::string& value) {
@@ -658,14 +645,7 @@ void MpvPlayer::SetPropertyAsync(const std::string& name, const std::string& val
if (completion) completion(error);
};
}
uint64_t request_id = callback ? pending_requests_.RegisterStatus(std::move(callback)) : 0;
char* property_value = const_cast<char*>(value.c_str());
int result = mpv_set_property_async(mpv_, request_id, name.c_str(), MPV_FORMAT_STRING, &property_value);
if (result < 0) {
auto cb = pending_requests_.TakeStatus(request_id);
if (cb) cb(result);
}
plezy::mpv_common::SubmitSetPropertyAsync(mpv_, pending_requests_, name, value, std::move(callback));
}
void MpvPlayer::GetPropertyAsync(const std::string& name, GetPropertyCallback callback) {
@@ -674,13 +654,7 @@ void MpvPlayer::GetPropertyAsync(const std::string& name, GetPropertyCallback ca
return;
}
uint64_t request_id = pending_requests_.RegisterProperty(std::move(callback));
int result = mpv_get_property_async(mpv_, request_id, name.c_str(), MPV_FORMAT_STRING);
if (result < 0) {
auto cb = pending_requests_.TakeProperty(request_id);
if (cb) cb(result, "");
}
plezy::mpv_common::SubmitGetPropertyAsync(mpv_, pending_requests_, name, std::move(callback));
}
void MpvPlayer::ObserveProperty(const std::string& name, const std::string& format, int id) {
@@ -920,33 +894,12 @@ void MpvPlayer::EnsureAudioRecoveryTimer() {
}
void MpvPlayer::HandleMpvEvent(mpv_event* event) {
if (plezy::mpv_common::DispatchReplyEvent(
pending_requests_, event, [](const char* value) { return SanitizeUtf8(value); })) {
return;
}
switch (event->event_id) {
case MPV_EVENT_COMMAND_REPLY:
case MPV_EVENT_SET_PROPERTY_REPLY: {
uint64_t request_id = event->reply_userdata;
StatusCallback callback = pending_requests_.TakeStatus(request_id);
if (callback) {
callback(event->error);
}
break;
}
case MPV_EVENT_GET_PROPERTY_REPLY: {
uint64_t request_id = event->reply_userdata;
GetPropertyCallback callback = pending_requests_.TakeProperty(request_id);
if (callback) {
int error = event->error;
std::string value;
if (error >= 0) {
auto* prop = static_cast<mpv_event_property*>(event->data);
if (prop && prop->format == MPV_FORMAT_STRING && prop->data) {
auto c_value = *static_cast<char**>(prop->data);
if (c_value) value = SanitizeUtf8(c_value);
}
}
callback(error, value);
}
break;
}
case MPV_EVENT_LOG_MESSAGE: {
auto* msg = static_cast<mpv_event_log_message*>(event->data);
if (!msg) break;
@@ -963,54 +916,12 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) {
case MPV_EVENT_PROPERTY_CHANGE: {
auto* prop = static_cast<mpv_event_property*>(event->data);
if (!prop || !prop->name) break;
mpv_node node;
node.format = prop->format;
mpv_node node = plezy::mpv_common::ExtractPropertyNode(prop);
switch (prop->format) {
case MPV_FORMAT_STRING:
node.u.string = prop->data ? *static_cast<char**>(prop->data) : nullptr;
break;
case MPV_FORMAT_FLAG:
node.u.flag = prop->data ? *static_cast<int*>(prop->data) : 0;
break;
case MPV_FORMAT_INT64:
node.u.int64 = prop->data ? *static_cast<int64_t*>(prop->data) : 0;
break;
case MPV_FORMAT_DOUBLE:
node.u.double_ = prop->data ? *static_cast<double*>(prop->data) : 0.0;
break;
case MPV_FORMAT_NODE:
if (prop->data) {
node = *static_cast<mpv_node*>(prop->data);
} else {
node.format = MPV_FORMAT_NONE;
}
break;
default:
node.format = MPV_FORMAT_NONE;
break;
}
if (strcmp(prop->name, "current-ao") == 0) {
const char* current_ao = nullptr;
if (prop->format == MPV_FORMAT_STRING && prop->data) {
current_ao = *static_cast<char**>(prop->data);
}
const bool is_null = current_ao && strcmp(current_ao, "null") == 0;
const auto transition =
audio_recovery_.SetCurrentAudioOutputNull(is_null, plezy::mpv_common::AudioRecoveryState::Clock::now());
if (transition == plezy::mpv_common::AudioOutputTransition::kFellBackToNull) {
LogRecovery("current-ao fell back to null; starting recovery");
EnsureAudioRecoveryTimer();
} else if (transition == plezy::mpv_common::AudioOutputTransition::kRecovered) {
LogRecovery("audio recovered (current-ao no longer null)");
}
}
if (strcmp(prop->name, "audio-device-list") == 0 && event->reply_userdata == 0 &&
audio_recovery_.OnAudioDeviceListChanged(plezy::mpv_common::AudioRecoveryState::Clock::now())) {
LogRecovery("audio-device-list changed while ao=null; rescheduling ao-reload");
EnsureAudioRecoveryTimer();
}
const auto notice = plezy::mpv_common::ObserveAudioRecoveryProperty(audio_recovery_, event, prop);
if (notice.message) LogRecovery(notice.message);
// Recovery runs off a GLib timer here, so newly queued work has to arm it.
if (notice.scheduled_work) EnsureAudioRecoveryTimer();
SendPropertyChange(prop->name, &node);
break;
@@ -1048,77 +959,41 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) {
break;
}
}
FlValue* MpvPlayer::NodeToFlValue(mpv_node* node) {
NodeConversionBudget budget{
/*remaining_entries=*/16384,
/*remaining_bytes=*/16 * 1024 * 1024,
};
return NodeToFlValue(node, 0, &budget);
}
bool MpvPlayer::ConvertNodeString(const char* input, NodeConversionBudget* budget, std::string* result) {
if (!input || !budget || !result) return false;
const size_t length = strnlen(input, budget->remaining_bytes + 1);
if (length > budget->remaining_bytes) return false;
budget->remaining_bytes -= length;
*result = SanitizeUtf8(input, length);
return true;
}
namespace {
FlValue* MpvPlayer::NodeToFlValue(mpv_node* node, size_t depth, NodeConversionBudget* budget) {
constexpr size_t kMaxNodeDepth = 32;
constexpr int kMaxNodeEntries = 16384;
if (!node || !budget || depth >= kMaxNodeDepth || budget->remaining_entries == 0) {
return fl_value_new_null();
// Adapts the shared, bounded mpv_node walk onto GLib-owned FlValues.
struct FlValueNodeBuilder {
using Value = FlValue*;
using ListBuilder = FlValue*;
using MapBuilder = FlValue*;
static Value Null() { return fl_value_new_null(); }
static Value Bool(bool value) { return fl_value_new_bool(value); }
static Value Int(int64_t value) { return fl_value_new_int(value); }
static Value Double(double value) { return fl_value_new_float(value); }
static Value String(const char* value, size_t length) {
return fl_value_new_string(SanitizeUtf8(value, length).c_str());
}
--budget->remaining_entries;
switch (node->format) {
case MPV_FORMAT_STRING: {
std::string value;
if (!ConvertNodeString(node->u.string, budget, &value)) return fl_value_new_null();
return fl_value_new_string(value.c_str());
}
case MPV_FORMAT_FLAG:
return fl_value_new_bool(node->u.flag != 0);
case MPV_FORMAT_INT64:
return fl_value_new_int(node->u.int64);
case MPV_FORMAT_DOUBLE:
return fl_value_new_float(node->u.double_);
case MPV_FORMAT_NODE_ARRAY: {
const mpv_node_list* list = node->u.list;
if (!list || list->num < 0 || list->num > kMaxNodeEntries || (list->num > 0 && !list->values)) {
return fl_value_new_null();
}
FlValue* result = fl_value_new_list();
for (int i = 0; i < list->num; i++) {
fl_value_append_take(result, NodeToFlValue(&list->values[i], depth + 1, budget));
}
return result;
}
case MPV_FORMAT_NODE_MAP: {
const mpv_node_list* map = node->u.list;
if (!map || map->num < 0 || map->num > kMaxNodeEntries || (map->num > 0 && (!map->keys || !map->values))) {
return fl_value_new_null();
}
FlValue* result = fl_value_new_map();
for (int i = 0; i < map->num; i++) {
if (!map->keys[i]) {
fl_value_unref(result);
return fl_value_new_null();
}
std::string key;
if (!ConvertNodeString(map->keys[i], budget, &key)) {
fl_value_unref(result);
return fl_value_new_null();
}
fl_value_set_string_take(result, key.c_str(), NodeToFlValue(&map->values[i], depth + 1, budget));
}
return result;
}
default:
return fl_value_new_null();
static ListBuilder NewList() { return fl_value_new_list(); }
static void Append(ListBuilder& list, Value value) { fl_value_append_take(list, value); }
static Value FinishList(ListBuilder list) { return list; }
static MapBuilder NewMap() { return fl_value_new_map(); }
static void Insert(MapBuilder& map, const char* key, size_t key_length, Value value) {
fl_value_set_string_take(map, SanitizeUtf8(key, key_length).c_str(), value);
}
static Value FinishMap(MapBuilder map) { return map; }
static void AbandonMap(MapBuilder& map) { fl_value_unref(map); }
};
} // namespace
FlValue* MpvPlayer::NodeToFlValue(mpv_node* node) { return plezy::mpv_common::ConvertNode<FlValueNodeBuilder>(node); }
FlValue* MpvPlayer::NodeToFlValue(mpv_node* node, plezy::mpv_common::NodeConversionBudget* budget) {
return plezy::mpv_common::ConvertNode<FlValueNodeBuilder>(node, 0, budget);
}
void MpvPlayer::SendPropertyChange(const char* name, mpv_node* data) {
+2 -8
View File
@@ -242,15 +242,9 @@ class MpvPlayer {
void LogRecovery(const std::string& text);
void SetHDREnabled(bool enabled, StatusCallback callback = nullptr);
struct NodeConversionBudget {
size_t remaining_entries;
size_t remaining_bytes;
};
/// Helper to convert mpv_node to FlValue.
/// Helper to convert mpv_node to FlValue, bounded by the shared node budget.
::_FlValue* NodeToFlValue(mpv_node* node);
::_FlValue* NodeToFlValue(mpv_node* node, size_t depth, NodeConversionBudget* budget);
bool ConvertNodeString(const char* input, NodeConversionBudget* budget, std::string* result);
::_FlValue* NodeToFlValue(mpv_node* node, plezy::mpv_common::NodeConversionBudget* budget);
const bool audio_only_;
mpv_handle* mpv_ = nullptr;
@@ -102,8 +102,8 @@ class MpvPlayerLifecycleTestPeer {
static FlValue* ConvertNode(MpvPlayer& player, mpv_node* node) { return player.NodeToFlValue(node); }
static FlValue* ConvertNodeWithBudget(
MpvPlayer& player, mpv_node* node, size_t remaining_entries, size_t remaining_bytes) {
MpvPlayer::NodeConversionBudget budget{remaining_entries, remaining_bytes};
return player.NodeToFlValue(node, 0, &budget);
plezy::mpv_common::NodeConversionBudget budget{remaining_entries, remaining_bytes};
return player.NodeToFlValue(node, &budget);
}
static void RegisterObservedNode(MpvPlayer& player, const std::string& name, int id) {
player.observed_properties_.Register(name, "node", id);