diff --git a/.dockerignore b/.dockerignore index 8579a8b..07c63ac 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,4 @@ data -config go.sum *.mp4 *.m4a diff --git a/.gitignore b/.gitignore index da744b4..77c5bf8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -config go.sum *.mp4 *.m4a diff --git a/Dockerfile b/Dockerfile index 7c79684..68c94c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ RUN wget -q -d https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_ && chmod +x /usr/local/bin/yt-dlp ADD *.go /src/. +ADD config /src/config ADD database /src/database Add ffmpeg /src/ffmpeg ADD handlers /src/handlers diff --git a/README.md b/README.md index 27b06ca..06b26d9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ go mod tidy export YTDLP_SITE_ADMIN_INITIAL_PASSWORD=abc123 export YTDLP_SITE_SESSION_AUTH_KEY=v9qpt37hc4qpmhf go run \ - -ldflags "-X main.GitSHA=$(git rev-parse HEAD) -X main.BuildDate=$(date +%Y-%m-%d)" \ + -ldflags "-X ytdlp-site/config.gitSHA=$(git rev-parse HEAD) -X ytdlp-site/config.buildDate=$(date +%Y-%m-%d)" \ *.go ``` diff --git a/config.go b/config/config.go similarity index 63% rename from config.go rename to config/config.go index 0c1679b..3875f3f 100644 --- a/config.go +++ b/config/config.go @@ -1,15 +1,16 @@ -package main +package config import ( "fmt" "os" + "path/filepath" "strings" ) -var GitSHA string -var BuildDate string +var gitSHA string +var buildDate string -func getDataDir() string { +func GetDataDir() string { value, exists := os.LookupEnv("YTDLP_SITE_DATA_DIR") if exists { return value @@ -17,15 +18,11 @@ func getDataDir() string { return "data" } -func getConfigDir() string { - value, exists := os.LookupEnv("YTDLP_SITE_CONFIG_DIR") - if exists { - return value - } - return "config" +func GetConfigDir() string { + return filepath.Join(GetDataDir(), "config") } -func getAdminInitialPassword() (string, error) { +func GetAdminInitialPassword() (string, error) { key := "YTDLP_SITE_ADMIN_INITIAL_PASSWORD" value, exists := os.LookupEnv(key) if exists { @@ -34,7 +31,7 @@ func getAdminInitialPassword() (string, error) { return "", fmt.Errorf("please set %s", key) } -func getSessionAuthKey() ([]byte, error) { +func GetSessionAuthKey() ([]byte, error) { key := "YTDLP_SITE_SESSION_AUTH_KEY" value, exists := os.LookupEnv(key) if exists { @@ -43,7 +40,7 @@ func getSessionAuthKey() ([]byte, error) { return []byte{}, fmt.Errorf("please set %s", key) } -func getSecure() bool { +func GetSecure() bool { key := "YTDLP_SITE_SECURE" if value, exists := os.LookupEnv(key); exists { lower := strings.ToLower(value) @@ -54,18 +51,18 @@ func getSecure() bool { return false } -func getGitSHA() string { - if GitSHA == "" { +func GetGitSHA() string { + if gitSHA == "" { return "" } else { - return GitSHA + return gitSHA } } -func getBuildDate() string { - if BuildDate == "" { +func GetBuildDate() string { + if buildDate == "" { return "" } else { - return BuildDate + return buildDate } } diff --git a/handlers.go b/handlers.go index 1c1fe28..dead94d 100644 --- a/handlers.go +++ b/handlers.go @@ -16,27 +16,15 @@ import ( "golang.org/x/crypto/bcrypt" "gorm.io/gorm" + "ytdlp-site/config" "ytdlp-site/ffmpeg" + "ytdlp-site/handlers" "ytdlp-site/media" "ytdlp-site/originals" "ytdlp-site/playlists" "ytdlp-site/ytdlp" ) -type Footer struct { - BuildDate string - BuildId string - BuildIdShort string -} - -func makeFooter() Footer { - return Footer{ - BuildDate: getBuildDate(), - BuildId: getGitSHA(), - BuildIdShort: getGitSHA()[0:7], - } -} - var ytdlpAudioOptions = []string{"-f", "bestvideo[height<=1080]+bestaudio/best[height<=1080]"} var ytdlpVideoOptions = []string{"-f", "bestaudio"} @@ -71,7 +59,7 @@ func homeHandler(c echo.Context) error { return c.Render(http.StatusOK, "home.html", map[string]interface{}{ - "Footer": makeFooter(), + "Footer": handlers.MakeFooter(), }) } @@ -123,7 +111,7 @@ func logoutHandler(c echo.Context) error { func downloadHandler(c echo.Context) error { return c.Render(http.StatusOK, "download.html", map[string]interface{}{ - "Footer": makeFooter(), + "Footer": handlers.MakeFooter(), }) } @@ -518,7 +506,7 @@ func processOriginal(originalID uint) { if hasOriginalVideo { - videoFilepath := filepath.Join(getDataDir(), video.Filename) + videoFilepath := filepath.Join(config.GetDataDir(), video.Filename) _, err := os.Stat(videoFilepath) if os.IsNotExist(err) { fmt.Println("Skipping non-existant file for processOriginal") @@ -540,7 +528,7 @@ func processOriginal(originalID uint) { } else if hasOriginalAudio { - audioFilepath := filepath.Join(getDataDir(), audio.Filename) + audioFilepath := filepath.Join(config.GetDataDir(), audio.Filename) _, err := os.Stat(audioFilepath) if os.IsNotExist(err) { fmt.Println("Skipping non-existant audio file for processOriginal") @@ -591,7 +579,7 @@ func startDownload(originalID uint, videoURL string, audioOnly bool) { // create temporary directory // do this in the data directory since /tmp is sometimes a different filesystem - tempDir, err := os.MkdirTemp(getDataDir(), "dl") + tempDir, err := os.MkdirTemp(config.GetDataDir(), "dl") if err != nil { log.Errorln("Error creating temporary directory:", err) originals.SetStatus(db, originalID, originals.StatusFailed) @@ -641,7 +629,7 @@ func startDownload(originalID uint, videoURL string, audioOnly bool) { // move to data directory srcPath := filepath.Join(tempDir, dlFilename) - dlFilepath := filepath.Join(getDataDir(), dlFilename) + dlFilepath := filepath.Join(config.GetDataDir(), dlFilename) log.Debugln("rename", srcPath, "->", dlFilepath) err = os.Rename(srcPath, dlFilepath) if err != nil { @@ -751,7 +739,7 @@ func videosHandler(c echo.Context) error { map[string]interface{}{ "videos": origs, "playlists": playlists, - "Footer": makeFooter(), + "Footer": handlers.MakeFooter(), }) } @@ -831,7 +819,7 @@ func videoHandler(c echo.Context) error { Order("CASE WHEN source = 'original' THEN 1 ELSE 0 END, bps ASC"). Find(&audios) - dataDir := getDataDir() + dataDir := config.GetDataDir() // create temporary URLs var videoURLs []VideoTemplate @@ -884,7 +872,7 @@ func videoHandler(c echo.Context) error { "videos": videoURLs, "audios": audioURLs, "dataDir": dataDir, - "Footer": makeFooter(), + "Footer": handlers.MakeFooter(), }) } @@ -917,7 +905,7 @@ func deleteTranscodedVideos(originalID uint) { var videos []media.Video db.Where("original_id = ?", originalID).Where("source = ?", "transcode").Find(&videos) for _, video := range videos { - path := filepath.Join(getDataDir(), video.Filename) + path := filepath.Join(config.GetDataDir(), video.Filename) log.Debugln("remove video", path) err := os.Remove(path) if err != nil { @@ -931,7 +919,7 @@ func deleteOriginalVideos(originalID uint) { var videos []media.Video db.Where("original_id = ?", originalID).Where("source = ?", "original").Find(&videos) for _, video := range videos { - path := filepath.Join(getDataDir(), video.Filename) + path := filepath.Join(config.GetDataDir(), video.Filename) fmt.Println("remove", path) err := os.Remove(path) if err != nil { @@ -945,7 +933,7 @@ func deleteAudiosWithSource(originalID uint, source string) { var audios []media.Audio db.Where("original_id = ?", originalID).Where("source = ?", source).Find(&audios) for _, audio := range audios { - path := filepath.Join(getDataDir(), audio.Filename) + path := filepath.Join(config.GetDataDir(), audio.Filename) log.Debugln("remove audio", path) err := os.Remove(path) if err != nil { @@ -987,7 +975,7 @@ func deleteVideo(id int) error { return result.Error } - videoPath := filepath.Join(getDataDir(), video.Filename) + videoPath := filepath.Join(config.GetDataDir(), video.Filename) log.Debugln("remove", videoPath) err := os.Remove(videoPath) if err != nil { @@ -1030,7 +1018,7 @@ func deleteAudioHandler(c echo.Context) error { return c.Redirect(http.StatusSeeOther, referrer) } - filePath := filepath.Join(getDataDir(), audio.Filename) + filePath := filepath.Join(config.GetDataDir(), audio.Filename) log.Debugln("remove", filePath) err := os.Remove(filePath) if err != nil { @@ -1157,7 +1145,7 @@ func playlistHandler(c echo.Context) error { "playlist": playlist, "unwatched": origs, "watched": watchedOrigs, - "Footer": makeFooter(), + "Footer": handlers.MakeFooter(), }) } diff --git a/handlers/footer.go b/handlers/footer.go new file mode 100644 index 0000000..a49ee62 --- /dev/null +++ b/handlers/footer.go @@ -0,0 +1,17 @@ +package handlers + +import "ytdlp-site/config" + +type Footer struct { + BuildDate string + BuildId string + BuildIdShort string +} + +func MakeFooter() Footer { + return Footer{ + BuildDate: config.GetBuildDate(), + BuildId: config.GetGitSHA(), + BuildIdShort: config.GetGitSHA()[0:7], + } +} diff --git a/handlers/status.go b/handlers/status.go new file mode 100644 index 0000000..179f604 --- /dev/null +++ b/handlers/status.go @@ -0,0 +1,28 @@ +package handlers + +import ( + "net/http" + + "github.com/labstack/echo/v4" + + "ytdlp-site/ffmpeg" + "ytdlp-site/ytdlp" +) + +func StatusGet(c echo.Context) error { + + ytdlpStdout, _, err := ytdlp.Run("--version") + if err != nil { + log.Errorln(err) + } + ffmpegStdout, _, err := ffmpeg.Ffmpeg("-version") + if err != nil { + log.Errorln(err) + } + + return c.Render(http.StatusOK, "status.html", map[string]interface{}{ + "ytdlp": string(ytdlpStdout), + "ffmpeg": string(ffmpegStdout), + "Footer": MakeFooter(), + }) +} diff --git a/main.go b/main.go index a60b7ac..e057a42 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "gorm.io/gorm" "gorm.io/gorm/logger" + "ytdlp-site/config" "ytdlp-site/database" "ytdlp-site/ffmpeg" "ytdlp-site/handlers" @@ -33,7 +34,7 @@ func ensureAdminAccount(db *gorm.DB) error { if err := db.Where("username = ?", "admin").First(&user).Error; err != nil { // no such user - password, err := getAdminInitialPassword() + password, err := config.GetAdminInitialPassword() if err != nil { return err } @@ -50,8 +51,8 @@ func main() { initLogger() - log.Infof("GitSHA: %s", getGitSHA()) - log.Infof("BuildDate: %s", getBuildDate()) + log.Infof("GitSHA: %s", config.GetGitSHA()) + log.Infof("BuildDate: %s", config.GetBuildDate()) ffmpeg.Init(log) handlers.Init(log) @@ -69,13 +70,13 @@ func main() { ) // Create config database - err := os.MkdirAll(getConfigDir(), 0700) + err := os.MkdirAll(config.GetConfigDir(), 0700) if err != nil { - log.Panicf("failed to create config dir %s", getConfigDir()) + log.Panicf("failed to create config dir %s", config.GetConfigDir()) } // Initialize database - dbPath := filepath.Join(getConfigDir(), "videos.db") + dbPath := filepath.Join(config.GetConfigDir(), "videos.db") db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{ Logger: gormLogger, }) @@ -106,7 +107,7 @@ func main() { } // create the cookie store - key, err := getSessionAuthKey() + key, err := config.GetSessionAuthKey() if err != nil { panic(fmt.Sprintf("%v", err)) } @@ -145,19 +146,20 @@ func main() { e.POST("/delete_audio/:id", deleteAudioHandler, authMiddleware) e.POST("/transcode_to_video/:id", transcodeToVideoHandler, authMiddleware) e.POST("/transcode_to_audio/:id", transcodeToAudioHandler, authMiddleware) + e.GET("/status", handlers.StatusGet, authMiddleware) e.GET("/p/:id", playlistHandler, authMiddleware) e.POST("/p/:id/delete", deletePlaylistHandler, authMiddleware) dataGroup := e.Group("/data") dataGroup.Use(authMiddleware) - dataGroup.Static("/", getDataDir()) + dataGroup.Static("/", config.GetDataDir()) staticGroup := e.Group("/static") staticGroup.Use(authMiddleware) staticGroup.Static("/", "static") - secure := getSecure() + secure := config.GetSecure() store.Options = &sessions.Options{ Path: "/", diff --git a/templates/status.html b/templates/status.html new file mode 100644 index 0000000..b0d8bf4 --- /dev/null +++ b/templates/status.html @@ -0,0 +1,33 @@ + + + + + + + {{.original.Title}} + + + + {{template "header-css" .}} + {{template "footer-css" .}} + + + + {{template "header" .}} +

Status

+
+

yt-dlp

+
+ {{.ytdlp}} +
+
+
+

ffmpeg

+
+ {{.ffmpeg}} +
+
+ {{template "footer" .}} + + + \ No newline at end of file diff --git a/workers.go b/workers.go index 3c08e61..e1a98f2 100644 --- a/workers.go +++ b/workers.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "time" + "ytdlp-site/config" "ytdlp-site/ffmpeg" "ytdlp-site/media" "ytdlp-site/originals" @@ -24,7 +25,7 @@ func videoToVideo(transID uint, height uint, fps float64, srcFilepath string) { // determine destination path dstFilename := uuid.Must(uuid.NewV7()).String() dstFilename = fmt.Sprintf("%s.mp4", dstFilename) - dstFilepath := filepath.Join(getDataDir(), dstFilename) + dstFilepath := filepath.Join(config.GetDataDir(), dstFilename) err := ensureDirFor(dstFilepath) if err != nil { @@ -100,7 +101,7 @@ func videoToAudio(transID uint, kbps uint, videoFilepath string) { // determine destination path audioFilename := uuid.Must(uuid.NewV7()).String() audioFilename = fmt.Sprintf("%s.mp3", audioFilename) - audioFilepath := filepath.Join(getDataDir(), audioFilename) + audioFilepath := filepath.Join(config.GetDataDir(), audioFilename) // ensure destination directory err := ensureDirFor(audioFilepath) @@ -154,7 +155,7 @@ func audioToAudio(transID uint, kbps uint, srcFilepath string) { // determine destination path dstFilename := uuid.Must(uuid.NewV7()).String() dstFilename = fmt.Sprintf("%s.mp3", dstFilename) - dstFilepath := filepath.Join(getDataDir(), dstFilename) + dstFilepath := filepath.Join(config.GetDataDir(), dstFilename) // ensure destination directory err := ensureDirFor(dstFilepath) @@ -261,7 +262,7 @@ func transcodePending() { db.Delete(&trans) continue } - srcFilepath := filepath.Join(getDataDir(), srcVideo.Filename) + srcFilepath := filepath.Join(config.GetDataDir(), srcVideo.Filename) if trans.DstKind == "video" { videoToVideo(trans.ID, trans.Height, trans.FPS, srcFilepath) @@ -280,7 +281,7 @@ func transcodePending() { db.Delete(&trans) continue } - srcFilepath := filepath.Join(getDataDir(), srcAudio.Filename) + srcFilepath := filepath.Join(config.GetDataDir(), srcAudio.Filename) audioToAudio(trans.ID, trans.Rate, srcFilepath) } else { fmt.Println("unexpected src kind for Transcode", trans)