Skip to content

Commit

Permalink
Handle embedded static directory properly and fix emotes directory
Browse files Browse the repository at this point in the history
  • Loading branch information
joeyak committed Mar 26, 2022
1 parent 37fbc79 commit 9d3897b
Show file tree
Hide file tree
Showing 48 changed files with 109 additions and 104 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ subscribers.json
/.settings/
/.project

# test alt static folder
testStatic
# Emotes dir
emotes
6 changes: 3 additions & 3 deletions common/emotes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package common

import (
"fmt"
"path/filepath"
"path"
"regexp"
"strings"
)
Expand All @@ -28,8 +28,8 @@ func NewEmotesMap() EmotesMap {
func (em EmotesMap) Add(fullpath string) EmotesMap {
fullpath = reStripStatic.ReplaceAllLiteralString(fullpath, "")

base := filepath.Base(fullpath)
code := base[0 : len(base)-len(filepath.Ext(base))]
base := path.Base(fullpath)
code := base[0 : len(base)-len(path.Ext(base))]

_, exists := em[code]

Expand Down
7 changes: 3 additions & 4 deletions common/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ package common
import (
"fmt"
html "html/template"
"io/fs"
"net/http"

fs "github.com/zorchenhimer/MovieNight/files"
)

// Holds the server's templates
var serverTemplates map[string]*html.Template

// Called from the server
func InitTemplates() error {
func InitTemplates(filesystem fs.FS) error {
serverTemplates = make(map[string]*html.Template)

// keys and files to load for that template
Expand All @@ -25,7 +24,7 @@ func InitTemplates() error {

// Parse server templates
for key, files := range serverTemplateDefs {
t, err := html.ParseFS(fs.StaticFS, files...)
t, err := html.ParseFS(filesystem, files...)
if err != nil {
return fmt.Errorf("unable to parse templates for %s: %w", key, err)
}
Expand Down
11 changes: 0 additions & 11 deletions common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ package common

import (
"net/http"
"os"
"path/filepath"
"regexp"
)

Expand All @@ -20,15 +18,6 @@ func IsValidName(name string) bool {
usernameRegex.MatchString(name)
}

// Return the absolut directory containing the MovieNight binary
func RunPath() string {
ex, er := os.Executable()
if er != nil {
panic(er)
}
return filepath.Dir(ex)
}

func Substr(input string, start int, length int) string {
asRunes := []rune(input)

Expand Down
48 changes: 18 additions & 30 deletions emotes.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import (
"strings"

"github.com/zorchenhimer/MovieNight/common"
"github.com/zorchenhimer/MovieNight/files"
)

func loadEmotes() error {
newEmotes, err := processEmoteDir(path.Join(common.RunPath(), "emotes"))
newEmotes, err := processEmoteDir(path.Join(files.RunPath(), "emotes"))
if err != nil {
return err
}
Expand All @@ -21,10 +22,10 @@ func loadEmotes() error {
return nil
}

func processEmoteDir(path string) (common.EmotesMap, error) {
dirInfo, err := os.ReadDir(path)
func processEmoteDir(dir string) (common.EmotesMap, error) {
dirInfo, err := os.ReadDir(dir)
if err != nil {
common.LogErrorf("could not open emoteDir: %v\n", err)
common.LogErrorf("could not open emote dir: %v\n", err)
}

subDirs := []string{}
Expand All @@ -39,21 +40,21 @@ func processEmoteDir(path string) (common.EmotesMap, error) {

em := common.NewEmotesMap()
// Find top level emotes
em, err = findEmotes(path, em)
em, err = findEmotes(dir, em)
if err != nil {
return nil, fmt.Errorf("could not findEmotes() in top level directory: %w", err)
}

// Get second level subdirs (eg, "twitch", "zorchenhimer", etc)
for _, dir := range subDirs {
subd, err := os.ReadDir(filepath.Join(path, dir))
for _, subDir := range subDirs {
subd, err := os.ReadDir(path.Join(dir, subDir))
if err != nil {
common.LogErrorf("Error reading dir %q: %v\n", subd, err)
continue
}
for _, d := range subd {
if d.IsDir() {
p := filepath.Join(path, dir, d.Name())
p := path.Join(dir, subDir, d.Name())
em, err = findEmotes(p, em)
if err != nil {
common.LogErrorf("Error finding emotes in %q: %v\n", p, err)
Expand All @@ -67,31 +68,18 @@ func processEmoteDir(path string) (common.EmotesMap, error) {
}

func findEmotes(dir string, em common.EmotesMap) (common.EmotesMap, error) {
var runPathLength = len(common.RunPath() + "/emotes/")

common.LogDebugf("finding emotes in %q\n", dir)
emotePNGs, err := filepath.Glob(filepath.Join(dir, "*.png"))
if err != nil {
return em, fmt.Errorf("unable to glob emote directory: %w", err)
}
common.LogInfof("Found %d emotePNGs\n", len(emotePNGs))

emoteGIFs, err := filepath.Glob(filepath.Join(dir, "*.gif"))
if err != nil {
return em, fmt.Errorf("unable to glob emote directory: %w", err)
}
common.LogInfof("Found %d emoteGIFs\n", len(emoteGIFs))

for _, file := range emotePNGs {
png := strings.ReplaceAll(common.Substr(file, runPathLength, len(file)), "\\", "/")
//common.LogDebugf("Emote PNG: %s", png)
em = em.Add(png)
}
for _, ext := range []string{"*.png", "*.gif"} {
files, err := filepath.Glob(path.Join(dir, ext))
if err != nil {
return nil, fmt.Errorf("unable to glob emote directory with %q: %w", ext, err)
}
common.LogInfof("Found %d %s emotes\n", len(files), ext)

for _, file := range emoteGIFs {
gif := strings.ReplaceAll(common.Substr(file, runPathLength, len(file)), "\\", "/")
//common.LogDebugf("Emote GIF: %s", gif)
em = em.Add(gif)
for _, file := range files {
em = em.Add(path.Join("emotes", strings.TrimPrefix(filepath.ToSlash(file), dir)))
}
}

return em, nil
Expand Down
56 changes: 53 additions & 3 deletions files/main.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,58 @@
package files

import (
"embed"
"fmt"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
)

//go:embed static/*.html static/css static/img static/js
var StaticFS embed.FS
type ioFS struct {
diskDir string
replaceKey string
fsys fs.FS
}

func (f ioFS) Open(name string) (fs.File, error) {
diskPath := path.Join(f.diskDir, strings.TrimPrefix(name, f.replaceKey))
if file, err := os.Open(diskPath); err == nil {
return file, nil
}

file, err := f.fsys.Open(name)
if err != nil {
return nil, fmt.Errorf("could not find file on disk in %q or in embedded FS: %w", f.diskDir, err)
}

return file, nil
}

func FS(fsys fs.FS, diskDir, replaceKey string) (fs.FS, error) {
if fsys == nil {
return nil, fmt.Errorf("fsys is null")
}

if diskDir == "" {
// replaceKey is cleared because there is no diskDir to replace with the base dir with
replaceKey = ""
diskDir = RunPath()
}

return ioFS{
diskDir: diskDir,
replaceKey: replaceKey,
fsys: fsys,
}, nil
}

// Return the absolut directory containing the MovieNight binary
func RunPath() string {
ex, er := os.Executable()
if er != nil {
panic(er)
}
dir := filepath.ToSlash(filepath.Dir(ex))
return strings.TrimPrefix(dir, filepath.VolumeName(dir))
}
3 changes: 1 addition & 2 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"encoding/json"
"io"
"net/http"
"path"
"strings"
"sync"

Expand Down Expand Up @@ -43,7 +42,7 @@ func (w writeFlusher) Flush() error {
}

func wsEmotes(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, path.Join("static/", r.URL.Path))
http.ServeFile(w, r, strings.TrimPrefix(r.URL.Path, "/"))
}

// Handling the websocket
Expand Down
15 changes: 11 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import (
"github.com/zorchenhimer/MovieNight/files"
)

//go:embed static/*.html static/css static/img static/js
var staticFS embed.FS

var stats = newStreamStats()

func setupSettings(adminPass string, confFile string) error {
Expand Down Expand Up @@ -46,14 +49,13 @@ func setupSettings(adminPass string, confFile string) error {
return nil
}

var staticFs embed.FS

type args struct {
Addr string `arg:"-l,--addr" help:"host:port of the HTTP server"`
RtmpAddr string `arg:"-r,--rtmp" help:"host:port of the RTMP server"`
StreamKey string `arg:"-k,--key" help:"Stream key, to protect your stream"`
AdminPass string `arg:"-a,--admin" help:"Set admin password. Overrides configuration in settings.json. This will not write the password to settings.json."`
ConfigFile string `arg:"-f,--config" default:"./settings.json" help:"URI of the conf file"`
StaticDir string `arg:"-s,--static" help:"Directory to read static files from by default"`
}

func main() {
Expand All @@ -66,13 +68,18 @@ func run(args args) {
var err error
start := time.Now()

staticFsys, err := files.FS(staticFS, args.StaticDir, "static")
if err != nil {
log.Fatalf("Error creating static FS: %v\n", err)
}

format.RegisterAll()

if err := setupSettings(args.AdminPass, args.ConfigFile); err != nil {
log.Fatalf("Error loading settings: %v\n", err)
}

if err := common.InitTemplates(); err != nil {
if err := common.InitTemplates(staticFsys); err != nil {
common.LogErrorln(err)
os.Exit(1)
}
Expand Down Expand Up @@ -117,7 +124,7 @@ func run(args args) {
router := http.NewServeMux()

router.HandleFunc("/ws", wsHandler) // Chat websocket
router.Handle("/static/", http.FileServer(http.FS(files.StaticFS)))
router.Handle("/static/", http.FileServer(http.FS(staticFsys)))
router.HandleFunc("/emotes/", wsEmotes)
router.HandleFunc("/chat", handleIndexTemplate)
router.HandleFunc("/video", handleIndexTemplate)
Expand Down
61 changes: 18 additions & 43 deletions settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,24 @@ type Settings struct {
cmdLineKey string // stream key from the command line

// Saved settings
AdminPassword string
ApprovedEmotes []string // list of channels that have been approved for emote use. Global emotes are always "approved".
Bans []BanInfo
LetThemLurk bool // whether or not to announce users joining/leaving chat
ListenAddress string
LogFile string
LogLevel common.LogLevel
MaxMessageCount int
NewPin bool // Auto generate a new pin on start. Overwrites RoomAccessPin if set.
PageTitle string // primary value for the page <title> element
RegenAdminPass bool // regenerate admin password on start?
RoomAccess AccessMode
RoomAccessPin string // The current pin
RtmpListenAddress string // host:port that the RTMP server listens on
SessionKey string // key for session data
StreamKey string
StreamStats bool
TitleLength int // maximum length of the title that can be set with the /playing
TwitchClientID string // client id from twitch developers portal
TwitchClientSecret string // OAuth from twitch developers portal: https://dev.twitch.tv/docs/authentication/getting-tokens-oauth#oauth-client-credentials-flow
WrappedEmotesOnly bool // only allow "wrapped" emotes. eg :Kappa: and [Kappa] but not Kappa
AdminPassword string
Bans []BanInfo
LetThemLurk bool // whether or not to announce users joining/leaving chat
ListenAddress string
LogFile string
LogLevel common.LogLevel
MaxMessageCount int
NewPin bool // Auto generate a new pin on start. Overwrites RoomAccessPin if set.
PageTitle string // primary value for the page <title> element
RegenAdminPass bool // regenerate admin password on start?
RoomAccess AccessMode
RoomAccessPin string // The current pin
RtmpListenAddress string // host:port that the RTMP server listens on
SessionKey string // key for session data
StreamKey string
StreamStats bool
TitleLength int // maximum length of the title that can be set with the /playing
WrappedEmotesOnly bool // only allow "wrapped" emotes. eg :Kappa: and [Kappa] but not Kappa

// Rate limiting stuff, in seconds
RateLimitChat time.Duration
Expand Down Expand Up @@ -314,25 +311,3 @@ func (s *Settings) generateNewPin() (string, error) {
}
return s.RoomAccessPin, nil
}

func (s *Settings) AddApprovedEmotes(channels []string) error {
defer s.lock.Unlock()
s.lock.Lock()

approved := map[string]int{}
for _, e := range s.ApprovedEmotes {
approved[e] = 1
}

for _, name := range channels {
approved[name] = 1
}

filtered := []string{}
for key := range approved {
filtered = append(filtered, key)
}

s.ApprovedEmotes = filtered
return s.unlockedSave()
}
2 changes: 0 additions & 2 deletions settings_example.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,5 @@
"RtmpListenAddress": ":1935",
"StreamKey": "ALongStreamKey",
"TitleLength": 50,
"TwitchClientID": "",
"TwitchClientSecret": "",
"WrappedEmotesOnly": false
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 9d3897b

Please sign in to comment.