<commit_before>package main

import (
	"context"
	"encoding/json"
	"errors"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"time"

	"github.com/bcspragu/Radiotation/app"
	"github.com/bcspragu/Radiotation/music"
	"github.com/bcspragu/Radiotation/spotify"
	"github.com/coreos/go-oidc"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"github.com/namsral/flag"
)

type tmplData struct {
	ClientID string
	Host     string
	Room     *app.Room
	User     *app.User
}

type srv struct {
	sync.RWMutex
	db db

	tmpls *template.Template
	sc    *securecookie.SecureCookie
	h     hub
}

var (
	_             = flag.String(flag.DefaultConfigFlagname, "config", "path to config file")
	addr          = flag.String("addr", ":8000", "http service address")
	clientID      = flag.String("client_id", "", "The Google ClientID to use")
	spotifyClient = flag.String("spotify_client_id", "", "The client ID of the Spotify application")
	spotifySecret = flag.String("spotify_secret", "", "The secret of the Spotify application")

	spotifyServer  music.SongServer
	googleVerifier *oidc.IDTokenVerifier

	errNoTracks = errors.New("radiotation: no tracks in room")
)

func main() {
	rand.Seed(time.Now().Unix())
	flag.Parse()

	if *clientID == "" || *spotifyClient == "" || *spotifySecret == "" {
		log.Fatalf("Missing a required flag, all of  --client_id, --spotify_client_id, and --spotify_secret are required.")
	}

	spotifyServer = spotify.NewSongServer("spotify.<EMAIL>", *spotifyClient, *spotifySecret)

	tmpls, err := template.ParseGlob("templates/*.html")
	if err != nil {
		log.Fatalf("Can't load templates, dying: %v", err)
	}

	sc, err := loadKeys()
	if err != nil {
		log.Fatalf("Can't load or generate keys, dying: %v", err)
	}

	googleProvider, err := oidc.NewProvider(context.Background(), "https://accounts.google.com")
	if err != nil {
		log.Fatalf("Failed to get provider for Google: %v", err)
	}
	googleVerifier = googleProvider.Verifier(&oidc.Config{
		ClientID: *clientID,
	})

	//db, err := initBoltDB()
	db, err := initInMemDB()
	if err != nil {
		log.Fatalf("Failed to initialize datastore: %v", err)
	}
	f, err := os.Open("inmem.db")
	if err != nil && !os.IsNotExist(err) {
		// A legitimate error, not just 'the file wasn't found'
		log.Fatalf("Failed to open datastore file for reading: %v", err)
	}

	if err == nil {
		if err := db.Load(f); err != nil {
			f.Close()
			log.Fatalf("Failed to load datastore: %v", err)
		}
	}
	f.Close()

	s := &srv{
		db:    db,
		tmpls: tmpls,
		sc:    sc,
		h: hub{
			broadcast:   make(chan []byte),
			register:    make(chan *connection),
			unregister:  make(chan *connection),
			connections: make(map[*connection]bool),
			userconns:   make(map[*app.User]*connection),
		},
	}
	go s.h.run()

	r := mux.NewRouter()
	r.HandleFunc("/", s.serveHome).Methods("GET")
	r.HandleFunc("/verifyToken", s.serveVerifyToken)
	r.HandleFunc("/rooms", s.withLogin(s.serveCreateRoom)).Methods("POST")
	r.HandleFunc("/rooms/{id}", s.withLogin(s.serveRoom)).Methods("GET")
	r.HandleFunc("/rooms/{id}/search", s.withLogin(s.serveSearch)).Methods("GET")
	r.HandleFunc("/rooms/{id}/queue", s.withLogin(s.serveQueue)).Methods("GET")
	r.HandleFunc("/rooms/{id}/now", s.withLogin(s.serveNowPlaying)).Methods("GET")
	r.HandleFunc("/rooms/{id}/add", s.withLogin(s.addToQueue)).Methods("POST")
	r.HandleFunc("/rooms/{id}/remove", s.withLogin(s.removeFromQueue)).Methods("POST")
	r.HandleFunc("/rooms/{id}/pop", s.serveSong).Methods("GET")
	r.HandleFunc("/ws", s.withLogin(s.serveData))

	http.Handle("/", r)

	if err := servePaths(); err != nil {
		log.Fatalf("Can't serve static assets: %v", err)
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		f, err := os.Create("inmem.db")
		if err != nil && !os.IsExist(err) {
			// A legitimate error, not just 'the file was found'
			log.Fatalf("Failed to open datastore file for writing: %v", err)
		}
		if err := s.db.Save(f); err != nil {
			log.Fatalf("Failed to save datastore: %v", err)
		}
		if err := f.Close(); err != nil {
			log.Fatalf("Failed to close datastore file: %v", err)
		}
		os.Exit(1)
	}()

	err = http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func (s *srv) serveHome(w http.ResponseWriter, r *http.Request) {
	if err := s.tmpls.ExecuteTemplate(w, "index.html", s.data(r)); err != nil {
		serveError(w, err)
	}
}

type QueueResponse struct {
	Error   bool
	Message string<commit_msg>Blah blah acbuild mount points blah blah<commit_after>package main

import (
	"context"
	"encoding/json"
	"errors"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"time"

	"github.com/bcspragu/Radiotation/app"
	"github.com/bcspragu/Radiotation/music"
	"github.com/bcspragu/Radiotation/spotify"
	"github.com/coreos/go-oidc"
	"github.com/gorilla/mux"
	"github.com/gorilla/securecookie"
	"github.com/namsral/flag"
)

type tmplData struct {
	ClientID string
	Host     string
	Room     *app.Room
	User     *app.User
}

type srv struct {
	sync.RWMutex
	db db

	tmpls *template.Template
	sc    *securecookie.SecureCookie
	h     hub
}

var (
	_             = flag.String(flag.DefaultConfigFlagname, "config", "path to config file")
	addr          = flag.String("addr", ":8000", "http service address")
	clientID      = flag.String("client_id", "", "The Google ClientID to use")
	spotifyClient = flag.String("spotify_client_id", "", "The client ID of the Spotify application")
	spotifySecret = flag.String("spotify_secret", "", "The secret of the Spotify application")

	spotifyServer  music.SongServer
	googleVerifier *oidc.IDTokenVerifier

	errNoTracks = errors.New("radiotation: no tracks in room")
)

func main() {
	rand.Seed(time.Now().Unix())
	flag.Parse()

	if *clientID == "" || *spotifyClient == "" || *spotifySecret == "" {
		log.Fatalf("Missing a required flag, all of  --client_id, --spotify_client_id, and --spotify_secret are required.")
	}

	spotifyServer = spotify.NewSongServer("spotify.com", *spotifyClient, *spotifySecret)

	tmpls, err := template.ParseGlob("templates/*.html")
	if err != nil {
		log.Fatalf("Can't load templates, dying: %v", err)
	}

	sc, err := loadKeys()
	if err != nil {
		log.Fatalf("Can't load or generate keys, dying: %v", err)
	}

	googleProvider, err := oidc.NewProvider(context.Background(), "https://accounts.google.com")
	if err != nil {
		log.Fatalf("Failed to get provider for Google: %v", err)
	}
	googleVerifier = googleProvider.Verifier(&oidc.Config{
		ClientID: *clientID,
	})

	//db, err := initBoltDB()
	db, err := initInMemDB()
	if err != nil {
		log.Fatalf("Failed to initialize datastore: %v", err)
	}
	f, err := os.Open("inmemdb")
	if err != nil && !os.IsNotExist(err) {
		// A legitimate error, not just 'the file wasn't found'
		log.Fatalf("Failed to open datastore file for reading: %v", err)
	}

	if err == nil {
		if err := db.Load(f); err != nil {
			f.Close()
			log.Fatalf("Failed to load datastore: %v", err)
		}
	}
	f.Close()

	s := &srv{
		db:    db,
		tmpls: tmpls,
		sc:    sc,
		h: hub{
			broadcast:   make(chan []byte),
			register:    make(chan *connection),
			unregister:  make(chan *connection),
			connections: make(map[*connection]bool),
			userconns:   make(map[*app.User]*connection),
		},
	}
	go s.h.run()

	r := mux.NewRouter()
	r.HandleFunc("/", s.serveHome).Methods("GET")
	r.HandleFunc("/verifyToken", s.serveVerifyToken)
	r.HandleFunc("/rooms", s.withLogin(s.serveCreateRoom)).Methods("POST")
	r.HandleFunc("/rooms/{id}", s.withLogin(s.serveRoom)).Methods("GET")
	r.HandleFunc("/rooms/{id}/search", s.withLogin(s.serveSearch)).Methods("GET")
	r.HandleFunc("/rooms/{id}/queue", s.withLogin(s.serveQueue)).Methods("GET")
	r.HandleFunc("/rooms/{id}/now", s.withLogin(s.serveNowPlaying)).Methods("GET")
	r.HandleFunc("/rooms/{id}/add", s.withLogin(s.addToQueue)).Methods("POST")
	r.HandleFunc("/rooms/{id}/remove", s.withLogin(s.removeFromQueue)).Methods("POST")
	r.HandleFunc("/rooms/{id}/pop", s.serveSong).Methods("GET")
	r.HandleFunc("/ws", s.withLogin(s.serveData))

	http.Handle("/", r)

	if err := servePaths(); err != nil {
		log.Fatalf("Can't serve static assets: %v", err)
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		f, err := os.Create("inmemdb")
		if err != nil && !os.IsExist(err) {
			// A legitimate error, not just 'the file was found'
			log.Fatalf("Failed to open datastore file for writing: %v", err)
		}
		if err := s.db.Save(f); err != nil {
			log.Fatalf("Failed to save datastore: %v", err)
		}
		if err := f.Close(); err != nil {
			log.Fatalf("Failed to close datastore file: %v", err)
		}
		os.Exit(1)
	}()

	err = http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func (s *srv) serveHome(w http.ResponseWriter, r *http.Request) {
	if err := s.tmpls.ExecuteTemplate(w, "index.html", s.data(r)); err != nil {
		serveError(w, err)
	}
}

type QueueResponse struct {
	Error   bool
	Message string
{'fraction_non_alphanumeric': 0.17672557519173057, 'fraction_numerical': 0.001333777925975325, 'mean_word_length': 3.9358200767964893, 'pattern_counts': {'":': 2, '<': 6, '<?xml version=': 0, '>': 4, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3640896', 'n_tokens_mistral': 3489, 'n_tokens_neox': 3343, 'n_words': 739}