Initial commit
This commit is contained in:
BIN
.github/cover.jpg
vendored
Normal file
BIN
.github/cover.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.DS_Store
|
||||||
|
__debug_bin
|
||||||
|
dist
|
||||||
14
.vscode/launch.json
vendored
Normal file
14
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Run App",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${workspaceFolder}/main.go",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"args": ["-port", "5173"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": ["ctrack", "osascript", "tmpl"]
|
||||||
|
}
|
||||||
2
Makefile
Normal file
2
Makefile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
build_app:
|
||||||
|
go build -ldflags="-s -w" -o ./dist/obs-spotify ./main.go
|
||||||
16
README.md
Normal file
16
README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# OBS Spotify
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## How to use?
|
||||||
|
|
||||||
|
1. Download ZIP from last release
|
||||||
|
2. Open obs-spotify binary
|
||||||
|
3. Add new "Browser" source in OBS
|
||||||
|
4. Add http://localhost:3000 url
|
||||||
|
5. Save source
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
1. `obs-spotify` accepts the port argument. Example ./obs-spotify -port=8888
|
||||||
|
2. `?refresh=5s`
|
||||||
75
index.html
Normal file
75
index.html
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Spotify</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spotify {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 16px;
|
||||||
|
|
||||||
|
/* Font */
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #e9e9e9;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cover {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
#artist {
|
||||||
|
opacity: 0.6;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="spotify">
|
||||||
|
<div class="image">
|
||||||
|
<img id="cover" src="{{ .Track.ArtworkURL }}" />
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<span id="name">{{ .Track.Name }}</span>
|
||||||
|
<div id="artist">{{ .Track.Artist }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const fetchCurrentTrack = () =>
|
||||||
|
fetch("http://localhost:{{ .Port }}/track").then((resp) => resp.json());
|
||||||
|
|
||||||
|
const artistEl = document.querySelector("#artist");
|
||||||
|
const coverEl = document.querySelector("#cover");
|
||||||
|
const nameEl = document.querySelector("#name");
|
||||||
|
|
||||||
|
const render = async () => {
|
||||||
|
const currentTrack = await fetchCurrentTrack();
|
||||||
|
|
||||||
|
if (!currentTrack || !currentTrack.name) return;
|
||||||
|
|
||||||
|
coverEl.src = currentTrack.artworkUrl;
|
||||||
|
nameEl.innerHTML = currentTrack.name;
|
||||||
|
artistEl.innerHTML = currentTrack.artist;
|
||||||
|
};
|
||||||
|
|
||||||
|
setInterval(render, {{ .Refresh }});
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
119
main.go
Normal file
119
main.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const command = `
|
||||||
|
tell application "Spotify"
|
||||||
|
set ctrack to "{"
|
||||||
|
set ctrack to ctrack & "\"artist\": \"" & current track's artist & "\""
|
||||||
|
set ctrack to ctrack & ",\"album\": \"" & current track's album & "\""
|
||||||
|
set ctrack to ctrack & ",\"discNumber\": " & current track's disc number
|
||||||
|
set ctrack to ctrack & ",\"duration\": " & current track's duration
|
||||||
|
set ctrack to ctrack & ",\"playedCount\": " & current track's played count
|
||||||
|
set ctrack to ctrack & ",\"trackNumber\": " & current track's track number
|
||||||
|
set ctrack to ctrack & ",\"popularity\": " & current track's popularity
|
||||||
|
set ctrack to ctrack & ",\"id\": \"" & current track's id & "\""
|
||||||
|
set ctrack to ctrack & ",\"position\": " & (player position as integer)
|
||||||
|
set ctrack to ctrack & ",\"name\": \"" & current track's name & "\""
|
||||||
|
set ctrack to ctrack & ",\"albumArtist\": \"" & current track's album artist & "\""
|
||||||
|
set ctrack to ctrack & ",\"artworkUrl\": \"" & current track's artwork url & "\""
|
||||||
|
set ctrack to ctrack & ",\"spotifyUrl\": \"" & current track's spotify url & "\""
|
||||||
|
set ctrack to ctrack & "}"
|
||||||
|
|
||||||
|
return ctrack
|
||||||
|
end tell`
|
||||||
|
|
||||||
|
type SpotifyCurrentTrack struct {
|
||||||
|
Artist string `json:"artist"`
|
||||||
|
Album string `json:"album"`
|
||||||
|
DiscNumber int `json:"discNumber"`
|
||||||
|
Duration int `json:"duration"`
|
||||||
|
PlayedCount int `json:"playedCount"`
|
||||||
|
TrackNumber int `json:"trackNumber"`
|
||||||
|
Popularity int `json:"popularity"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Position int `json:"position"`
|
||||||
|
AlbumArtist string `json:"albumArtist"`
|
||||||
|
ArtworkURL string `json:"artworkUrl"`
|
||||||
|
SpotifyURL string `json:"spotifyUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Track SpotifyCurrentTrack
|
||||||
|
Refresh int64
|
||||||
|
Port string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentTrack() SpotifyCurrentTrack {
|
||||||
|
currentTrack := SpotifyCurrentTrack{}
|
||||||
|
|
||||||
|
cmd := exec.Command("osascript", "-e", command)
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return currentTrack
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(output, ¤tTrack); err != nil {
|
||||||
|
return currentTrack
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentTrack
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
port := flag.String("port", "5783", "http port")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
ex, _ := os.Executable()
|
||||||
|
tmpl, err := template.ParseFiles(filepath.Join(ex, "../index.html"))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
response := Response{
|
||||||
|
Track: getCurrentTrack(),
|
||||||
|
Refresh: time.Second.Milliseconds(),
|
||||||
|
Port: *port,
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshQueryParam := req.URL.Query().Get("refresh")
|
||||||
|
|
||||||
|
if refreshQueryParam != "" {
|
||||||
|
if duration, err := time.ParseDuration(refreshQueryParam); err == nil {
|
||||||
|
response.Refresh = duration.Milliseconds()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tmpl.Execute(w, response); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
http.HandleFunc("/track", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
|
||||||
|
resp, _ := json.Marshal(getCurrentTrack())
|
||||||
|
|
||||||
|
fmt.Fprint(w, string(resp))
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Printf("The server is running on port %s!", *port)
|
||||||
|
|
||||||
|
http.ListenAndServe(fmt.Sprintf(":%s", *port), nil)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user