node build fixed

This commit is contained in:
ra_ma
2025-09-20 14:08:38 +01:00
parent c6ebbe069d
commit 3d298fa434
1516 changed files with 535727 additions and 2 deletions

View File

@@ -0,0 +1,188 @@
package mpchc
const (
setVolumeCmd = -2
seekCmd = -1
quickOpenFileCmd = 969
openFileCmd = 800
openDVDBDCmd = 801
openDeviceCmd = 802
reopenFileCmd = 976
moveToRecycleBinCmd = 24044
saveACopyCmd = 805
saveImageCmd = 806
saveImageAutoCmd = 807
saveThumbnailsCmd = 808
loadSubtitleCmd = 809
saveSubtitleCmd = 810
closeCmd = 804
propertiesCmd = 814
exitCmd = 816
playPauseCmd = 889
playCmd = 887
pauseCmd = 888
stopCmd = 890
framestepCmd = 891
framestepBackCmd = 892
goToCmd = 893
increaseRateCmd = 895
decreaseRateCmd = 894
resetRateCmd = 896
audioDelayPlus10MsCmd = 905
audioDelayMinus10MsCmd = 906
jumpForwardSmallCmd = 900
jumpBackwardSmallCmd = 899
jumpForwardMediumCmd = 902
jumpBackwardMediumCmd = 901
jumpForwardLargeCmd = 904
jumpBackwardLargeCmd = 903
jumpForwardKeyframeCmd = 898
jumpBackwardKeyframeCmd = 897
jumpToBeginningCmd = 996
nextCmd = 922
previousCmd = 921
nextFileCmd = 920
previousFileCmd = 919
tunerScanCmd = 974
quickAddFavoriteCmd = 975
toggleCaptionAndMenuCmd = 817
toggleSeekerCmd = 818
toggleControlsCmd = 819
toggleInformationCmd = 820
toggleStatisticsCmd = 821
toggleStatusCmd = 822
toggleSubresyncBarCmd = 823
togglePlaylistBarCmd = 824
toggleCaptureBarCmd = 825
toggleNavigationBarCmd = 33415
toggleDebugShadersCmd = 826
viewMinimalCmd = 827
viewCompactCmd = 828
viewNormalCmd = 829
fullscreenCmd = 830
fullscreenWithoutResChangeCmd = 831
zoom50Cmd = 832
zoom100Cmd = 833
zoom200Cmd = 834
zoomAutoFitCmd = 968
zoomAutoFitLargerOnlyCmd = 4900
nextARPreseCmd = 859
vidFrmHalfCmd = 835
vidFrmNormalCmd = 836
vidFrmDoubleCmd = 837
vidFrmStretchCmd = 838
vidFrmInsideCmd = 839
vidFrmZoom1Cmd = 841
vidFrmZoom2Cmd = 842
vidFrmOutsideCmd = 840
vidFrmSwitchZoomCmd = 843
alwaysOnTopCmd = 884
pnsResetCmd = 861
pnsIncSizeCmd = 862
pnsIncWidthCmd = 864
pnsIncHeightCmd = 866
pnsDecSizeCmd = 863
pnsDecWidthCmd = 865
pnsDecHeightCmd = 867
pnsCenterCmd = 876
pnsLeftCmd = 868
pnsRightCmd = 869
pnsUpCmd = 870
pnsDownCmd = 871
pnsUpLeftCmd = 872
pnsUpRightCmd = 873
pnsDownLeftCmd = 874
pnsDownRightCmd = 875
pnsRotateXPlusCmd = 877
pnsRotateXMinusCmd = 878
pnsRotateYPlusCmd = 879
pnsRotateYMinusCmd = 880
pnsRotateZPlusCmd = 881
pnsRotateZMinusCmd = 882
volumeUpCmd = 907
volumeDownCmd = 908
volumeMuteCmd = 909
volumeBoostIncreaseCmd = 970
volumeBoostDecreaseCmd = 971
volumeBoostMinCmd = 972
volumeBoostMaxCmd = 973
toggleCustomChannelMappingCmd = 993
toggleNormalizationCmd = 994
toggleRegainVolumeCmd = 995
brightnessIncreaseCmd = 984
brightnessDecreaseCmd = 985
contrastIncreaseCmd = 986
contrastDecreaseCmd = 987
hueIncreaseCmd = 988
hueDecreaseCmd = 989
saturationIncreaseCmd = 990
saturationDecreaseCmd = 991
resetColorSettingsCmd = 992
dvdTitleMenuCmd = 923
dvdRootMenuCmd = 924
dvdSubtitleMenuCmd = 925
dvdAudioMenuCmd = 926
dvdAngleMenuCmd = 927
dvdChapterMenuCmd = 928
dvdMenuLeftCmd = 929
dvdMenuRightCmd = 930
dvdMenuUpCmd = 931
dvdMenuDownCmd = 932
dvdMenuActivateCmd = 933
dvdMenuBackCmd = 934
dvdMenuLeaveCmd = 935
bossKeyCmd = 944
playerMenuShortCmd = 949
playerMenuLongCmd = 950
filtersMenuCmd = 951
optionsCmd = 815
nextAudioCmd = 952
prevAudioCmd = 953
nextSubtitleCmd = 954
prevSubtitleCmd = 955
onOffSubtitleCmd = 956
reloadSubtitlesCmd = 2302
downloadSubtitlesCmd = 812
nextAudioOGMCmd = 957
prevAudioOGMCmd = 958
nextSubtitleOGMCmd = 959
prevSubtitleOGMCmd = 960
nextAngleDVDCmd = 961
prevAngleDVDCmd = 962
nextAudioDVDCmd = 963
prevAudioDVDCmd = 964
nextSubtitleDVDCmd = 965
prevSubtitleDVDCmd = 966
onOffSubtitleDVDCmd = 967
tearingTestCmd = 32769
remainingTimeCmd = 32778
nextShaderPresetCmd = 57382
prevShaderPresetCmd = 57384
toggleDirect3DFullscreenCmd = 32779
gotoPrevSubtitleCmd = 32780
gotoNextSubtitleCmd = 32781
shiftSubtitleLeftCmd = 32782
shiftSubtitleRightCmd = 32783
displayStatsCmd = 32784
resetDisplayStatsCmd = 33405
vsyncCmd = 33243
enableFrameTimeCorrectionCmd = 33265
accurateVsyncCmd = 33260
decreaseVsyncOffsetCmd = 33246
increaseVsyncOffsetCmd = 33247
subtitleDelayMinusCmd = 24000
subtitleDelayPlusCmd = 24001
afterPlaybackExitCmd = 912
afterPlaybackStandByCmd = 913
afterPlaybackHibernateCmd = 914
afterPlaybackShutdownCmd = 915
afterPlaybackLogOffCmd = 916
afterPlaybackLockCmd = 917
afterPlaybackTurnOffTheMonitorCmd = 918
afterPlaybackPlayNextFileInTheFolderCmd = 947
toggleEDLWindowCmd = 846
edlSetInCmd = 847
edlSetOutCmd = 848
edlNewClipCmd = 849
edlSaveCmd = 860
)

View File

@@ -0,0 +1,139 @@
package mpchc
import (
"fmt"
"io"
"net/http"
neturl "net/url"
"path/filepath"
"strings"
"github.com/rs/zerolog"
)
type MpcHc struct {
Host string
Port int
Path string
Logger *zerolog.Logger
}
func (api *MpcHc) url() string {
return fmt.Sprintf("http://%s:%d", api.Host, api.Port)
}
// Execute sends a command to MPC and returns the response.
func (api *MpcHc) Execute(command int, data map[string]interface{}) (string, error) {
url := fmt.Sprintf("%s/command.html?wm_command=%d", api.url(), command)
if data != nil {
queryParams := neturl.Values{}
for key, value := range data {
queryParams.Add(key, fmt.Sprintf("%v", value))
}
url += "&" + queryParams.Encode()
}
response, err := http.Get(url)
if err != nil {
api.Logger.Error().Err(err).Msg("mpc hc: Failed to execute command")
return "", err
}
defer response.Body.Close()
// Check HTTP status code and errors
statusCode := response.StatusCode
if !((statusCode >= 200) && (statusCode <= 299)) {
err = fmt.Errorf("http error code: %d\n", statusCode)
return "", err
}
// Get byte response and http status code
byteArr, readErr := io.ReadAll(response.Body)
if readErr != nil {
err = fmt.Errorf("error reading response: %s\n", readErr)
return "", err
}
// Write response
res := string(byteArr)
return res, nil
}
func escapeInput(input string) string {
if strings.HasPrefix(input, "http") {
return neturl.QueryEscape(input)
} else {
input = filepath.FromSlash(input)
return strings.ReplaceAll(neturl.QueryEscape(input), "+", "%20")
}
}
// OpenAndPlay opens a video file in MPC.
func (api *MpcHc) OpenAndPlay(filePath string) (string, error) {
url := fmt.Sprintf("%s/browser.html?path=%s", api.url(), escapeInput(filePath))
api.Logger.Trace().Str("url", url).Msg("mpc hc: Opening and playing")
response, err := http.Get(url)
if err != nil {
api.Logger.Error().Err(err).Msg("mpc hc: Failed to connect to MPC")
return "", err
}
defer response.Body.Close()
// Check HTTP status code and errors
statusCode := response.StatusCode
if !((statusCode >= 200) && (statusCode <= 299)) {
err = fmt.Errorf("http error code: %d\n", statusCode)
api.Logger.Error().Err(err).Msg("mpc hc: Failed to open and play")
return "", err
}
// Get byte response and http status code
byteArr, readErr := io.ReadAll(response.Body)
if readErr != nil {
err = fmt.Errorf("error reading response: %s\n", readErr)
api.Logger.Error().Err(err).Msg("mpc hc: Failed to open and play")
return "", err
}
// Write response
res := string(byteArr)
return res, nil
}
// GetVariables retrieves player variables from MPC.
func (api *MpcHc) GetVariables() (*Variables, error) {
url := fmt.Sprintf("%s/variables.html", api.url())
response, err := http.Get(url)
if err != nil {
api.Logger.Error().Err(err).Msg("mpc hc: Failed to get variables")
return &Variables{}, err
}
defer response.Body.Close()
// Check HTTP status code and errors
statusCode := response.StatusCode
if !((statusCode >= 200) && (statusCode <= 299)) {
err = fmt.Errorf("http error code: %d\n", statusCode)
api.Logger.Error().Err(err).Msg("mpc hc: Failed to get variables")
return &Variables{}, err
}
// Get byte response and http status code
byteArr, readErr := io.ReadAll(response.Body)
if readErr != nil {
err = fmt.Errorf("error reading response: %s\n", readErr)
api.Logger.Error().Err(err).Msg("mpc hc: Failed to get variables")
return &Variables{}, err
}
// Write response
res := string(byteArr)
vars := parseVariables(res)
return vars, nil
}

View File

@@ -0,0 +1,101 @@
package mpchc
import (
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/assert"
"seanime/internal/test_utils"
"seanime/internal/util"
"testing"
"time"
)
func TestMpcHc_Start(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.MediaPlayer())
mpc := &MpcHc{
Host: test_utils.ConfigData.Provider.MpcHost,
Path: test_utils.ConfigData.Provider.MpcPath,
Port: test_utils.ConfigData.Provider.MpcPort,
Logger: util.NewLogger(),
}
err := mpc.Start()
assert.NoError(t, err)
}
func TestMpcHc_Play(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.MediaPlayer())
mpc := &MpcHc{
Host: test_utils.ConfigData.Provider.MpcHost,
Path: test_utils.ConfigData.Provider.MpcPath,
Port: test_utils.ConfigData.Provider.MpcPort,
Logger: util.NewLogger(),
}
err := mpc.Start()
assert.NoError(t, err)
res, err := mpc.OpenAndPlay("E:\\ANIME\\Violet.Evergarden.The.Movie.1080p.Dual.Audio.BDRip.10.bits.DD.x265-EMBER.mkv")
assert.NoError(t, err)
t.Log(res)
}
func TestMpcHc_GetVariables(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.MediaPlayer())
mpc := &MpcHc{
Host: test_utils.ConfigData.Provider.MpcHost,
Path: test_utils.ConfigData.Provider.MpcPath,
Port: test_utils.ConfigData.Provider.MpcPort,
Logger: util.NewLogger(),
}
err := mpc.Start()
assert.NoError(t, err)
res, err := mpc.GetVariables()
if err != nil {
t.Fatal(err.Error())
}
spew.Dump(res)
}
func TestMpcHc_Seek(t *testing.T) {
test_utils.InitTestProvider(t, test_utils.MediaPlayer())
mpc := &MpcHc{
Host: test_utils.ConfigData.Provider.MpcHost,
Path: test_utils.ConfigData.Provider.MpcPath,
Port: test_utils.ConfigData.Provider.MpcPort,
Logger: util.NewLogger(),
}
err := mpc.Start()
assert.NoError(t, err)
_, err = mpc.OpenAndPlay("E:\\ANIME\\[SubsPlease] Bocchi the Rock! (01-12) (1080p) [Batch]\\[SubsPlease] Bocchi the Rock! - 01v2 (1080p) [ABDDAE16].mkv")
assert.NoError(t, err)
err = mpc.Pause()
time.Sleep(400 * time.Millisecond)
err = mpc.Seek(100000)
assert.NoError(t, err)
time.Sleep(400 * time.Millisecond)
err = mpc.Pause()
vars, err := mpc.GetVariables()
assert.NoError(t, err)
spew.Dump(vars)
}

View File

@@ -0,0 +1,57 @@
package mpchc
import (
"fmt"
"seanime/internal/util"
"strings"
"time"
)
func (api *MpcHc) getExecutableName() string {
if len(api.Path) > 0 {
if strings.Contains(api.Path, "64") {
return "mpc-hc64.exe"
} else {
return strings.Replace(api.Path, "C:\\Program Files\\MPC-HC\\", "", 1)
}
}
return "mpc-hc64.exe"
}
func (api *MpcHc) GetExecutablePath() string {
if len(api.Path) > 0 {
return api.Path
}
return "C:\\Program Files\\MPC-HC\\mpc-hc64.exe"
}
func (api *MpcHc) isRunning(executable string) bool {
cmd := util.NewCmd("tasklist")
output, err := cmd.Output()
if err != nil {
return false
}
return strings.Contains(string(output), executable)
}
func (api *MpcHc) Start() error {
name := api.getExecutableName()
exe := api.GetExecutablePath()
if api.isRunning(name) {
return nil
}
cmd := util.NewCmd(exe)
err := cmd.Start()
if err != nil {
api.Logger.Error().Err(err).Msg("mpc-hc: Error starting MPC-HC")
return fmt.Errorf("error starting MPC-HC: %w", err)
}
time.Sleep(1 * time.Second)
return nil
}

View File

@@ -0,0 +1,58 @@
package mpchc
import "strconv"
func (api *MpcHc) Play() (err error) {
_, err = api.Execute(playCmd, nil)
return
}
func (api *MpcHc) Pause() (err error) {
_, err = api.Execute(pauseCmd, nil)
return
}
func (api *MpcHc) TogglePlay() (err error) {
_, err = api.Execute(playPauseCmd, nil)
return
}
func (api *MpcHc) Stop() (err error) {
_, err = api.Execute(stopCmd, nil)
return
}
func (api *MpcHc) ToggleFullScreen() (err error) {
_, err = api.Execute(fullscreenCmd, nil)
return
}
// Seek position in ms
func (api *MpcHc) Seek(pos int) (err error) {
_, err = api.Execute(seekCmd, map[string]interface{}{"position": millisecondsToDuration(pos)})
return
}
//----------------------------------------------------------------------------------------------------------------------
func millisecondsToDuration(ms int) string {
if ms <= 0 {
return "00:00:00"
}
duration := ms / 1000
hours := duration / 3600
duration %= 3600
minutes := duration / 60
duration %= 60
return padStart(strconv.Itoa(hours), 2, "0") + ":" + padStart(strconv.Itoa(minutes), 2, "0") + ":" + padStart(strconv.Itoa(duration), 2, "0")
}
func padStart(s string, length int, pad string) string {
for len(s) < length {
s = pad + s
}
return s
}

View File

@@ -0,0 +1,68 @@
package mpchc
import (
"github.com/PuerkitoBio/goquery"
"strconv"
"strings"
)
type Variables struct {
Version string `json:"version"`
File string `json:"file"`
FilePath string `json:"filepath"`
FileDir string `json:"filedir"`
Size string `json:"size"`
State int `json:"state"`
StateString string `json:"statestring"`
Position float64 `json:"position"`
PositionString string `json:"positionstring"`
Duration float64 `json:"duration"`
DurationString string `json:"durationstring"`
VolumeLevel float64 `json:"volumelevel"`
Muted bool `json:"muted"`
}
func parseVariables(variablePageHtml string) *Variables {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(variablePageHtml))
if err != nil {
// Handle error
return &Variables{}
}
fields := make(map[string]string)
doc.Find("p").Each(func(_ int, s *goquery.Selection) {
id, exists := s.Attr("id")
if !exists {
return
}
text := s.Text()
fields[id] = text
})
return &Variables{
Version: fields["version"],
File: fields["file"],
FilePath: fields["filepath"],
FileDir: fields["filedir"],
Size: fields["size"],
State: parseInt(fields["state"]),
StateString: fields["statestring"],
Position: parseFloat(fields["position"]),
PositionString: fields["positionstring"],
Duration: parseFloat(fields["duration"]),
DurationString: fields["durationstring"],
VolumeLevel: parseFloat(fields["volumelevel"]),
Muted: fields["muted"] != "0",
}
}
func parseInt(value string) int {
intValue, _ := strconv.Atoi(value)
return intValue
}
func parseFloat(value string) float64 {
floatValue, _ := strconv.ParseFloat(value, 64)
return floatValue
}