├── go.mod ├── go.sum ├── vlc-protocol.desktop ├── .gitignore ├── pkg └── path │ ├── path_unix.go │ └── path_windows.go ├── .github └── workflows │ └── release.yml ├── .goreleaser.yml ├── protocol-deregister.bat ├── protocol-register.bat ├── LICENSE ├── README.md └── main.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tgdrive/player-protocol 2 | 3 | go 1.22.0 4 | 5 | require golang.org/x/sys v0.24.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= 2 | golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 3 | -------------------------------------------------------------------------------- /vlc-protocol.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Name=VLC protocol 4 | Comment=Open vlc:// links with VLC. 5 | Exec=player-protocol %u 6 | TryExec=player-protocol 7 | Icon=vlc 8 | Terminal=false 9 | Type=Application 10 | MimeType=x-scheme-handler/vlc; 11 | NoDisplay=true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | go.work.sum 23 | 24 | # env file 25 | .env 26 | 27 | -------------------------------------------------------------------------------- /pkg/path/path_unix.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package path 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func FindPlayerPath(player string) (string, error) { 12 | if player == "potplayer" { 13 | return "", fmt.Errorf("PotPlayer is not supported on Linux") 14 | } 15 | 16 | commonPaths := []string{ 17 | filepath.Join("/usr/bin", player), 18 | filepath.Join("/usr/local/bin", player), 19 | filepath.Join(os.Getenv("HOME"), "bin", player), 20 | } 21 | 22 | for _, path := range commonPaths { 23 | if _, err := os.Stat(path); err == nil { 24 | return path, nil 25 | } 26 | } 27 | 28 | return "", fmt.Errorf("%s not found", player) 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Set up Go 18 | uses: actions/setup-go@v5 19 | with: 20 | go-version: stable 21 | 22 | - name: Run GoReleaser 23 | uses: goreleaser/goreleaser-action@v6 24 | with: 25 | distribution: goreleaser 26 | version: latest 27 | args: release --clean 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | project_name: player-protocol 3 | builds: 4 | - env: 5 | - CGO_ENABLED=0 6 | main: main.go 7 | flags: -trimpath 8 | ldflags: 9 | - -extldflags=-static 10 | - -H=windowsgui 11 | - -s -w 12 | mod_timestamp: "{{ .CommitTimestamp }}" 13 | goos: 14 | - linux 15 | - windows 16 | goarch: 17 | - amd64 18 | - arm64 19 | 20 | checksum: 21 | name_template: "{{ .ProjectName }}_checksums.txt" 22 | 23 | archives: 24 | - name_template: "{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}" 25 | format: zip 26 | files: 27 | - protocol-register.bat 28 | - protocol-deregister.bat 29 | 30 | changelog: 31 | sort: asc 32 | filters: 33 | exclude: 34 | - '^docs:' 35 | - '^test:' 36 | - '^ci:' 37 | - '^README' 38 | - '^Update' 39 | - Merge pull request 40 | - Merge branch 41 | -------------------------------------------------------------------------------- /protocol-deregister.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | @echo. 3 | 4 | rem Check for administrative privileges 5 | net session >nul 2>&1 6 | if %errorLevel% neq 0 ( 7 | echo This script requires administrative privileges. 8 | echo Please run it as an administrator. 9 | pause 10 | exit /b 11 | ) 12 | 13 | echo Deregistering vlc:// and potplayer:// protocols... 14 | @echo. 15 | 16 | rem Remove VLC protocol registration 17 | reg delete HKCR\vlc /f 18 | if %errorLevel% equ 0 ( 19 | echo VLC protocol deregistered successfully. 20 | ) else ( 21 | echo Failed to deregister VLC protocol. It may not have been registered. 22 | ) 23 | 24 | @echo. 25 | 26 | rem Remove PotPlayer protocol registration 27 | reg delete HKCR\potplayer /f 28 | if %errorLevel% equ 0 ( 29 | echo PotPlayer protocol deregistered successfully. 30 | ) else ( 31 | echo Failed to deregister PotPlayer protocol. It may not have been registered. 32 | ) 33 | 34 | @echo. 35 | echo Protocol deregistration process completed. 36 | pause -------------------------------------------------------------------------------- /protocol-register.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | @echo. 3 | if not exist "%~dp0player-protocol.exe" ( 4 | echo Warning: Can't find player-protocol.exe. 5 | echo Did you compile it successfully? 6 | @echo. 7 | pause 8 | exit /b 9 | ) 10 | 11 | echo If you see "ERROR: Access is denied." then you need to right click and use "Run as Administrator". 12 | @echo. 13 | echo Associating vlc:// and potplayer:// with player-protocol.exe... 14 | 15 | rem Register VLC protocol 16 | reg add HKCR\vlc /ve /t REG_SZ /d "URL:VLC Protocol" /f 17 | reg add HKCR\vlc /v "URL Protocol" /t REG_SZ /d "" /f 18 | reg add HKCR\vlc\shell\open\command /ve /t REG_SZ /d "\"%~dp0player-protocol.exe\" %%1" /f 19 | 20 | rem Register PotPlayer protocol 21 | reg add HKCR\potplayer /ve /t REG_SZ /d "URL:PotPlayer Protocol" /f 22 | reg add HKCR\potplayer /v "URL Protocol" /t REG_SZ /d "" /f 23 | reg add HKCR\potplayer\shell\open\command /ve /t REG_SZ /d "\"%~dp0player-protocol.exe\" %%1" /f 24 | 25 | @echo. 26 | echo Protocols registered successfully. 27 | pause -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Teldrive 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-protocol 2 | 3 | **Open VLC and Pot Player directly from browser.Support ``file://`` ``https://`` ``http://`` protocols.You can't open local file directly from browser in any of these players so player-protocol enables this through file:// protocol.** 4 | 5 | ## Setup 6 | 7 | ### Install on Windows 8 | 9 | - Download the latest release from [here](https://github.com/tgdrive/player-protocol/releases/latest). 10 | - Extract the zip file. 11 | - Run ``protocol-register.bat`` as administrator. 12 | 13 | **Uninstall:** 14 | - Run ``protocol-deregister.bat`` as administrator. 15 | 16 | ### Install on Linux 17 | 18 | ```bash 19 | curl -sL instl.vercel.app/tgdrive/player-protocol | bash 20 | curl -LO https://raw.githubusercontent.com/tgdrive/player-protocol/main/vlc-protocol.desktop 21 | xdg-desktop-menu install vlc-protocol.desktop 22 | rm vlc-protocol.desktop 23 | ``` 24 | **Uninstall:** 25 | ```bash 26 | xdg-desktop-menu uninstall vlc-protocol.desktop 27 | sudo rm /usr/local/bin/player-protocol 28 | ``` 29 | ## Usage 30 | 31 | ``` 32 | vlc://file:///path/to/local/file #linux 33 | 34 | vlc://file://X:/path/to/local/file # windows 35 | 36 | vlc://https://ia804605.us.archive.org/22/items/big-buck-bunny-4k/Big_Buck_Bunny_4K.mp4 37 | 38 | potplayer://https://www.youtube.com/watch?v=dQw4w9WgXcQ 39 | 40 | ``` 41 | **For PotPlayer use `potplayer://` instead of `vlc://`** 42 | -------------------------------------------------------------------------------- /pkg/path/path_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package path 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | 10 | "golang.org/x/sys/windows/registry" 11 | ) 12 | 13 | func FindPlayerPath(player string) (string, error) { 14 | path, err := findPathInRegistry(player) 15 | if err == nil { 16 | return path, nil 17 | } 18 | commonPaths := []string{ 19 | filepath.Join(os.Getenv("ProgramFiles"), player, fmt.Sprintf("%s.exe", player)), 20 | filepath.Join(os.Getenv("ProgramFiles(x86)"), player, fmt.Sprintf("%s.exe", player)), 21 | filepath.Join(os.Getenv("LocalAppData"), "Programs", player, fmt.Sprintf("%s.exe", player)), 22 | } 23 | 24 | if player == "potplayer" { 25 | commonPaths = append(commonPaths, 26 | filepath.Join(os.Getenv("ProgramFiles"), "DAUM", "PotPlayer", "PotPlayerMini64.exe"), 27 | filepath.Join(os.Getenv("ProgramFiles(x86)"), "DAUM", "PotPlayer", "PotPlayerMini.exe"), 28 | ) 29 | } 30 | 31 | for _, path := range commonPaths { 32 | if _, err := os.Stat(path); err == nil { 33 | return path, nil 34 | } 35 | } 36 | 37 | return "", fmt.Errorf("%s not found", player) 38 | } 39 | 40 | func findPathInRegistry(player string) (string, error) { 41 | var key string 42 | var valueName string 43 | 44 | switch player { 45 | case "vlc": 46 | key = `SOFTWARE\VideoLAN\VLC` 47 | valueName = "InstallDir" 48 | case "potplayer": 49 | key = `SOFTWARE\DAUM\PotPlayer64` 50 | valueName = "ProgramPath" 51 | default: 52 | return "", fmt.Errorf("unsupported player: %s", player) 53 | } 54 | 55 | k, err := registry.OpenKey(registry.LOCAL_MACHINE, key, registry.QUERY_VALUE) 56 | if err != nil { 57 | return "", err 58 | } 59 | defer k.Close() 60 | 61 | path, _, err := k.GetStringValue(valueName) 62 | if err != nil { 63 | return "", err 64 | } 65 | 66 | if player == "vlc" { 67 | path = filepath.Join(path, "vlc.exe") 68 | } 69 | 70 | return path, nil 71 | } 72 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "os" 7 | "os/exec" 8 | "strings" 9 | 10 | player_path "github.com/tgdrive/player-protocol/pkg/path" 11 | ) 12 | 13 | func main() { 14 | if len(os.Args) != 2 { 15 | fmt.Println("Usage: program ") 16 | os.Exit(1) 17 | } 18 | 19 | arg := os.Args[1] 20 | 21 | var player, protocol string 22 | if strings.HasPrefix(arg, "vlc://") { 23 | player = "vlc" 24 | protocol = "vlc://" 25 | } else if isWindows() && strings.HasPrefix(arg, "potplayer://") { 26 | player = "potplayer" 27 | protocol = "potplayer://" 28 | } else { 29 | fmt.Println("Invalid protocol. Use vlc:// or potplayer:// (PotPlayer is only supported on Windows)") 30 | os.Exit(1) 31 | } 32 | 33 | link := strings.TrimPrefix(arg, protocol) 34 | 35 | if !strings.HasPrefix(link, "http://") && !strings.HasPrefix(link, "https://") && !strings.HasPrefix(link, "file://") { 36 | fmt.Println("Invalid link. Must start with http://, https://, or file://") 37 | os.Exit(1) 38 | } 39 | 40 | isFileProtocol := strings.HasPrefix(link, "file://") 41 | if isFileProtocol { 42 | link = strings.TrimPrefix(link, "file://") 43 | link = strings.TrimPrefix(link, "/") 44 | if isWindows() { 45 | link = strings.Replace(link, "/", "\\", -1) 46 | } 47 | link, _ = url.QueryUnescape(link) 48 | } 49 | 50 | playerPath, err := player_path.FindPlayerPath(player) 51 | if err != nil { 52 | fmt.Printf("Error finding %s path: %v\n", player, err) 53 | os.Exit(1) 54 | } 55 | 56 | var cmd *exec.Cmd 57 | switch player { 58 | case "vlc": 59 | if isFileProtocol { 60 | cmd = exec.Command(playerPath, link) 61 | } else { 62 | cmd = exec.Command(playerPath, "--open", link) 63 | } 64 | case "potplayer": 65 | cmd = exec.Command(playerPath, link) 66 | } 67 | 68 | err = cmd.Start() 69 | if err != nil { 70 | fmt.Println("Error starting player:", err) 71 | os.Exit(1) 72 | } 73 | } 74 | 75 | func isWindows() bool { 76 | return os.PathSeparator == '\\' 77 | } 78 | --------------------------------------------------------------------------------