mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2025-12-06 18:32:50 +08:00
capitalized first character in all wshub event key
This commit is contained in:
@@ -120,7 +120,7 @@ func (s *wsServer) handleWsInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
if data.EventID == "" {
|
if data.EventID == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
eventCacheData, _ := json.Marshal(data)
|
eventCacheData, _ := toCapitalizedJSON(data)
|
||||||
err := client.conn.WriteMessage(websocket.TextMessage, eventCacheData)
|
err := client.conn.WriteMessage(websocket.TextMessage, eventCacheData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warn("write message failed", err)
|
s.log.Warn("write message failed", err)
|
||||||
|
|||||||
70
plugin/wshub/utils.go
Normal file
70
plugin/wshub/utils.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package wshub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// capitalize is a helper function to safely capitalize the first letter of a string.
|
||||||
|
// It's robust against empty strings.
|
||||||
|
func capitalize(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
r := []rune(s)
|
||||||
|
r[0] = unicode.ToUpper(r[0])
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// capitalizeKeys recursively traverses an interface{} and capitalizes the keys of any maps it finds.
|
||||||
|
func capitalizeKeys(data interface{}) interface{} {
|
||||||
|
// Use a type switch to handle the different types of data we might encounter.
|
||||||
|
switch value := data.(type) {
|
||||||
|
// If it's a map, we iterate over its keys and values.
|
||||||
|
case map[string]interface{}:
|
||||||
|
newMap := make(map[string]interface{})
|
||||||
|
for k, v := range value {
|
||||||
|
// Capitalize the key and recursively process the value.
|
||||||
|
newMap[capitalize(k)] = capitalizeKeys(v)
|
||||||
|
}
|
||||||
|
return newMap
|
||||||
|
|
||||||
|
// If it's a slice, we iterate over its elements.
|
||||||
|
case []interface{}:
|
||||||
|
// The slice itself doesn't have keys, but its elements might.
|
||||||
|
newSlice := make([]interface{}, len(value))
|
||||||
|
for i, v := range value {
|
||||||
|
// Recursively process each element in the slice.
|
||||||
|
newSlice[i] = capitalizeKeys(v)
|
||||||
|
}
|
||||||
|
return newSlice
|
||||||
|
|
||||||
|
// For any other type (string, int, bool, etc.), return it as is.
|
||||||
|
default:
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// toCapitalizedJSON marshals any data structure (including structs) to a JSON string
|
||||||
|
// with all keys having their first letter capitalized.
|
||||||
|
func toCapitalizedJSON(payload interface{}) ([]byte, error) {
|
||||||
|
// Step 1: Marshal the data to JSON. This respects the `json` tags on any structs.
|
||||||
|
tempJSON, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to perform initial marshal: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Unmarshal the JSON into a generic interface{}.
|
||||||
|
// This converts all JSON objects into map[string]interface{}, regardless of the original type.
|
||||||
|
var genericData interface{}
|
||||||
|
if err := json.Unmarshal(tempJSON, &genericData); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal into generic interface: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Recursively capitalize the keys of the generic data structure.
|
||||||
|
capitalizedData := capitalizeKeys(genericData)
|
||||||
|
|
||||||
|
// Step 4: Marshal the final, capitalized data structure back to JSON.
|
||||||
|
return json.MarshalIndent(capitalizedData, "", " ")
|
||||||
|
}
|
||||||
164
plugin/wshub/utils_test.go
Normal file
164
plugin/wshub/utils_test.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package wshub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- Example struct that might be used in the Data field ---
|
||||||
|
type UserDetails struct {
|
||||||
|
UserIdentifier int `json:"userIdentifier"`
|
||||||
|
EmailAddress string `json:"emailAddress"`
|
||||||
|
IsActive bool `json:"isActive"`
|
||||||
|
Metadata map[string]interface{} `json:"metadata"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToCapitalizedJSON(t *testing.T) {
|
||||||
|
// Define a struct for our table-driven tests
|
||||||
|
testCases := []struct {
|
||||||
|
name string // Name of the test case
|
||||||
|
input interface{} // Input to the function
|
||||||
|
expectedJSON string // The expected JSON output string
|
||||||
|
expectError bool // Whether we expect an error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Simple Struct",
|
||||||
|
input: UserDetails{
|
||||||
|
UserIdentifier: 101,
|
||||||
|
EmailAddress: "test@example.com",
|
||||||
|
IsActive: true,
|
||||||
|
},
|
||||||
|
expectedJSON: `{
|
||||||
|
"UserIdentifier": 101,
|
||||||
|
"EmailAddress": "test@example.com",
|
||||||
|
"IsActive": true,
|
||||||
|
"Metadata": null,
|
||||||
|
"Tags": null
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Struct with Nested Map",
|
||||||
|
input: UserDetails{
|
||||||
|
UserIdentifier: 102,
|
||||||
|
EmailAddress: "another@example.com",
|
||||||
|
IsActive: false,
|
||||||
|
Metadata: map[string]interface{}{
|
||||||
|
"lastLogin": "2024-01-01T12:00:00Z",
|
||||||
|
"loginCount": 5,
|
||||||
|
},
|
||||||
|
Tags: []string{"beta", "tester"},
|
||||||
|
},
|
||||||
|
expectedJSON: `{
|
||||||
|
"UserIdentifier": 102,
|
||||||
|
"EmailAddress": "another@example.com",
|
||||||
|
"IsActive": false,
|
||||||
|
"Metadata": {
|
||||||
|
"LastLogin": "2024-01-01T12:00:00Z",
|
||||||
|
"LoginCount": 5
|
||||||
|
},
|
||||||
|
"Tags": ["beta", "tester"]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Simple Map",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"firstName": "John",
|
||||||
|
"lastName": "Doe",
|
||||||
|
},
|
||||||
|
expectedJSON: `{
|
||||||
|
"FirstName": "John",
|
||||||
|
"LastName": "Doe"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Nested Map and Slice",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"event": "user.created",
|
||||||
|
"payload": map[string]interface{}{
|
||||||
|
"userName": "jdoe",
|
||||||
|
"roles": []interface{}{
|
||||||
|
"editor",
|
||||||
|
map[string]interface{}{"permissionLevel": 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedJSON: `{
|
||||||
|
"Event": "user.created",
|
||||||
|
"Payload": {
|
||||||
|
"UserName": "jdoe",
|
||||||
|
"Roles": [
|
||||||
|
"editor",
|
||||||
|
{
|
||||||
|
"PermissionLevel": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Top-level Slice with Structs",
|
||||||
|
input: []UserDetails{
|
||||||
|
{UserIdentifier: 201, EmailAddress: "user1@test.com"},
|
||||||
|
{UserIdentifier: 202, EmailAddress: "user2@test.com"},
|
||||||
|
},
|
||||||
|
expectedJSON: `[
|
||||||
|
{
|
||||||
|
"UserIdentifier": 201, "EmailAddress": "user1@test.com", "IsActive": false, "Metadata": null, "Tags": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UserIdentifier": 202, "EmailAddress": "user2@test.com", "IsActive": false, "Metadata": null, "Tags": null
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Nil Input",
|
||||||
|
input: nil,
|
||||||
|
expectedJSON: `null`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty map",
|
||||||
|
input: map[string]interface{}{},
|
||||||
|
expectedJSON: `{}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Test Runner ---
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// Call the function we are testing
|
||||||
|
actualBytes, err := toCapitalizedJSON(tc.input)
|
||||||
|
|
||||||
|
// Check for an unexpected error
|
||||||
|
if !tc.expectError && err != nil {
|
||||||
|
t.Fatalf("ToCapitalizedJSON() returned an unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
// Check for an expected error that did not occur
|
||||||
|
if tc.expectError && err == nil {
|
||||||
|
t.Fatalf("ToCapitalizedJSON() was expected to return an error, but it did not")
|
||||||
|
}
|
||||||
|
|
||||||
|
// To reliably compare JSON, we unmarshal both the actual and expected
|
||||||
|
// results into a generic interface{} and use reflect.DeepEqual.
|
||||||
|
// This avoids issues with whitespace and key ordering.
|
||||||
|
var actualResult interface{}
|
||||||
|
if err := json.Unmarshal(actualBytes, &actualResult); err != nil {
|
||||||
|
t.Fatalf("Failed to unmarshal actual result: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedResult interface{}
|
||||||
|
if err := json.Unmarshal([]byte(tc.expectedJSON), &expectedResult); err != nil {
|
||||||
|
t.Fatalf("Failed to unmarshal expected JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the results
|
||||||
|
if !reflect.DeepEqual(actualResult, expectedResult) {
|
||||||
|
// Use MarshalIndent to get a pretty-printed version for easier comparison
|
||||||
|
prettyActual, _ := json.MarshalIndent(actualResult, "", " ")
|
||||||
|
prettyExpected, _ := json.MarshalIndent(expectedResult, "", " ")
|
||||||
|
t.Errorf("Result does not match expected.\nGot:\n%s\n\nWant:\n%s", string(prettyActual), string(prettyExpected))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"AynaLivePlayer/pkg/event"
|
"AynaLivePlayer/pkg/event"
|
||||||
"AynaLivePlayer/pkg/i18n"
|
"AynaLivePlayer/pkg/i18n"
|
||||||
"AynaLivePlayer/pkg/logger"
|
"AynaLivePlayer/pkg/logger"
|
||||||
"encoding/json"
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/data/binding"
|
"fyne.io/fyne/v2/data/binding"
|
||||||
@@ -183,7 +182,7 @@ func (w *WsHub) registerEvents() {
|
|||||||
EventID: e.Id,
|
EventID: e.Id,
|
||||||
Data: e.Data,
|
Data: e.Data,
|
||||||
}
|
}
|
||||||
val, err := json.Marshal(ed)
|
val, err := toCapitalizedJSON(ed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.log.Errorf("failed to marshal event data %v", err)
|
w.log.Errorf("failed to marshal event data %v", err)
|
||||||
return
|
return
|
||||||
|
|||||||
1
todo.txt
1
todo.txt
@@ -16,6 +16,7 @@
|
|||||||
----
|
----
|
||||||
|
|
||||||
Finished
|
Finished
|
||||||
|
- 2024.07.24 : 修复网易云,修复wshub大小写问题
|
||||||
- 2024.07.07 : QQ音乐
|
- 2024.07.07 : QQ音乐
|
||||||
- 2024.06.30 : 添加vlc核心,修复若干bug,gui框架更新,修复点歌限制为1时可能出现的无法点歌的问题
|
- 2024.06.30 : 添加vlc核心,修复若干bug,gui框架更新,修复点歌限制为1时可能出现的无法点歌的问题
|
||||||
- 2024.05.27 : 修复web弹幕获取到0个host的时候闪退的问题
|
- 2024.05.27 : 修复web弹幕获取到0个host的时候闪退的问题
|
||||||
|
|||||||
Reference in New Issue
Block a user