├── .github ├── dependabot.yml └── workflows │ └── go.yml ├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── api ├── api.go ├── bilibili.go └── server.go ├── browser.go ├── cors └── cors.go ├── go.mod ├── go.sum ├── main.go ├── utils └── utils.go └── wails.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | 12 | build: 13 | name: Build 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - name: Set up Go 19 | uses: actions/setup-go@v3 20 | with: 21 | go-version: 1.18 22 | 23 | - name: Build dependencies 24 | run: | 25 | git clone https://github.com/xmdhs/player 26 | cd player 27 | git reset --hard 8688cf11d87fbbe67167d0d12b4ada5c88e98c5b 28 | npm install -g pnpm 29 | pnpm install 30 | pnpm run build 31 | mkdir ../frontend 32 | cp -r dist/ ../frontend/ 33 | 34 | #- name: Get Wails dependencies 35 | # run: sudo apt update && sudo apt install -y libgtk-3-dev libwebkit2gtk-4.0-dev 36 | 37 | - name: Build 38 | run: | 39 | go install github.com/wailsapp/wails/v2/cmd/wails@latest 40 | go build -trimpath -ldflags="-w -s" -o build/bin/player-linux-browser -tags=browser 41 | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags="-w -s" -o build/bin/player-windows-browser.exe -tags=browser 42 | wails build -trimpath -ldflags="-w -s" -o player-windows.exe -platform windows/amd64 -upx 43 | 44 | - name: Test 45 | run: go test -tags browser -race -v ./... 46 | 47 | - name: Upload a Build Artifact 48 | uses: actions/upload-artifact@v2 49 | with: 50 | # A file, directory or wildcard pattern that describes what to upload 51 | path: build/bin/player-* 52 | name: ${{ github.run_number }} 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | frontend 3 | player.db 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "gopls": { 3 | "buildFlags": [ 4 | "-tags=" 5 | ] 6 | }, 7 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 xmdhs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # player-go 2 | 一个简单的播放器,对于 dplayer 的包装。 3 | 4 | 可以加载本地视频、视频直链以及 b 站视频,并且能加载 b 站、巴哈、弹弹play 的弹幕。 5 | 6 | 以及弹幕正则和按用户屏蔽功能。 7 | 8 | ## 下载 9 | https://github.com/xmdhs/player-go/releases 10 | -------------------------------------------------------------------------------- /api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | 7 | "github.com/julienschmidt/httprouter" 8 | "go.etcd.io/bbolt" 9 | ) 10 | 11 | func store(db *bbolt.DB) httprouter.Handle { 12 | return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { 13 | w.Header().Set("Access-Control-Allow-Origin", "*") 14 | key := p.ByName("key") 15 | b, err := io.ReadAll(r.Body) 16 | if err != nil { 17 | http.Error(w, err.Error(), http.StatusInternalServerError) 18 | return 19 | } 20 | err = db.Update(func(tx *bbolt.Tx) error { 21 | bkt, err := tx.CreateBucketIfNotExists([]byte("data")) 22 | if err != nil { 23 | return err 24 | } 25 | return bkt.Put([]byte(key), b) 26 | }) 27 | if err != nil { 28 | http.Error(w, err.Error(), http.StatusInternalServerError) 29 | return 30 | } 31 | } 32 | } 33 | 34 | func read(db *bbolt.DB) httprouter.Handle { 35 | return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { 36 | w.Header().Set("Access-Control-Allow-Origin", "*") 37 | key := p.ByName("key") 38 | err := db.View(func(tx *bbolt.Tx) error { 39 | bkt := tx.Bucket([]byte("data")) 40 | if bkt == nil { 41 | http.NotFound(w, r) 42 | return nil 43 | } 44 | v := bkt.Get([]byte(key)) 45 | if v == nil { 46 | http.NotFound(w, r) 47 | return nil 48 | } 49 | w.Write(v) 50 | return nil 51 | }) 52 | if err != nil { 53 | http.Error(w, err.Error(), http.StatusInternalServerError) 54 | return 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /api/bilibili.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "net/http" 7 | "net/url" 8 | "time" 9 | 10 | "github.com/julienschmidt/httprouter" 11 | ) 12 | 13 | func bilivideoGet(t *http.Transport) httprouter.Handle { 14 | c := http.Client{ 15 | Transport: t, 16 | Timeout: 10 * time.Second, 17 | } 18 | return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { 19 | w.Header().Set("Access-Control-Allow-Origin", "*") 20 | qn := r.FormValue("qn") 21 | if qn == "" { 22 | qn = "120" 23 | } 24 | bvid := r.FormValue("bvid") 25 | cid := r.FormValue("cid") 26 | 27 | if bvid == "" || cid == "" { 28 | http.Error(w, "bvid or cid is required", http.StatusBadRequest) 29 | return 30 | } 31 | fnval := "0" 32 | if qn == "120" { 33 | fnval = "128" 34 | } 35 | DedeUserID := r.FormValue("DedeUserID") 36 | DedeUserID__ckMd5 := r.FormValue("DedeUserID__ckMd5") 37 | SESSDATA := r.FormValue("SESSDATA") 38 | bili_jct := r.FormValue("bili_jct") 39 | 40 | uq := url.Values{} 41 | uq.Set("bvid", bvid) 42 | uq.Set("cid", cid) 43 | uq.Set("fnval", fnval) 44 | uq.Set("qn", qn) 45 | reqs, err := http.NewRequest("GET", "https://api.bilibili.com/x/player/playurl?"+uq.Encode(), nil) 46 | if err != nil { 47 | http.Error(w, err.Error(), http.StatusInternalServerError) 48 | return 49 | } 50 | reqs.Header.Set("cookie", "DedeUserID="+DedeUserID+"; DedeUserID__ckMd5="+DedeUserID__ckMd5+"; SESSDATA="+SESSDATA+"; bili_jct="+bili_jct) 51 | resp, err := c.Do(reqs) 52 | if resp != nil { 53 | defer resp.Body.Close() 54 | } 55 | if err != nil { 56 | http.Error(w, err.Error(), http.StatusInternalServerError) 57 | return 58 | } 59 | b, err := io.ReadAll(resp.Body) 60 | if err != nil { 61 | http.Error(w, err.Error(), http.StatusInternalServerError) 62 | return 63 | } 64 | bvideo := bili[biliVideoInfo]{} 65 | err = json.Unmarshal(b, &bvideo) 66 | if err != nil { 67 | http.Error(w, err.Error(), http.StatusInternalServerError) 68 | return 69 | } 70 | if bvideo.Code != 0 { 71 | http.Error(w, bvideo.Message, http.StatusInternalServerError) 72 | return 73 | } 74 | if len(bvideo.Data.Durl) != 1 { 75 | http.Error(w, "无法处理分段视频", http.StatusInternalServerError) 76 | return 77 | } 78 | w.Write([]byte(bvideo.Data.Durl[0].URL)) 79 | } 80 | } 81 | 82 | type bili[T any] struct { 83 | Code int `json:"code"` 84 | Data T `json:"data"` 85 | Message string `json:"message"` 86 | TTL int `json:"ttl"` 87 | } 88 | 89 | type biliVideoInfo struct { 90 | AcceptDescription []string `json:"accept_description"` 91 | AcceptFormat string `json:"accept_format"` 92 | AcceptQuality []int `json:"accept_quality"` 93 | Durl []biliVideoDurl `json:"durl"` 94 | Format string `json:"format"` 95 | From string `json:"from"` 96 | Message string `json:"message"` 97 | Quality int `json:"quality"` 98 | Result string `json:"result"` 99 | SeekParam string `json:"seek_param"` 100 | SeekType string `json:"seek_type"` 101 | Timelength int `json:"timelength"` 102 | VideoCodecid int `json:"video_codecid"` 103 | } 104 | 105 | type biliVideoDurl struct { 106 | Ahead string `json:"ahead"` 107 | BackupURL []string `json:"backup_url"` 108 | Length int `json:"length"` 109 | Order int `json:"order"` 110 | Size int `json:"size"` 111 | URL string `json:"url"` 112 | Vhead string `json:"vhead"` 113 | } 114 | -------------------------------------------------------------------------------- /api/server.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/julienschmidt/httprouter" 10 | "github.com/xmdhs/player-go/utils" 11 | "go.etcd.io/bbolt" 12 | ) 13 | 14 | func Server(cxt context.Context, db *bbolt.DB, t *http.Transport) int { 15 | p := utils.GetProt() 16 | 17 | mux := httprouter.New() 18 | 19 | mux.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 20 | if r.Header.Get("Access-Control-Request-Method") != "" { 21 | header := w.Header() 22 | header.Set("Access-Control-Allow-Origin", "*") 23 | header.Set("Access-Control-Allow-Methods", r.Header.Get("Access-Control-Request-Method")) 24 | header.Set("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers")) 25 | } 26 | w.WriteHeader(http.StatusNoContent) 27 | }) 28 | 29 | mux.POST("/store/:key", store(db)) 30 | mux.GET("/store/:key", read(db)) 31 | mux.GET("/bilibili.flv", bilivideoGet(t)) 32 | 33 | s := http.Server{ 34 | Addr: "127.0.0.1:" + strconv.FormatInt(p, 10), 35 | Handler: mux, 36 | ReadTimeout: 10 * time.Second, 37 | WriteTimeout: 60 * time.Second, 38 | ReadHeaderTimeout: 5 * time.Second, 39 | } 40 | 41 | go s.ListenAndServe() 42 | go func() { 43 | <-cxt.Done() 44 | s.Shutdown(cxt) 45 | }() 46 | return int(p) 47 | } 48 | -------------------------------------------------------------------------------- /browser.go: -------------------------------------------------------------------------------- 1 | //go:build browser 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "context" 8 | "embed" 9 | "flag" 10 | "fmt" 11 | "log" 12 | "net/http" 13 | "strconv" 14 | "time" 15 | 16 | "github.com/pkg/browser" 17 | "github.com/xmdhs/player-go/api" 18 | "github.com/xmdhs/player-go/cors" 19 | "go.etcd.io/bbolt" 20 | 21 | "github.com/mattn/go-ieproxy" 22 | ) 23 | 24 | //go:embed frontend/dist 25 | var assets embed.FS 26 | 27 | func main() { 28 | cxt := context.Background() 29 | 30 | t := http.DefaultTransport.(*http.Transport).Clone() 31 | t.Proxy = ieproxy.GetProxyFunc() 32 | 33 | db, err := bbolt.Open("player.db", 0600, nil) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | cport := cors.Server(cxt, t) 39 | apiPort := api.Server(cxt, db, t) 40 | 41 | ieproxy.OverrideEnvWithStaticProxy() 42 | 43 | mux := http.NewServeMux() 44 | 45 | mux.HandleFunc("/", indexH(cport, apiPort)) 46 | mux.Handle("/assets/", AddPrefix("frontend/dist", http.FileServer(http.FS(assets)))) 47 | 48 | server := &http.Server{ 49 | Addr: "127.0.0.1:" + port, 50 | ReadTimeout: 10 * time.Second, 51 | WriteTimeout: 20 * time.Second, 52 | ReadHeaderTimeout: 5 * time.Second, 53 | Handler: mux, 54 | } 55 | fmt.Printf("http://127.0.0.1:%v", port) 56 | browser.OpenURL("http://127.0.0.1:" + port) 57 | log.Println(server.ListenAndServe()) 58 | } 59 | 60 | var ( 61 | port string 62 | ) 63 | 64 | func init() { 65 | flag.StringVar(&port, "port", "8080", "port") 66 | flag.Parse() 67 | } 68 | 69 | func indexH(port, apiPort int) http.HandlerFunc { 70 | b, err := assets.ReadFile("frontend/dist/index.html") 71 | if err != nil { 72 | panic(err) 73 | } 74 | b = bytes.Replace(b, []byte(``), []byte("\n"+ 75 | ""), -1) 76 | return func(w http.ResponseWriter, r *http.Request) { 77 | w.Header().Set("Content-Type", "text/html") 78 | w.Write(b) 79 | } 80 | } 81 | 82 | func AddPrefix(prefix string, h http.Handler) http.Handler { 83 | if prefix == "" { 84 | return h 85 | } 86 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 87 | if r.URL.Path == "/" { 88 | r.URL.Path = prefix 89 | } else { 90 | r.URL.Path = prefix + r.URL.Path 91 | } 92 | h.ServeHTTP(w, r) 93 | }) 94 | } 95 | -------------------------------------------------------------------------------- /cors/cors.go: -------------------------------------------------------------------------------- 1 | package cors 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net/http" 7 | "net/http/httputil" 8 | "net/url" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | "github.com/xmdhs/player-go/utils" 14 | ) 15 | 16 | func Server(cxt context.Context, t *http.Transport) int { 17 | p := utils.GetProt() 18 | s := http.Server{ 19 | Addr: "127.0.0.1:" + strconv.FormatInt(p, 10), 20 | Handler: handler(t), 21 | ReadTimeout: 10 * time.Second, 22 | WriteTimeout: 60 * time.Second, 23 | ReadHeaderTimeout: 5 * time.Second, 24 | } 25 | 26 | go s.ListenAndServe() 27 | go func() { 28 | <-cxt.Done() 29 | s.Shutdown(cxt) 30 | }() 31 | return int(p) 32 | } 33 | 34 | func handler(t *http.Transport) http.HandlerFunc { 35 | return func(w http.ResponseWriter, r *http.Request) { 36 | q := r.URL.Query() 37 | proxyURL := q.Get("_proxyURL") 38 | if proxyURL != "" { 39 | purl, err := url.Parse(proxyURL) 40 | if err != nil { 41 | w.WriteHeader(http.StatusBadRequest) 42 | w.Write([]byte(err.Error())) 43 | return 44 | } 45 | corsProxy(purl, t, q.Get("_referer")).ServeHTTP(w, r) 46 | return 47 | } 48 | u := r.URL.String() 49 | u = strings.TrimPrefix(u, "/") 50 | purl, err := url.Parse(u) 51 | if err != nil { 52 | w.WriteHeader(http.StatusBadRequest) 53 | w.Write([]byte(err.Error())) 54 | return 55 | } 56 | if purl.Scheme == "" { 57 | purl.Scheme = "http" 58 | } 59 | if purl.Host == "" { 60 | w.WriteHeader(http.StatusBadRequest) 61 | return 62 | } 63 | corsProxy(purl, t, "").ServeHTTP(w, r) 64 | } 65 | } 66 | 67 | func corsProxy(u *url.URL, t *http.Transport, referer string) http.HandlerFunc { 68 | return func(w http.ResponseWriter, r *http.Request) { 69 | proxy := httputil.NewSingleHostReverseProxy(&url.URL{ 70 | Host: u.Host, 71 | Scheme: u.Scheme, 72 | }) 73 | proxy.ErrorLog = log.Default() 74 | proxy.Transport = t 75 | 76 | df := proxy.Director 77 | 78 | proxy.Director = func(r *http.Request) { 79 | df(r) 80 | r.Header.Del("referer") 81 | r.Header.Del("origin") 82 | r.Header.Del("X-Forwarded-For") 83 | r.Header.Del("X-Real-IP") 84 | r.Host = u.Host 85 | if referer != "" { 86 | r.Header.Set("referer", referer) 87 | } 88 | } 89 | 90 | proxy.ModifyResponse = func(r *http.Response) error { 91 | r.Header.Set("Access-Control-Allow-Origin", "*") 92 | r.Header.Set("Access-Control-Allow-Methods", r.Header.Get("Access-Control-Request-Method")) 93 | r.Header.Set("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers")) 94 | r.Header.Set("X-ToProxy", r.Request.URL.String()) 95 | if r.StatusCode >= 300 && r.StatusCode < 400 && r.Header.Get("Location") != "" { 96 | r.Header.Set("Location", "/"+r.Header.Get("Location")) 97 | } 98 | return nil 99 | } 100 | 101 | r.URL = u 102 | r.RemoteAddr = "" 103 | r.RequestURI = u.String() 104 | 105 | proxy.ServeHTTP(w, r) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xmdhs/player-go 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/julienschmidt/httprouter v1.3.0 7 | github.com/mattn/go-ieproxy v0.0.7 8 | github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 9 | github.com/wailsapp/wails/v2 v2.3.1 10 | go.etcd.io/bbolt v1.3.6 11 | ) 12 | 13 | require ( 14 | github.com/bep/debounce v1.2.1 // indirect 15 | github.com/go-ole/go-ole v1.2.6 // indirect 16 | github.com/google/uuid v1.1.2 // indirect 17 | github.com/imdario/mergo v0.3.12 // indirect 18 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect 19 | github.com/labstack/echo/v4 v4.9.0 // indirect 20 | github.com/labstack/gommon v0.3.1 // indirect 21 | github.com/leaanthony/go-ansi-parser v1.0.1 // indirect 22 | github.com/leaanthony/gosod v1.0.3 // indirect 23 | github.com/leaanthony/slicer v1.5.0 // indirect 24 | github.com/mattn/go-colorable v0.1.11 // indirect 25 | github.com/mattn/go-isatty v0.0.14 // indirect 26 | github.com/pkg/errors v0.9.1 // indirect 27 | github.com/samber/lo v1.27.1 // indirect 28 | github.com/tkrajina/go-reflector v0.5.5 // indirect 29 | github.com/valyala/bytebufferpool v1.0.0 // indirect 30 | github.com/valyala/fasttemplate v1.2.1 // indirect 31 | github.com/wailsapp/mimetype v1.4.1 // indirect 32 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect 33 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect 34 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect 35 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect 36 | golang.org/x/text v0.3.7 // indirect 37 | ) 38 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= 2 | github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 7 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 8 | github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= 9 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 10 | github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= 11 | github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 12 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= 13 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= 14 | github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= 15 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 16 | github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY= 17 | github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= 18 | github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= 19 | github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= 20 | github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= 21 | github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= 22 | github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4= 23 | github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= 24 | github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ= 25 | github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= 26 | github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= 27 | github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= 28 | github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= 29 | github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= 30 | github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= 31 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 32 | github.com/mattn/go-ieproxy v0.0.7 h1:d2hBmNUJOAf2aGgzMQtz1wBByJQvRk72/1TXBiCVHXU= 33 | github.com/mattn/go-ieproxy v0.0.7/go.mod h1:6ZpRmhBaYuBX1U2za+9rC9iCGLsSp2tftelZne7CPko= 34 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 35 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 36 | github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc= 37 | github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= 38 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 39 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 40 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 41 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 42 | github.com/samber/lo v1.27.1 h1:sTXwkRiIFIQG+G0HeAvOEnGjqWeWtI9cg5/n51KrxPg= 43 | github.com/samber/lo v1.27.1/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg= 44 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 45 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 46 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 47 | github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= 48 | github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ= 49 | github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= 50 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 51 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 52 | github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= 53 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 54 | github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= 55 | github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= 56 | github.com/wailsapp/wails/v2 v2.3.1 h1:ZJz+pyIBKyASkgO8JO31NuHO1gTTHmvwiHYHwei1CqM= 57 | github.com/wailsapp/wails/v2 v2.3.1/go.mod h1:zlNLI0E2c2qA6miiuAHtp0Bac8FaGH0tlhA19OssR/8= 58 | go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= 59 | go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= 60 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 61 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= 62 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 63 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= 64 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= 65 | golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 66 | golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 67 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= 68 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 69 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 70 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 71 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 72 | golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 73 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 74 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 75 | golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 76 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 77 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 78 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 79 | golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 80 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= 81 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 82 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 83 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 84 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 85 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 86 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 87 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 88 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 89 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 90 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 91 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 92 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 93 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | //go:build !browser 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "embed" 8 | "log" 9 | "net/http" 10 | "os" 11 | "path" 12 | 13 | "github.com/mattn/go-ieproxy" 14 | "github.com/wailsapp/wails/v2" 15 | "github.com/wailsapp/wails/v2/pkg/options" 16 | "github.com/wailsapp/wails/v2/pkg/options/windows" 17 | "github.com/xmdhs/player-go/api" 18 | "github.com/xmdhs/player-go/cors" 19 | "go.etcd.io/bbolt" 20 | ) 21 | 22 | //go:embed frontend/dist 23 | var assets embed.FS 24 | 25 | func main() { 26 | t := http.DefaultTransport.(*http.Transport).Clone() 27 | t.Proxy = ieproxy.GetProxyFunc() 28 | 29 | db, err := bbolt.Open("player.db", 0600, nil) 30 | if err != nil { 31 | panic(err) 32 | } 33 | app := &App{ 34 | t: t, 35 | db: db, 36 | } 37 | pwd, err := os.Getwd() 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | err = wails.Run(&options.App{ 43 | Title: "player", 44 | Width: 800, 45 | Height: 600, 46 | Assets: &assets, 47 | OnStartup: app.startup, 48 | OnShutdown: app.shutdown, 49 | Bind: []interface{}{ 50 | app, 51 | }, 52 | Windows: &windows.Options{ 53 | WebviewUserDataPath: path.Join(pwd, "data"), 54 | }, 55 | }) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | } 60 | 61 | type App struct { 62 | ctx context.Context 63 | t *http.Transport 64 | db *bbolt.DB 65 | } 66 | 67 | func (b *App) startup(ctx context.Context) { 68 | b.ctx = ctx 69 | } 70 | 71 | func (b *App) shutdown(ctx context.Context) {} 72 | 73 | func (b *App) CorsServer() int { 74 | return cors.Server(b.ctx, b.t) 75 | } 76 | 77 | func (b *App) ApiServer() int { 78 | return api.Server(b.ctx, b.db, b.t) 79 | } 80 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | func GetProt() int64 { 10 | l, err := net.Listen("tcp", "127.0.0.1:0") 11 | if err != nil { 12 | panic(err) 13 | } 14 | defer l.Close() 15 | list := strings.Split(l.Addr().String(), ":") 16 | i, err := strconv.ParseInt(list[1], 10, 64) 17 | if err != nil { 18 | panic(err) 19 | } 20 | return i 21 | } 22 | -------------------------------------------------------------------------------- /wails.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "player-go", 3 | "info": { 4 | "productVersion": "0.0.1", 5 | "copyright": "xmdhs", 6 | "comments": "player for danmu" 7 | }, 8 | "outputfilename": "player" 9 | } --------------------------------------------------------------------------------