mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2025-12-15 14:38:17 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd46c167ff | ||
|
|
8c6ea48ad4 | ||
|
|
6fc2773b12 |
14
README.md
Normal file
14
README.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# AynaLivePlayer
|
||||||
|
|
||||||
|
Bilibili Audio Bot. Written by Golang.
|
||||||
|
|
||||||
|
Provider By Aynakeya
|
||||||
|
|
||||||
|
QQ group: 621035845
|
||||||
|
|
||||||
|
## build
|
||||||
|
|
||||||
|
```
|
||||||
|
go build -ldflags -H=windowsgui app/gui/main.go
|
||||||
|
|
||||||
|
```
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"AynaLivePlayer/config"
|
"AynaLivePlayer/config"
|
||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
"AynaLivePlayer/gui"
|
"AynaLivePlayer/gui"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"AynaLivePlayer/logger"
|
"AynaLivePlayer/logger"
|
||||||
"AynaLivePlayer/plugin/diange"
|
"AynaLivePlayer/plugin/diange"
|
||||||
"AynaLivePlayer/plugin/qiege"
|
"AynaLivePlayer/plugin/qiege"
|
||||||
@@ -32,6 +33,7 @@ func main() {
|
|||||||
defer func() {
|
defer func() {
|
||||||
controller.Destroy()
|
controller.Destroy()
|
||||||
config.SaveToConfigFile(config.CONFIG_PATH)
|
config.SaveToConfigFile(config.CONFIG_PATH)
|
||||||
|
i18n.SaveTranslation()
|
||||||
}()
|
}()
|
||||||
gui.Initialize()
|
gui.Initialize()
|
||||||
gui.MainWindow.ShowAndRun()
|
gui.MainWindow.ShowAndRun()
|
||||||
|
|||||||
220
assets/translation.json
Normal file
220
assets/translation.json
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
{
|
||||||
|
"Languages": [
|
||||||
|
"en",
|
||||||
|
"zh-CN"
|
||||||
|
],
|
||||||
|
"Messages": {
|
||||||
|
"gui.player.button.lrc": {
|
||||||
|
"en": "lrc",
|
||||||
|
"zh-CN": "歌词"
|
||||||
|
},
|
||||||
|
"gui.player.playlist.artist": {
|
||||||
|
"en": "Artist",
|
||||||
|
"zh-CN": "歌手"
|
||||||
|
},
|
||||||
|
"gui.player.playlist.op.delete": {
|
||||||
|
"en": "Delete",
|
||||||
|
"zh-CN": "删除"
|
||||||
|
},
|
||||||
|
"gui.player.playlist.op.top": {
|
||||||
|
"en": "Top",
|
||||||
|
"zh-CN": "置顶"
|
||||||
|
},
|
||||||
|
"gui.player.playlist.ops": {
|
||||||
|
"en": "OPs",
|
||||||
|
"zh-CN": "操作"
|
||||||
|
},
|
||||||
|
"gui.player.playlist.title": {
|
||||||
|
"en": "Title",
|
||||||
|
"zh-CN": "歌名"
|
||||||
|
},
|
||||||
|
"gui.player.playlist.user": {
|
||||||
|
"en": "User",
|
||||||
|
"zh-CN": "用户"
|
||||||
|
},
|
||||||
|
"gui.playlist.add.cancel": {
|
||||||
|
"en": "Cancel",
|
||||||
|
"zh-CN": "取消"
|
||||||
|
},
|
||||||
|
"gui.playlist.add.confirm": {
|
||||||
|
"en": "Confirm",
|
||||||
|
"zh-CN": "确认"
|
||||||
|
},
|
||||||
|
"gui.playlist.add.id_url": {
|
||||||
|
"en": "ID/URL",
|
||||||
|
"zh-CN": "ID/网址"
|
||||||
|
},
|
||||||
|
"gui.playlist.add.prompt": {
|
||||||
|
"en": "Please enter the ID or URL of the song you want to add.",
|
||||||
|
"zh-CN": "输入歌单ID或者歌单网址。"
|
||||||
|
},
|
||||||
|
"gui.playlist.add.title": {
|
||||||
|
"en": "Add Playlist",
|
||||||
|
"zh-CN": "添加歌单"
|
||||||
|
},
|
||||||
|
"gui.playlist.button.add": {
|
||||||
|
"en": "Add",
|
||||||
|
"zh-CN": "添加"
|
||||||
|
},
|
||||||
|
"gui.playlist.button.refresh": {
|
||||||
|
"en": "Refresh",
|
||||||
|
"zh-CN": "刷新"
|
||||||
|
},
|
||||||
|
"gui.playlist.button.remove": {
|
||||||
|
"en": "Remove",
|
||||||
|
"zh-CN": "移除"
|
||||||
|
},
|
||||||
|
"gui.playlist.button.set_as_system": {
|
||||||
|
"en": "SetAsSystem",
|
||||||
|
"zh-CN": "设为默认歌单"
|
||||||
|
},
|
||||||
|
"gui.playlist.current": {
|
||||||
|
"en": "Current: ",
|
||||||
|
"zh-CN": "当前为: "
|
||||||
|
},
|
||||||
|
"gui.playlist.current.none": {
|
||||||
|
"en": "Current: None",
|
||||||
|
"zh-CN": "当前为: 无"
|
||||||
|
},
|
||||||
|
"gui.room.btn.connect": {
|
||||||
|
"en": "Connect",
|
||||||
|
"zh-CN": "连接"
|
||||||
|
},
|
||||||
|
"gui.room.btn.disconnect": {
|
||||||
|
"en": "Disconnect",
|
||||||
|
"zh-CN": "断开"
|
||||||
|
},
|
||||||
|
"gui.room.id": {
|
||||||
|
"en": "Room ID: ",
|
||||||
|
"zh-CN": "房间号: "
|
||||||
|
},
|
||||||
|
"gui.room.status.connected": {
|
||||||
|
"en": "Connected",
|
||||||
|
"zh-CN": "已连接"
|
||||||
|
},
|
||||||
|
"gui.room.status.disconnected": {
|
||||||
|
"en": "Disconnected",
|
||||||
|
"zh-CN": "已断开"
|
||||||
|
},
|
||||||
|
"gui.room.status.failed": {
|
||||||
|
"en": "Set Failed",
|
||||||
|
"zh-CN": "设置失败"
|
||||||
|
},
|
||||||
|
"gui.room.waiting": {
|
||||||
|
"en": "Waiting",
|
||||||
|
"zh-CN": "等待连接"
|
||||||
|
},
|
||||||
|
"gui.search.artist": {
|
||||||
|
"en": "Artist",
|
||||||
|
"zh-CN": "歌手"
|
||||||
|
},
|
||||||
|
"gui.search.filter": {
|
||||||
|
"en": "Source Filter: ",
|
||||||
|
"zh-CN": "选择源: "
|
||||||
|
},
|
||||||
|
"gui.search.operation": {
|
||||||
|
"en": "Operation",
|
||||||
|
"zh-CN": "操作 "
|
||||||
|
},
|
||||||
|
"gui.search.placeholder": {
|
||||||
|
"en": "Keyword",
|
||||||
|
"zh-CN": "Keyword"
|
||||||
|
},
|
||||||
|
"gui.search.search": {
|
||||||
|
"en": "Search",
|
||||||
|
"zh-CN": "搜索"
|
||||||
|
},
|
||||||
|
"gui.search.source": {
|
||||||
|
"en": "Source",
|
||||||
|
"zh-CN": "歌源"
|
||||||
|
},
|
||||||
|
"gui.search.title": {
|
||||||
|
"en": "Title",
|
||||||
|
"zh-CN": "歌名"
|
||||||
|
},
|
||||||
|
"gui.tab.config": {
|
||||||
|
"en": "Config",
|
||||||
|
"zh-CN": "设置"
|
||||||
|
},
|
||||||
|
"gui.tab.player": {
|
||||||
|
"en": "Player",
|
||||||
|
"zh-CN": "播放器"
|
||||||
|
},
|
||||||
|
"gui.tab.playlist": {
|
||||||
|
"en": "Playlist",
|
||||||
|
"zh-CN": "播放列表"
|
||||||
|
},
|
||||||
|
"gui.tab.room": {
|
||||||
|
"en": "Room",
|
||||||
|
"zh-CN": "直播间"
|
||||||
|
},
|
||||||
|
"gui.tab.search": {
|
||||||
|
"en": "Search",
|
||||||
|
"zh-CN": "搜索"
|
||||||
|
},
|
||||||
|
"plugin.diange.admin": {
|
||||||
|
"en": "Admin",
|
||||||
|
"zh-CN": "管理员"
|
||||||
|
},
|
||||||
|
"plugin.diange.cooldown": {
|
||||||
|
"en": "Cooldown",
|
||||||
|
"zh-CN": "点歌冷却"
|
||||||
|
},
|
||||||
|
"plugin.diange.custom_cmd": {
|
||||||
|
"en": "Custom Command (Default one still works)",
|
||||||
|
"zh-CN": "自定义命令 (默认的依然可用)"
|
||||||
|
},
|
||||||
|
"plugin.diange.description": {
|
||||||
|
"en": "Basic Diange Configuration",
|
||||||
|
"zh-CN": "点歌基本设置"
|
||||||
|
},
|
||||||
|
"plugin.diange.permission": {
|
||||||
|
"en": "Permission",
|
||||||
|
"zh-CN": "点歌权限"
|
||||||
|
},
|
||||||
|
"plugin.diange.privilege": {
|
||||||
|
"en": "Privilege",
|
||||||
|
"zh-CN": "舰长"
|
||||||
|
},
|
||||||
|
"plugin.diange.queue_max": {
|
||||||
|
"en": "Max Queue",
|
||||||
|
"zh-CN": "最大点歌数"
|
||||||
|
},
|
||||||
|
"plugin.diange.title": {
|
||||||
|
"en": "Diange",
|
||||||
|
"zh-CN": "点歌"
|
||||||
|
},
|
||||||
|
"plugin.diange.user": {
|
||||||
|
"en": "User",
|
||||||
|
"zh-CN": "普通用户"
|
||||||
|
},
|
||||||
|
"plugin.qiege.admin": {
|
||||||
|
"en": "Admin",
|
||||||
|
"zh-CN": "管理员"
|
||||||
|
},
|
||||||
|
"plugin.qiege.custom_cmd": {
|
||||||
|
"en": "Custom Command (Default one still works)",
|
||||||
|
"zh-CN": "自定义命令 (默认的依然可用)"
|
||||||
|
},
|
||||||
|
"plugin.qiege.description": {
|
||||||
|
"en": "Basic Qiege configuration",
|
||||||
|
"zh-CN": "基础切歌设置"
|
||||||
|
},
|
||||||
|
"plugin.qiege.permission": {
|
||||||
|
"en": "Permission",
|
||||||
|
"zh-CN": "切歌权限"
|
||||||
|
},
|
||||||
|
"plugin.qiege.privilege": {
|
||||||
|
"en": "Privilege",
|
||||||
|
"zh-CN": "舰长"
|
||||||
|
},
|
||||||
|
"plugin.qiege.title": {
|
||||||
|
"en": "Qiege",
|
||||||
|
"zh-CN": "切歌"
|
||||||
|
},
|
||||||
|
"plugin.qiege.user": {
|
||||||
|
"en": "User",
|
||||||
|
"zh-CN": "切自己"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "alpha 0.6"
|
const VERSION = "alpha 0.6.5"
|
||||||
|
|
||||||
const CONFIG_PATH = "./config.ini"
|
const CONFIG_PATH = "./config.ini"
|
||||||
const Assests_PATH = "./assets"
|
const Assests_PATH = "./assets"
|
||||||
@@ -38,7 +38,7 @@ func init() {
|
|||||||
fmt.Println("config not found, using default config")
|
fmt.Println("config not found, using default config")
|
||||||
ConfigFile = ini.Empty()
|
ConfigFile = ini.Empty()
|
||||||
}
|
}
|
||||||
for _, cfg := range []Config{Log, LiveRoom, Player, Provider} {
|
for _, cfg := range []Config{Log, LiveRoom, Player, Provider, General} {
|
||||||
LoadConfig(cfg)
|
LoadConfig(cfg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
config/config_general.go
Normal file
13
config/config_general.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type _GeneralConfig struct {
|
||||||
|
Language string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *_GeneralConfig) Name() string {
|
||||||
|
return "General"
|
||||||
|
}
|
||||||
|
|
||||||
|
var General = &_GeneralConfig{
|
||||||
|
Language: "en",
|
||||||
|
}
|
||||||
14
gui/gui.go
14
gui/gui.go
@@ -2,7 +2,9 @@ package gui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"AynaLivePlayer/config"
|
"AynaLivePlayer/config"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"AynaLivePlayer/logger"
|
"AynaLivePlayer/logger"
|
||||||
|
"fmt"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -29,22 +31,22 @@ func l() *logrus.Entry {
|
|||||||
func Initialize() {
|
func Initialize() {
|
||||||
os.Setenv("FYNE_FONT", config.GetAssetPath("msyh.ttc"))
|
os.Setenv("FYNE_FONT", config.GetAssetPath("msyh.ttc"))
|
||||||
App = app.New()
|
App = app.New()
|
||||||
MainWindow = App.NewWindow("AynaLivePlayer")
|
MainWindow = App.NewWindow(fmt.Sprintf("AynaLivePlayer Ver.%s", config.VERSION))
|
||||||
|
|
||||||
tabs := container.NewAppTabs(
|
tabs := container.NewAppTabs(
|
||||||
container.NewTabItem("Player",
|
container.NewTabItem(i18n.T("gui.tab.player"),
|
||||||
newPaddedBoarder(nil, createPlayController(), nil, nil, createPlaylist()),
|
newPaddedBoarder(nil, createPlayController(), nil, nil, createPlaylist()),
|
||||||
),
|
),
|
||||||
container.NewTabItem("Search",
|
container.NewTabItem(i18n.T("gui.tab.search"),
|
||||||
newPaddedBoarder(createSearchBar(), nil, nil, nil, createSearchList()),
|
newPaddedBoarder(createSearchBar(), nil, nil, nil, createSearchList()),
|
||||||
),
|
),
|
||||||
container.NewTabItem("Room",
|
container.NewTabItem(i18n.T("gui.tab.room"),
|
||||||
newPaddedBoarder(createRoomController(), nil, nil, nil, createRoomLogger()),
|
newPaddedBoarder(createRoomController(), nil, nil, nil, createRoomLogger()),
|
||||||
),
|
),
|
||||||
container.NewTabItem("Playlist",
|
container.NewTabItem(i18n.T("gui.tab.playlist"),
|
||||||
newPaddedBoarder(nil, nil, createPlaylists(), nil, createPlaylistMedias()),
|
newPaddedBoarder(nil, nil, createPlaylists(), nil, createPlaylistMedias()),
|
||||||
),
|
),
|
||||||
container.NewTabItem("Config",
|
container.NewTabItem(i18n.T("gui.tab.config"),
|
||||||
newPaddedBoarder(nil, nil, nil, nil, createConfigLayout()),
|
newPaddedBoarder(nil, nil, nil, nil, createConfigLayout()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"AynaLivePlayer/config"
|
"AynaLivePlayer/config"
|
||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
"AynaLivePlayer/event"
|
"AynaLivePlayer/event"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"AynaLivePlayer/player"
|
"AynaLivePlayer/player"
|
||||||
"AynaLivePlayer/util"
|
"AynaLivePlayer/util"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
@@ -64,7 +65,7 @@ func createPlayController() fyne.CanvasObject {
|
|||||||
|
|
||||||
PlayController.Volume = widget.NewSlider(0, 100)
|
PlayController.Volume = widget.NewSlider(0, 100)
|
||||||
volumeIcon := widget.NewIcon(theme.VolumeMuteIcon())
|
volumeIcon := widget.NewIcon(theme.VolumeMuteIcon())
|
||||||
PlayController.ButtonLrc = widget.NewButton("lrc", func() {})
|
PlayController.ButtonLrc = widget.NewButton(i18n.T("gui.player.button.lrc"), func() {})
|
||||||
|
|
||||||
volumeControl := container.NewBorder(nil, nil, container.NewHBox(widget.NewLabel(" "), volumeIcon), nil,
|
volumeControl := container.NewBorder(nil, nil, container.NewHBox(widget.NewLabel(" "), volumeIcon), nil,
|
||||||
container.NewGridWithColumns(3, container.NewMax(PlayController.Volume), PlayController.ButtonLrc))
|
container.NewGridWithColumns(3, container.NewMax(PlayController.Volume), PlayController.ButtonLrc))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
"AynaLivePlayer/event"
|
"AynaLivePlayer/event"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"AynaLivePlayer/player"
|
"AynaLivePlayer/player"
|
||||||
"fmt"
|
"fmt"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
@@ -23,10 +24,10 @@ func (b *playlistOperationButton) Tapped(e *fyne.PointEvent) {
|
|||||||
|
|
||||||
func newPlaylistOperationButton() *playlistOperationButton {
|
func newPlaylistOperationButton() *playlistOperationButton {
|
||||||
b := &playlistOperationButton{Index: 0}
|
b := &playlistOperationButton{Index: 0}
|
||||||
deleteItem := fyne.NewMenuItem("Delete", func() {
|
deleteItem := fyne.NewMenuItem(i18n.T("gui.player.playlist.op.delete"), func() {
|
||||||
fmt.Println("delete", b.Index)
|
fmt.Println("delete", b.Index)
|
||||||
})
|
})
|
||||||
topItem := fyne.NewMenuItem("Top", func() {
|
topItem := fyne.NewMenuItem(i18n.T("gui.player.playlist.op.top"), func() {
|
||||||
controller.UserPlaylist.Move(b.Index, 0)
|
controller.UserPlaylist.Move(b.Index, 0)
|
||||||
})
|
})
|
||||||
m := fyne.NewMenu("", deleteItem, topItem)
|
m := fyne.NewMenu("", deleteItem, topItem)
|
||||||
@@ -72,8 +73,11 @@ func createPlaylist() fyne.CanvasObject {
|
|||||||
registerPlaylistHandler()
|
registerPlaylistHandler()
|
||||||
return container.NewBorder(
|
return container.NewBorder(
|
||||||
container.NewBorder(nil, nil,
|
container.NewBorder(nil, nil,
|
||||||
widget.NewLabel("#"), widget.NewLabel("OPs"),
|
widget.NewLabel("#"), widget.NewLabel(i18n.T("gui.player.playlist.ops")),
|
||||||
container.NewGridWithColumns(3, widget.NewLabel("Title"), widget.NewLabel("Artist"), widget.NewLabel("User"))),
|
container.NewGridWithColumns(3,
|
||||||
|
widget.NewLabel(i18n.T("gui.player.playlist.title")),
|
||||||
|
widget.NewLabel(i18n.T("gui.player.playlist.artist")),
|
||||||
|
widget.NewLabel(i18n.T("gui.player.playlist.user")))),
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
nil, nil,
|
nil, nil,
|
||||||
UserPlaylist.List,
|
UserPlaylist.List,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"AynaLivePlayer/config"
|
"AynaLivePlayer/config"
|
||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"fmt"
|
"fmt"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -25,9 +26,9 @@ type PlaylistManagerContainer struct {
|
|||||||
|
|
||||||
func (p *PlaylistManagerContainer) UpdateCurrentSystemPlaylist() {
|
func (p *PlaylistManagerContainer) UpdateCurrentSystemPlaylist() {
|
||||||
if config.Player.PlaylistIndex >= len(controller.PlaylistManager) {
|
if config.Player.PlaylistIndex >= len(controller.PlaylistManager) {
|
||||||
p.CurrentSystemPlaylist.SetText("Current: None")
|
p.CurrentSystemPlaylist.SetText(i18n.T("gui.playlist.current.none"))
|
||||||
}
|
}
|
||||||
p.CurrentSystemPlaylist.SetText("Current: " + controller.PlaylistManager[config.Player.PlaylistIndex].Name)
|
p.CurrentSystemPlaylist.SetText(i18n.T("gui.playlist.current") + controller.PlaylistManager[config.Player.PlaylistIndex].Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
var PlaylistManager = &PlaylistManagerContainer{}
|
var PlaylistManager = &PlaylistManagerContainer{}
|
||||||
@@ -44,22 +45,22 @@ func createPlaylists() fyne.CanvasObject {
|
|||||||
object.(*widget.Label).SetText(
|
object.(*widget.Label).SetText(
|
||||||
controller.PlaylistManager[id].Name)
|
controller.PlaylistManager[id].Name)
|
||||||
})
|
})
|
||||||
PlaylistManager.AddBtn = widget.NewButton("Add", func() {
|
PlaylistManager.AddBtn = widget.NewButton(i18n.T("gui.playlist.button.add"), func() {
|
||||||
providerEntry := widget.NewSelect(config.Provider.Priority, nil)
|
providerEntry := widget.NewSelect(config.Provider.Priority, nil)
|
||||||
idEntry := widget.NewEntry()
|
idEntry := widget.NewEntry()
|
||||||
dia := dialog.NewCustomConfirm(
|
dia := dialog.NewCustomConfirm(
|
||||||
"Add Playlist",
|
i18n.T("gui.playlist.add.title"),
|
||||||
"Add",
|
i18n.T("gui.playlist.add.confirm"),
|
||||||
"Cancel",
|
i18n.T("gui.playlist.add.cancel"),
|
||||||
container.NewVBox(
|
container.NewVBox(
|
||||||
container.New(
|
container.New(
|
||||||
layout.NewFormLayout(),
|
layout.NewFormLayout(),
|
||||||
widget.NewLabel("Provider"),
|
widget.NewLabel(i18n.T("gui.playlist.add.confirm")),
|
||||||
providerEntry,
|
providerEntry,
|
||||||
widget.NewLabel("ID/URL"),
|
widget.NewLabel(i18n.T("gui.playlist.add.id_url")),
|
||||||
idEntry,
|
idEntry,
|
||||||
),
|
),
|
||||||
widget.NewLabel("Please Enter playlist id or playlist url"),
|
widget.NewLabel(i18n.T("gui.playlist.add.prompt")),
|
||||||
),
|
),
|
||||||
func(b bool) {
|
func(b bool) {
|
||||||
if b && len(providerEntry.Selected) > 0 && len(idEntry.Text) > 0 {
|
if b && len(providerEntry.Selected) > 0 && len(idEntry.Text) > 0 {
|
||||||
@@ -73,7 +74,7 @@ func createPlaylists() fyne.CanvasObject {
|
|||||||
dia.Resize(fyne.NewSize(512, 256))
|
dia.Resize(fyne.NewSize(512, 256))
|
||||||
dia.Show()
|
dia.Show()
|
||||||
})
|
})
|
||||||
PlaylistManager.RemoveBtn = widget.NewButton("Remove", func() {
|
PlaylistManager.RemoveBtn = widget.NewButton(i18n.T("gui.playlist.button.remove"), func() {
|
||||||
controller.RemovePlaylist(PlaylistManager.Index)
|
controller.RemovePlaylist(PlaylistManager.Index)
|
||||||
//PlaylistManager.Index = 0
|
//PlaylistManager.Index = 0
|
||||||
PlaylistManager.Playlists.Select(0)
|
PlaylistManager.Playlists.Select(0)
|
||||||
@@ -95,13 +96,13 @@ func createPlaylists() fyne.CanvasObject {
|
|||||||
|
|
||||||
func createPlaylistMedias() fyne.CanvasObject {
|
func createPlaylistMedias() fyne.CanvasObject {
|
||||||
PlaylistManager.RefreshBtn = createAsyncButton(
|
PlaylistManager.RefreshBtn = createAsyncButton(
|
||||||
widget.NewButtonWithIcon("Refresh", theme.ViewRefreshIcon(), nil),
|
widget.NewButtonWithIcon(i18n.T("gui.playlist.button.refresh"), theme.ViewRefreshIcon(), nil),
|
||||||
func() {
|
func() {
|
||||||
controller.PreparePlaylistByIndex(PlaylistManager.Index)
|
controller.PreparePlaylistByIndex(PlaylistManager.Index)
|
||||||
PlaylistManager.PlaylistMedia.Refresh()
|
PlaylistManager.PlaylistMedia.Refresh()
|
||||||
})
|
})
|
||||||
PlaylistManager.SetAsSystemBtn = createAsyncButton(
|
PlaylistManager.SetAsSystemBtn = createAsyncButton(
|
||||||
widget.NewButton("SetAsSystem", nil),
|
widget.NewButton(i18n.T("gui.playlist.button.set_as_system"), nil),
|
||||||
func() {
|
func() {
|
||||||
controller.SetSystemPlaylist(PlaylistManager.Index)
|
controller.SetSystemPlaylist(PlaylistManager.Index)
|
||||||
PlaylistManager.PlaylistMedia.Refresh()
|
PlaylistManager.PlaylistMedia.Refresh()
|
||||||
@@ -143,7 +144,7 @@ func createPlaylistMedias() fyne.CanvasObject {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
return container.NewBorder(
|
return container.NewBorder(
|
||||||
container.NewHBox(PlaylistManager.RefreshBtn, PlaylistManager.SetAsSystemBtn), nil,
|
container.NewHBox(PlaylistManager.RefreshBtn, PlaylistManager.SetAsSystemBtn, PlaylistManager.CurrentSystemPlaylist), nil,
|
||||||
nil, nil,
|
nil, nil,
|
||||||
PlaylistManager.PlaylistMedia)
|
PlaylistManager.PlaylistMedia)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"AynaLivePlayer/config"
|
"AynaLivePlayer/config"
|
||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
"AynaLivePlayer/event"
|
"AynaLivePlayer/event"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"AynaLivePlayer/liveclient"
|
"AynaLivePlayer/liveclient"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -21,11 +22,11 @@ var RoomController = &RoomControllerContainer{}
|
|||||||
|
|
||||||
func createRoomController() fyne.CanvasObject {
|
func createRoomController() fyne.CanvasObject {
|
||||||
RoomController.Input = widget.NewSelectEntry(config.LiveRoom.History)
|
RoomController.Input = widget.NewSelectEntry(config.LiveRoom.History)
|
||||||
RoomController.ConnectBtn = widget.NewButton("Connect", func() {
|
RoomController.ConnectBtn = widget.NewButton(i18n.T("gui.room.btn.connect"), func() {
|
||||||
RoomController.ConnectBtn.Disable()
|
RoomController.ConnectBtn.Disable()
|
||||||
controller.SetDanmuClient(RoomController.Input.Text)
|
controller.SetDanmuClient(RoomController.Input.Text)
|
||||||
if controller.LiveClient == nil {
|
if controller.LiveClient == nil {
|
||||||
RoomController.Status.SetText("Set Failed")
|
RoomController.Status.SetText(i18n.T("gui.room.status.failed"))
|
||||||
RoomController.ConnectBtn.Enable()
|
RoomController.ConnectBtn.Enable()
|
||||||
RoomController.Status.Refresh()
|
RoomController.Status.Refresh()
|
||||||
return
|
return
|
||||||
@@ -34,9 +35,9 @@ func createRoomController() fyne.CanvasObject {
|
|||||||
controller.LiveClient.Handler().RegisterA(liveclient.EventStatusChange, "gui.liveclient.status", func(event *event.Event) {
|
controller.LiveClient.Handler().RegisterA(liveclient.EventStatusChange, "gui.liveclient.status", func(event *event.Event) {
|
||||||
d := event.Data.(liveclient.StatusChangeEvent)
|
d := event.Data.(liveclient.StatusChangeEvent)
|
||||||
if d.Connected {
|
if d.Connected {
|
||||||
RoomController.Status.SetText("Connected")
|
RoomController.Status.SetText(i18n.T("gui.room.status.connected"))
|
||||||
} else {
|
} else {
|
||||||
RoomController.Status.SetText("Disconnected")
|
RoomController.Status.SetText(i18n.T("gui.room.status.disconnected"))
|
||||||
}
|
}
|
||||||
RoomController.Status.Refresh()
|
RoomController.Status.Refresh()
|
||||||
})
|
})
|
||||||
@@ -45,13 +46,13 @@ func createRoomController() fyne.CanvasObject {
|
|||||||
RoomController.ConnectBtn.Enable()
|
RoomController.ConnectBtn.Enable()
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
RoomController.DisConnectBtn = widget.NewButton("Disconnect", func() {
|
RoomController.DisConnectBtn = widget.NewButton(i18n.T("gui.room.btn.disconnect"), func() {
|
||||||
controller.ResetDanmuClient()
|
controller.ResetDanmuClient()
|
||||||
})
|
})
|
||||||
RoomController.Status = widget.NewLabel("Waiting")
|
RoomController.Status = widget.NewLabel(i18n.T("gui.room.waiting"))
|
||||||
return container.NewBorder(
|
return container.NewBorder(
|
||||||
nil, nil,
|
nil, nil,
|
||||||
widget.NewLabel("Room ID:"), container.NewHBox(RoomController.ConnectBtn, RoomController.DisConnectBtn),
|
widget.NewLabel(i18n.T("gui.room.id")), container.NewHBox(RoomController.ConnectBtn, RoomController.DisConnectBtn),
|
||||||
container.NewBorder(nil, nil, nil, RoomController.Status, RoomController.Input),
|
container.NewBorder(nil, nil, nil, RoomController.Status, RoomController.Input),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"AynaLivePlayer/config"
|
"AynaLivePlayer/config"
|
||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"AynaLivePlayer/player"
|
"AynaLivePlayer/player"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -20,8 +21,8 @@ var SearchBar = &SearchBarContainer{}
|
|||||||
|
|
||||||
func createSearchBar() fyne.CanvasObject {
|
func createSearchBar() fyne.CanvasObject {
|
||||||
SearchBar.Input = widget.NewEntry()
|
SearchBar.Input = widget.NewEntry()
|
||||||
SearchBar.Input.SetPlaceHolder("Keyword")
|
SearchBar.Input.SetPlaceHolder(i18n.T("gui.search.placeholder"))
|
||||||
SearchBar.Button = widget.NewButton("Search", nil)
|
SearchBar.Button = widget.NewButton(i18n.T("gui.search.search"), nil)
|
||||||
SearchBar.Button.OnTapped = createAsyncOnTapped(SearchBar.Button, func() {
|
SearchBar.Button.OnTapped = createAsyncOnTapped(SearchBar.Button, func() {
|
||||||
keyword := SearchBar.Input.Text
|
keyword := SearchBar.Input.Text
|
||||||
s := make([]string, len(SearchBar.UseSource.Selected))
|
s := make([]string, len(SearchBar.UseSource.Selected))
|
||||||
@@ -44,10 +45,10 @@ func createSearchBar() fyne.CanvasObject {
|
|||||||
SearchBar.UseSource.Horizontal = true
|
SearchBar.UseSource.Horizontal = true
|
||||||
SearchBar.UseSource.SetSelected(s)
|
SearchBar.UseSource.SetSelected(s)
|
||||||
searchInput := container.NewBorder(
|
searchInput := container.NewBorder(
|
||||||
nil, nil, widget.NewLabel("Search"), SearchBar.Button,
|
nil, nil, widget.NewLabel(i18n.T("gui.search.search")), SearchBar.Button,
|
||||||
SearchBar.Input)
|
SearchBar.Input)
|
||||||
return container.NewVBox(
|
return container.NewVBox(
|
||||||
searchInput,
|
searchInput,
|
||||||
container.NewHBox(widget.NewLabel("Source filter: "), SearchBar.UseSource),
|
container.NewHBox(widget.NewLabel(i18n.T("gui.search.filter")), SearchBar.UseSource),
|
||||||
widget.NewSeparator())
|
widget.NewSeparator())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package gui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"AynaLivePlayer/player"
|
"AynaLivePlayer/player"
|
||||||
"AynaLivePlayer/provider"
|
"AynaLivePlayer/provider"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -53,11 +54,11 @@ func createSearchList() fyne.CanvasObject {
|
|||||||
})
|
})
|
||||||
return container.NewBorder(
|
return container.NewBorder(
|
||||||
container.NewBorder(nil, nil,
|
container.NewBorder(nil, nil,
|
||||||
widget.NewLabel("#"), widget.NewLabel("Operation"),
|
widget.NewLabel("#"), widget.NewLabel(i18n.T("gui.search.operation")),
|
||||||
container.NewGridWithColumns(3,
|
container.NewGridWithColumns(3,
|
||||||
widget.NewLabel("title"),
|
widget.NewLabel(i18n.T("gui.search.title")),
|
||||||
widget.NewLabel("artist"),
|
widget.NewLabel(i18n.T("gui.search.artist")),
|
||||||
widget.NewLabel("source"))),
|
widget.NewLabel(i18n.T("gui.search.source")))),
|
||||||
nil, nil, nil,
|
nil, nil, nil,
|
||||||
SearchResult.List,
|
SearchResult.List,
|
||||||
)
|
)
|
||||||
|
|||||||
63
i18n/i18n.go
Normal file
63
i18n/i18n.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package i18n
|
||||||
|
|
||||||
|
import (
|
||||||
|
"AynaLivePlayer/config"
|
||||||
|
"AynaLivePlayer/util"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const FILENAME = "translation.json"
|
||||||
|
|
||||||
|
type Translation struct {
|
||||||
|
Languages []string
|
||||||
|
Messages map[string]map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Translation) HasLanguage(lang string) bool {
|
||||||
|
for _, l := range t.Languages {
|
||||||
|
if l == lang {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var TranslationMap Translation
|
||||||
|
var CurrentLanguage string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TranslationMap = Translation{make([]string, 0), make(map[string]map[string]string)}
|
||||||
|
file, err := ioutil.ReadFile(config.GetAssetPath(FILENAME))
|
||||||
|
if err == nil {
|
||||||
|
_ = json.Unmarshal([]byte(file), &TranslationMap)
|
||||||
|
}
|
||||||
|
LoadLanguage(config.General.Language)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadLanguage(lang string) {
|
||||||
|
CurrentLanguage = lang
|
||||||
|
if TranslationMap.HasLanguage(lang) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
TranslationMap.Languages = append(TranslationMap.Languages, lang)
|
||||||
|
for id, m := range TranslationMap.Messages {
|
||||||
|
m[lang] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func T(id string) string {
|
||||||
|
if x, ok := TranslationMap.Messages[id]; ok {
|
||||||
|
return x[CurrentLanguage]
|
||||||
|
}
|
||||||
|
TranslationMap.Messages[id] = make(map[string]string)
|
||||||
|
for _, l := range TranslationMap.Languages {
|
||||||
|
TranslationMap.Messages[id][l] = id
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveTranslation() {
|
||||||
|
content, _ := util.MarshalIndentUnescape(TranslationMap, "", " ")
|
||||||
|
_ = ioutil.WriteFile(config.GetAssetPath(FILENAME), []byte(content), 0666)
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"AynaLivePlayer/config"
|
"AynaLivePlayer/config"
|
||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
"AynaLivePlayer/gui"
|
"AynaLivePlayer/gui"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"AynaLivePlayer/liveclient"
|
"AynaLivePlayer/liveclient"
|
||||||
"AynaLivePlayer/logger"
|
"AynaLivePlayer/logger"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
@@ -91,11 +92,11 @@ func (d *Diange) Execute(command string, args []string, danmu *liveclient.DanmuM
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Diange) Title() string {
|
func (d *Diange) Title() string {
|
||||||
return "Diange"
|
return i18n.T("plugin.diange.title")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Diange) Description() string {
|
func (d *Diange) Description() string {
|
||||||
return "Basic diange configuration"
|
return i18n.T("plugin.diange.description")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Diange) CreatePanel() fyne.CanvasObject {
|
func (d *Diange) CreatePanel() fyne.CanvasObject {
|
||||||
@@ -103,21 +104,21 @@ func (d *Diange) CreatePanel() fyne.CanvasObject {
|
|||||||
return d.panel
|
return d.panel
|
||||||
}
|
}
|
||||||
dgPerm := container.NewHBox(
|
dgPerm := container.NewHBox(
|
||||||
widget.NewLabel("点歌权限"),
|
widget.NewLabel(i18n.T("plugin.diange.permission")),
|
||||||
widget.NewCheckWithData("用户", binding.BindBool(&d.UserPermission)),
|
widget.NewCheckWithData(i18n.T("plugin.diange.user"), binding.BindBool(&d.UserPermission)),
|
||||||
widget.NewCheckWithData("舰长", binding.BindBool(&d.PrivilegePermission)),
|
widget.NewCheckWithData(i18n.T("plugin.diange.privilege"), binding.BindBool(&d.PrivilegePermission)),
|
||||||
widget.NewCheckWithData("管理员", binding.BindBool(&d.AdminPermission)),
|
widget.NewCheckWithData(i18n.T("plugin.diange.admin"), binding.BindBool(&d.AdminPermission)),
|
||||||
)
|
)
|
||||||
dgQueue := container.NewBorder(nil, nil,
|
dgQueue := container.NewBorder(nil, nil,
|
||||||
widget.NewLabel("最大点歌数"), nil,
|
widget.NewLabel(i18n.T("plugin.diange.queue_max")), nil,
|
||||||
widget.NewEntryWithData(binding.IntToString(binding.BindInt(&d.QueueMax))),
|
widget.NewEntryWithData(binding.IntToString(binding.BindInt(&d.QueueMax))),
|
||||||
)
|
)
|
||||||
dgCoolDown := container.NewBorder(nil, nil,
|
dgCoolDown := container.NewBorder(nil, nil,
|
||||||
widget.NewLabel("点歌冷却时间"), nil,
|
widget.NewLabel(i18n.T("plugin.diange.cooldown")), nil,
|
||||||
widget.NewEntryWithData(binding.IntToString(binding.BindInt(&d.UserCoolDown))),
|
widget.NewEntryWithData(binding.IntToString(binding.BindInt(&d.UserCoolDown))),
|
||||||
)
|
)
|
||||||
dgShortCut := container.NewBorder(nil, nil,
|
dgShortCut := container.NewBorder(nil, nil,
|
||||||
widget.NewLabel("自定义命令 (默认的依然可用)"), nil,
|
widget.NewLabel(i18n.T("plugin.diange.custom_cmd")), nil,
|
||||||
widget.NewEntryWithData(binding.BindString(&d.CustomCMD)),
|
widget.NewEntryWithData(binding.BindString(&d.CustomCMD)),
|
||||||
)
|
)
|
||||||
d.panel = container.NewVBox(dgPerm, dgQueue, dgCoolDown, dgShortCut)
|
d.panel = container.NewVBox(dgPerm, dgQueue, dgCoolDown, dgShortCut)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"AynaLivePlayer/config"
|
"AynaLivePlayer/config"
|
||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
"AynaLivePlayer/gui"
|
"AynaLivePlayer/gui"
|
||||||
|
"AynaLivePlayer/i18n"
|
||||||
"AynaLivePlayer/liveclient"
|
"AynaLivePlayer/liveclient"
|
||||||
"AynaLivePlayer/logger"
|
"AynaLivePlayer/logger"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
@@ -74,26 +75,25 @@ func (d *Qiege) Execute(command string, args []string, danmu *liveclient.DanmuMe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Qiege) Title() string {
|
func (d *Qiege) Title() string {
|
||||||
return "Qiege"
|
return i18n.T("plugin.qiege.title")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Qiege) Description() string {
|
func (d *Qiege) Description() string {
|
||||||
return "Basic Qiege configuration"
|
return i18n.T("plugin.qiege.description")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Qiege) CreatePanel() fyne.CanvasObject {
|
func (d *Qiege) CreatePanel() fyne.CanvasObject {
|
||||||
if d.panel != nil {
|
if d.panel != nil {
|
||||||
return d.panel
|
return d.panel
|
||||||
}
|
}
|
||||||
|
|
||||||
dgPerm := container.NewHBox(
|
dgPerm := container.NewHBox(
|
||||||
widget.NewLabel("切歌权限"),
|
widget.NewLabel(i18n.T("plugin.qiege.permission")),
|
||||||
widget.NewCheckWithData("切自己", binding.BindBool(&d.UserPermission)),
|
widget.NewCheckWithData(i18n.T("plugin.qiege.user"), binding.BindBool(&d.UserPermission)),
|
||||||
widget.NewCheckWithData("舰长", binding.BindBool(&d.PrivilegePermission)),
|
widget.NewCheckWithData(i18n.T("plugin.qiege.privilege"), binding.BindBool(&d.PrivilegePermission)),
|
||||||
widget.NewCheckWithData("管理员", binding.BindBool(&d.AdminPermission)),
|
widget.NewCheckWithData(i18n.T("plugin.qiege.admin"), binding.BindBool(&d.AdminPermission)),
|
||||||
)
|
)
|
||||||
qgShortCut := container.NewBorder(nil, nil,
|
qgShortCut := container.NewBorder(nil, nil,
|
||||||
widget.NewLabel("自定义命令 (默认的依然可用)"), nil,
|
widget.NewLabel(i18n.T("plugin.qiege.custom_cmd")), nil,
|
||||||
widget.NewEntryWithData(binding.BindString(&d.CustomCMD)),
|
widget.NewEntryWithData(binding.BindString(&d.CustomCMD)),
|
||||||
)
|
)
|
||||||
d.panel = container.NewVBox(dgPerm, qgShortCut)
|
d.panel = container.NewVBox(dgPerm, qgShortCut)
|
||||||
|
|||||||
3
todo.txt
3
todo.txt
@@ -5,7 +5,7 @@
|
|||||||
- @5 delete optimization
|
- @5 delete optimization
|
||||||
|
|
||||||
- 歌词来源
|
- 歌词来源
|
||||||
- kuwo歌单
|
|
||||||
- 文本输出
|
- 文本输出
|
||||||
- web输出
|
- web输出
|
||||||
- 进入beta版本
|
- 进入beta版本
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
----
|
----
|
||||||
|
|
||||||
Finished
|
Finished
|
||||||
|
- 2022.6.25: kuwo歌单
|
||||||
- 2022.6.25: 设置界面
|
- 2022.6.25: 设置界面
|
||||||
- 2022.6.25: @6 bug, race condition, playlist size changed during playlist update.
|
- 2022.6.25: @6 bug, race condition, playlist size changed during playlist update.
|
||||||
- 2022.6.23: 用户歌单操作
|
- 2022.6.23: 用户歌单操作
|
||||||
33
util/json.go
Normal file
33
util/json.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MarshalUnescape(v interface{}) (string, error) {
|
||||||
|
bf := bytes.NewBuffer([]byte{})
|
||||||
|
jsonEncoder := json.NewEncoder(bf)
|
||||||
|
jsonEncoder.SetEscapeHTML(false)
|
||||||
|
err := jsonEncoder.Encode(v)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return bf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshalIndentUnescape(v interface{}, prefix, indent string) (string, error) {
|
||||||
|
bf := bytes.NewBuffer([]byte{})
|
||||||
|
jsonEncoder := json.NewEncoder(bf)
|
||||||
|
jsonEncoder.SetEscapeHTML(false)
|
||||||
|
err := jsonEncoder.Encode(v)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = json.Indent(&buf, bf.Bytes(), prefix, indent)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user