Initial commit
This commit is contained in:
parent
40c9af083e
commit
fae249faae
|
@ -3,11 +3,13 @@
|
|||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.bin
|
||||
*.dll
|
||||
*.dylib
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.manifest
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.0.2
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"thresshold": "1500",
|
||||
"ffmpegbin": "/usr/bin/ffmpeg",
|
||||
"templates": [
|
||||
{ "mask": "Asia BJ, {title}.mp4", "hint": "master.json" }
|
||||
,{ "mask": "Mr. Video, {title}.mp4", "hint": "master.json" }
|
||||
,{ "mask": "BissFlix, {title}.mp4", "hint": "rendition.m3u8" }
|
||||
,{ "mask": "Kat, {title}.mp4", "hint": "rendition.m3u8" }
|
||||
,{ "mask": "Millie, {title}.mp4", "hint": "rendition.m3u8" }
|
||||
,{ "mask": "Run To The Movies, {title}.mp4", "hint": "rendition.m3u8" }
|
||||
]
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2024 Brian Newman. All rights reserved.
|
||||
|
||||
// Package handlers ...
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"go.cdmnky.io/v2/net/session"
|
||||
)
|
||||
|
||||
func IsAuthorized(w http.ResponseWriter, r *http.Request, session *session.Manager) string {
|
||||
|
||||
authorized := "no"
|
||||
// s := session.SessionStart(w, r)
|
||||
// if s.Get("authorized") != nil {
|
||||
// authorized = s.Get("authorized").(string)
|
||||
// }
|
||||
|
||||
return authorized
|
||||
}
|
||||
|
||||
func IsMobile(userAgent string) bool {
|
||||
return (strings.Contains(strings.ToLower(userAgent), "android") ||
|
||||
strings.Contains(strings.ToLower(userAgent), "iphone"))
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2024 Brian Newman. All rights reserved.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"cdmnky.io/net/patreon-ui/local.pkg/src/response"
|
||||
"cdmnky.io/net/patreon-ui/local.pkg/src/sitevars"
|
||||
|
||||
"go.cdmnky.io/v2/net/router"
|
||||
"go.cdmnky.io/v2/net/session"
|
||||
)
|
||||
|
||||
// Unauthorized ...
|
||||
func Unauthorized(w http.ResponseWriter) {
|
||||
resp := response.New(fmt.Sprintf("%v", http.StatusUnauthorized), "Unauthorized", []byte{})
|
||||
data, _ := resp.ToJSON()
|
||||
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
// Public ...
|
||||
func Public(h router.Handle, db *sql.DB, s *session.Manager) router.Handle {
|
||||
return router.Handle(func(w http.ResponseWriter, r *http.Request, p url.Values) {
|
||||
t1 := time.Now()
|
||||
|
||||
// session := s.SessionStart(w, r)
|
||||
// if session.Get("token") != nil {
|
||||
// token := session.Get("token").(string)
|
||||
// obj := user.New()
|
||||
// obj.Token = token
|
||||
// cnt, _ := obj.Db(db).Where(obj).Debug(false).Count()
|
||||
// if cnt == 0 {
|
||||
// s.SessionDestroy(w, r)
|
||||
// }
|
||||
// }
|
||||
|
||||
h(w, r, p)
|
||||
|
||||
t2 := time.Now()
|
||||
|
||||
var realIP = r.Header.Get("X-Real-IP")
|
||||
if len(realIP) == 0 {
|
||||
realIP = r.RemoteAddr
|
||||
}
|
||||
var userAgent = r.Header.Get("User-Agent")
|
||||
var referrer = r.Header.Get("Referer")
|
||||
now := time.Now()
|
||||
mask := "2006/01/02 15:04:05"
|
||||
fmt.Printf("%s|%s|%s|%s|%s|%s|%v\n", now.Format(mask), realIP, r.Method, r.URL.String(), userAgent, referrer, t2.Sub(t1))
|
||||
})
|
||||
}
|
||||
|
||||
// Secured ...
|
||||
func Secured(h router.Handle, db *sql.DB, s *session.Manager, sitevars *sitevars.Sitevars) router.Handle {
|
||||
return router.Handle(func(w http.ResponseWriter, r *http.Request, p url.Values) {
|
||||
|
||||
authorized := false
|
||||
|
||||
// session := s.SessionStart(w, r)
|
||||
// if session.Get("token") != nil {
|
||||
// token := session.Get("token").(string)
|
||||
// obj := user.New()
|
||||
// obj.Token = token
|
||||
// cnt, _ := obj.Db(db).Where(obj).Debug(false).Count()
|
||||
// if cnt == 1 {
|
||||
// authorized = true
|
||||
// }
|
||||
// }
|
||||
|
||||
if authorized {
|
||||
h(w, r, p)
|
||||
} else {
|
||||
Unauthorized(w)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
package mods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"go.cdmnky.io/v2/fs"
|
||||
"go.cdmnky.io/v2/str"
|
||||
"go.cdmnky.io/v2/utils"
|
||||
)
|
||||
|
||||
func Format(format string, i ...any) string {
|
||||
return fmt.Sprintf(format, i...)
|
||||
}
|
||||
|
||||
func ProcessVideo(videoURL, videofile string) (err error) {
|
||||
|
||||
fmt.Printf("Fetching video data into file '%s'... ", videofile)
|
||||
data, err := Dload(videoURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(videofile, data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Complete!")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Dload(src string) (data []byte, err error) {
|
||||
|
||||
client := &http.Client{}
|
||||
req, _ := http.NewRequest("GET", src, nil)
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if res.StatusCode == 200 {
|
||||
data, err = io.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("errcode: %d", res.StatusCode)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func Encode(ffmpeg string, files []string) (err error) {
|
||||
|
||||
if !fs.FileExists(ffmpeg) {
|
||||
return fmt.Errorf("ffmpeg binary not found: %s", ffmpeg)
|
||||
}
|
||||
ratio := "1280x720"
|
||||
|
||||
for _, file := range files {
|
||||
encfile := fmt.Sprintf("/tmp/%s.mp4", str.Random(12))
|
||||
fmt.Printf("Encoding (%s) '%s'\n", ratio, encfile)
|
||||
args := []string{
|
||||
"-i",
|
||||
file,
|
||||
"-s", ratio,
|
||||
"-vcodec", "h264",
|
||||
"-acodec", "aac",
|
||||
encfile,
|
||||
}
|
||||
cmd := exec.Command(ffmpeg, args...)
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
os.Rename(encfile, file)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetFileSize(label, filename string) int64 {
|
||||
|
||||
fh, err := os.OpenFile(filename, os.O_RDONLY, os.ModeTemporary)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
fi, err := fh.Stat()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("%s: %s (%s)\n", label, filename, utils.GetHumanSize(fi.Size()))
|
||||
|
||||
return fi.Size()
|
||||
}
|
||||
|
||||
func ProcessAudio(URL, audiofile string) (err error) {
|
||||
|
||||
fmt.Printf("Fetching audio data into file '%s'... ", audiofile)
|
||||
data, err := Dload(URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(audiofile, data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Complete!")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Merge(ffmpeg, videofile, audiofile, tmpfile string) (err error) {
|
||||
|
||||
if !fs.FileExists(ffmpeg) {
|
||||
return fmt.Errorf("ffmpeg binary not found: %s", ffmpeg)
|
||||
}
|
||||
if !fs.FileExists(videofile) {
|
||||
return fmt.Errorf("work file not found: %s", videofile)
|
||||
}
|
||||
if !fs.FileExists(audiofile) {
|
||||
return fmt.Errorf("work file not found: %s", audiofile)
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-i",
|
||||
videofile,
|
||||
"-i",
|
||||
audiofile,
|
||||
"-c:v",
|
||||
"copy",
|
||||
"-c:a",
|
||||
"aac",
|
||||
tmpfile,
|
||||
}
|
||||
cmd := exec.Command(ffmpeg, args...)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package mods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cdmnky.io/net/patreon-ui/local.pkg/src/config"
|
||||
"go.cdmnky.io/v2/str"
|
||||
"go.cdmnky.io/v2/utils"
|
||||
)
|
||||
|
||||
func ProcPatreon(fn func(string), config *config.Config, playlistURL string, outfile string, maxSize int64) {
|
||||
|
||||
sleepTimeout := 5
|
||||
|
||||
//postProcessScript := ""
|
||||
tmpfile := Format("/tmp/%s.mp4", str.Random(12))
|
||||
|
||||
interval := 0
|
||||
|
||||
data, err := Dload(playlistURL)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
playlist := string(data)
|
||||
lines := strings.Split(playlist, "\n")
|
||||
|
||||
now := time.Now()
|
||||
|
||||
parts := []string{}
|
||||
for _, line := range lines {
|
||||
if len(line) > 0 && line[0:1] != "#" {
|
||||
parts = append(parts, line)
|
||||
}
|
||||
}
|
||||
|
||||
fh, err := os.Create(tmpfile)
|
||||
if err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
defer func() {
|
||||
fmt.Printf("Closing file '%s'", tmpfile)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
fn(Format("%v", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
fmt.Println("Complete!")
|
||||
}()
|
||||
|
||||
cnt := 1
|
||||
for _, part := range parts {
|
||||
fmt.Printf("Temp file............. %s\n", tmpfile)
|
||||
fmt.Printf("Outfile............... %s\n", outfile)
|
||||
fmt.Printf("Timeout............... %d\n", interval)
|
||||
fmt.Printf("# of parts to fetch... %d\n", len(parts))
|
||||
fmt.Printf("Writing to file '%s'\n", tmpfile)
|
||||
|
||||
fn(Format("Fetching part %d of %d...", cnt, len(parts)))
|
||||
data, err := Dload(part)
|
||||
if err != nil && err.Error() == "errcode: 403" {
|
||||
_, err = fh.Write(data)
|
||||
if err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
_, err = fh.Write(data)
|
||||
if err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
|
||||
if interval > 0 {
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
|
||||
fileSize := GetFileSize("Temp file", tmpfile)
|
||||
if maxSize > 0 && fileSize > int64(maxSize) {
|
||||
if err = Encode(config.FFMpegBin, []string{tmpfile}); err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("File size (%v) does not exceed max file size (%v); continuing...\n", fileSize, maxSize)
|
||||
}
|
||||
|
||||
fmt.Printf("Renaming '%s' to '%s'\n", tmpfile, outfile)
|
||||
os.Rename(tmpfile, outfile)
|
||||
|
||||
GetFileSize("Output file", outfile)
|
||||
|
||||
//if len(postProcessScript) > 0 {
|
||||
// execute(postProcessScript, outfile)
|
||||
//}
|
||||
|
||||
elapsedTime := utils.ElapsedTime(now)
|
||||
fmt.Printf("Elapsed time: %s\n", elapsedTime)
|
||||
fmt.Printf("Download complete. Filename: '%s', Elapsed time: %s\n", outfile, elapsedTime)
|
||||
fn(Format("Complete. Elapsed time: %s\n", elapsedTime))
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package mods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cdmnky.io/net/patreon-ui/local.pkg/src/config"
|
||||
vimeo "cdmnky.io/net/vimeo/pkg.local/vimeo/src"
|
||||
"go.cdmnky.io/v2/str"
|
||||
"go.cdmnky.io/v2/utils"
|
||||
)
|
||||
|
||||
func ProcVimeo(fn func(string), config *config.Config, playlistURL string, outfile string, maxSize int64) {
|
||||
|
||||
sleepTimeout := 5
|
||||
|
||||
videofile := fmt.Sprintf("/tmp/%s.mp4", str.Random(12))
|
||||
audiofile := fmt.Sprintf("/tmp/%s.mp3", str.Random(12))
|
||||
tmpfile := fmt.Sprintf("/tmp/%s.mp4", str.Random(12))
|
||||
encfile := fmt.Sprintf("/tmp/%s.mp4", str.Random(12))
|
||||
idxStart := "1"
|
||||
|
||||
now := time.Now()
|
||||
|
||||
fmt.Printf("Work Video File............. %s\n", videofile)
|
||||
fmt.Printf("Work Audio File............. %s\n", audiofile)
|
||||
fmt.Printf("Work Temp File.............. %s\n", tmpfile)
|
||||
fmt.Printf("Work Encoded File........... %s\n", encfile)
|
||||
fmt.Printf("Start Index................. %s\n", idxStart)
|
||||
fmt.Printf("Outfile..................... %s\n", outfile)
|
||||
|
||||
data, err := Dload(playlistURL)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
objVimeo := vimeo.Vimeo{}
|
||||
_, err = objVimeo.Unmarshal(data)
|
||||
if err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
|
||||
playlistURL = playlistURL[:strings.Index(playlistURL, "?")]
|
||||
baseURL, _ := url.JoinPath(playlistURL, "../", objVimeo.BaseURL)
|
||||
_ = baseURL
|
||||
|
||||
success := 0
|
||||
|
||||
idxs, err := strconv.Atoi(idxStart)
|
||||
if err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
fmt.Println("Fetching video data")
|
||||
time.Sleep(3 * time.Second)
|
||||
for idx := idxs; idx < len(objVimeo.Video); idx++ {
|
||||
URL, _ := url.JoinPath(baseURL, objVimeo.Video[idx].BaseURL, objVimeo.Video[idx].IndexSegment)
|
||||
fmt.Printf("Trying [%d/%d] %s...\n", idx, len(objVimeo.Video), objVimeo.Video[idx].ID)
|
||||
fn(Format("Fetching video data: Trying [%d/%d] %s...", idx, len(objVimeo.Video), objVimeo.Video[idx].ID))
|
||||
if err := ProcessVideo(URL, videofile); err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
} else {
|
||||
success++
|
||||
break
|
||||
}
|
||||
}
|
||||
GetFileSize("Video file", videofile)
|
||||
|
||||
fmt.Println("Fetching audio data")
|
||||
time.Sleep(3 * time.Second)
|
||||
for idx := 1; idx < len(objVimeo.Audio); idx++ {
|
||||
URL, _ := url.JoinPath(baseURL, objVimeo.Audio[idx].BaseURL, objVimeo.Audio[idx].IndexSegment)
|
||||
fmt.Printf("Trying [%d/%d] %s...\n", idx+1, len(objVimeo.Audio), objVimeo.Audio[idx].ID)
|
||||
fn(Format("Fetching audio data: Trying [%d/%d] %s...", idx+1, len(objVimeo.Audio), objVimeo.Audio[idx].ID))
|
||||
if err := ProcessAudio(URL, audiofile); err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
} else {
|
||||
success++
|
||||
break
|
||||
}
|
||||
}
|
||||
GetFileSize("Audio file", audiofile)
|
||||
|
||||
if success != 2 {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Merging audio/video data into temp file '%s'...\n", tmpfile)
|
||||
fn(Format("Merging audio/video data into temp file '%s'...", tmpfile))
|
||||
if err = Merge(config.FFMpegBin, videofile, audiofile, tmpfile); err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
|
||||
fileSize := GetFileSize("Temp file", tmpfile)
|
||||
if maxSize > 0 && fileSize > int64(maxSize) {
|
||||
if err = Encode(config.FFMpegBin, []string{tmpfile}); err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("File size does not exceed max file size; continuing...")
|
||||
}
|
||||
|
||||
fmt.Printf("Renaming '%s' to '%s'...\n", tmpfile, outfile)
|
||||
os.Rename(tmpfile, outfile)
|
||||
GetFileSize("Output file", outfile)
|
||||
|
||||
//if override || x.Prompt("Cleanup work files (y/n)? ") == "y" {
|
||||
fmt.Printf("Removing '%s'...\n", videofile)
|
||||
if err = os.Remove(videofile); err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
fmt.Printf("Removing '%s'...\n", audiofile)
|
||||
if err = os.Remove(audiofile); err != nil {
|
||||
fn(Format("Error: %v", err))
|
||||
time.Sleep(time.Duration(sleepTimeout) * time.Second)
|
||||
}
|
||||
//}
|
||||
|
||||
elapsedTime := utils.ElapsedTime(now)
|
||||
fmt.Printf("Elapsed time: %s\n", elapsedTime)
|
||||
|
||||
fmt.Printf("Download complete. Filename: '%s', Elapsed time: %s", outfile, elapsedTime)
|
||||
fn(Format("Complete. Elapsed time: %s\n", elapsedTime))
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright 2024 Brian Newman. All rights reserved.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cdmnky.io/net/patreon-ui/handlers/mods"
|
||||
"cdmnky.io/net/patreon-ui/local.pkg/src/config"
|
||||
"github.com/roblillack/spot"
|
||||
"github.com/roblillack/spot/ui"
|
||||
)
|
||||
|
||||
// NewServer ...
|
||||
func NewServer(config *config.Config) *Server {
|
||||
return &Server{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// Server ...
|
||||
type Server struct {
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
// Start ...
|
||||
func (x *Server) Start() {
|
||||
ui.Init()
|
||||
|
||||
height := 260
|
||||
width := 600
|
||||
profileIdx := 0
|
||||
|
||||
spot.MountFn(func(ctx *spot.RenderContext) spot.Component {
|
||||
|
||||
hints := map[string]string{}
|
||||
profiles := []string{}
|
||||
for _, t := range x.config.Templates {
|
||||
profiles = append(profiles, t.Mask)
|
||||
hints[t.Mask] = t.Hint
|
||||
}
|
||||
|
||||
filename, setFilename := spot.UseState[string](ctx, "")
|
||||
lblFilename := &ui.Label{
|
||||
X: 5, Y: 213, Width: width - 10, Height: 20, Value: filename,
|
||||
}
|
||||
|
||||
status, showStatus := spot.UseState[string](ctx, fmt.Sprintf("Hint: %s", hints[profiles[profileIdx]]))
|
||||
lblStatus := &ui.Label{
|
||||
X: 5, Y: 235, Width: width - 10, Height: 20, Value: status,
|
||||
}
|
||||
|
||||
cmbProfile := &ui.Dropdown{
|
||||
X: 5, Y: 5, Width: width - 10, Height: 20, Items: profiles, Editable: false, SelectedIndex: profileIdx,
|
||||
OnSelectionDidChange: func(idx int) {
|
||||
profileIdx = idx
|
||||
showStatus(fmt.Sprintf("Hint: %s", hints[profiles[idx]]))
|
||||
},
|
||||
}
|
||||
|
||||
lblVideoURL := &ui.Label{
|
||||
X: 5, Y: 30, Width: width - 10, Height: 20, Value: "Video URL:",
|
||||
}
|
||||
videoURL := ""
|
||||
videoURL, setVideoURL := spot.UseState[string](ctx, "")
|
||||
txtVideeoURL := &ui.TextField{
|
||||
X: 5, Y: 50, Width: width - 10, Height: 20, Value: videoURL,
|
||||
OnChange: func(value string) { setVideoURL(value) },
|
||||
}
|
||||
|
||||
lblVideoTitle := &ui.Label{
|
||||
X: 5, Y: 80, Width: width - 10, Height: 20, Value: "Video Title:",
|
||||
}
|
||||
videoTitle, setVideoTitle := spot.UseState[string](ctx, "")
|
||||
txtVideeoTitle := &ui.TextField{
|
||||
X: 5, Y: 100, Width: width - 10, Height: 20, Value: videoTitle,
|
||||
OnChange: func(value string) { setVideoTitle(value) },
|
||||
}
|
||||
|
||||
lblThreshhold := &ui.Label{
|
||||
X: 5, Y: 130, Width: width - 10, Height: 20, Value: "Size in MB to trigger re-encoding (0 for none)",
|
||||
}
|
||||
thresshold, setThresshold := spot.UseState[string](ctx, x.config.Thresshold)
|
||||
txtThresshold := &ui.TextField{
|
||||
X: 5, Y: 150, Width: width - 10, Height: 20, Value: thresshold,
|
||||
OnChange: func(value string) { setThresshold(value) },
|
||||
}
|
||||
|
||||
btnProcess := &ui.Button{
|
||||
X: 5, Y: 180, Width: width - 10, Height: 25,
|
||||
Title: "Process Download",
|
||||
OnClick: func() {
|
||||
|
||||
mask := profiles[profileIdx]
|
||||
title := videoTitle
|
||||
url := videoURL
|
||||
threshold, err := strconv.ParseInt(thresshold, 0, 64)
|
||||
if err != nil {
|
||||
showStatus(mods.Format("Error: %v", err))
|
||||
return
|
||||
}
|
||||
threshold = threshold * (1000 * 1000)
|
||||
mask = strings.Replace(mask, "{title}", title, -1)
|
||||
setFilename(mods.Format("Output file: %s", mask))
|
||||
|
||||
//if strings.Contains(url, "akamaized.net") {
|
||||
if strings.Contains(url, "vimeocdn.com") {
|
||||
go mods.ProcVimeo(showStatus, x.config, url, mask, threshold)
|
||||
} else {
|
||||
go mods.ProcPatreon(showStatus, x.config, url, mask, threshold)
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
return &ui.Window{
|
||||
Title: x.config.Appname,
|
||||
Width: width,
|
||||
Height: height,
|
||||
Children: []spot.Component{
|
||||
cmbProfile,
|
||||
lblVideoURL,
|
||||
txtVideeoURL,
|
||||
lblVideoTitle,
|
||||
txtVideeoTitle,
|
||||
lblThreshhold,
|
||||
txtThresshold,
|
||||
btnProcess,
|
||||
lblFilename,
|
||||
lblStatus,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
ui.Run()
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2024 Brian Newman. All rights reserved.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"cdmnky.io/net/patreon-ui/local.pkg/src/sitevars"
|
||||
|
||||
"go.cdmnky.io/v2/net/assets"
|
||||
"go.cdmnky.io/v2/net/router"
|
||||
"go.cdmnky.io/v2/net/session"
|
||||
)
|
||||
|
||||
// NewTemplate ...
|
||||
func NewTemplate(
|
||||
db *sql.DB,
|
||||
assets *assets.Assets,
|
||||
session *session.Manager,
|
||||
sitevars *sitevars.Sitevars,
|
||||
) *Template {
|
||||
return &Template{db: db, assets: assets, session: session, sitevars: sitevars}
|
||||
}
|
||||
|
||||
// Register ...
|
||||
func (x *Template) Register(r *router.Router) {
|
||||
r.Handle("POST", "/template", x.Create)
|
||||
r.Handle("GET", "/template", x.Read)
|
||||
r.Handle("PUT", "/template", x.Update)
|
||||
r.Handle("DELETE", "/template", x.Delete)
|
||||
}
|
||||
|
||||
// Template ...
|
||||
type Template struct {
|
||||
db *sql.DB
|
||||
assets *assets.Assets
|
||||
session *session.Manager
|
||||
sitevars *sitevars.Sitevars
|
||||
}
|
||||
|
||||
// Create ...
|
||||
func (x *Template) Create(w http.ResponseWriter, r *http.Request, params url.Values) {
|
||||
w.Write([]byte{})
|
||||
}
|
||||
|
||||
// Read ...
|
||||
func (x *Template) Read(w http.ResponseWriter, r *http.Request, params url.Values) {
|
||||
w.Write([]byte{})
|
||||
}
|
||||
|
||||
// Update ...
|
||||
func (x *Template) Update(w http.ResponseWriter, r *http.Request, params url.Values) {
|
||||
w.Write([]byte{})
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (x *Template) Delete(w http.ResponseWriter, r *http.Request, params url.Values) {
|
||||
w.Write([]byte{})
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// auto generated by cdgen, 2019.05.1 on Wednesday Jun 12 @ 6:40PM
|
||||
// source file: database/config.json
|
||||
|
||||
/*
|
||||
* This is free and unencumbered software released into the public
|
||||
* domain.
|
||||
*
|
||||
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
* distribute this software, either in source code form or as a compiled
|
||||
* binary, for any purpose, commercial or non-commercial, and by any
|
||||
* means.
|
||||
*
|
||||
* The complete license can be found in the UNLICENSE file.
|
||||
*
|
||||
* Vim:
|
||||
* set foldmethod=marker
|
||||
* set shiftwidth=2
|
||||
* set smartcase
|
||||
* set tabstop=2
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Config models the table config
|
||||
type Config struct {
|
||||
Appname string `json:"-"`
|
||||
Thresshold string `json:"thresshold"`
|
||||
FFMpegBin string `json:"ffmpegbin"`
|
||||
Templates []Template `json:"templates"`
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
Mask string `json:"mask"`
|
||||
Hint string `json:"hint"`
|
||||
}
|
||||
|
||||
// New ...
|
||||
func New() *Config {
|
||||
return &Config{
|
||||
Templates: []Template{
|
||||
{
|
||||
Mask: "Creator Name, {title}",
|
||||
Hint: "URL: master.json",
|
||||
},
|
||||
},
|
||||
FFMpegBin: "/usr/bin/ffmpeg",
|
||||
}
|
||||
}
|
||||
|
||||
// ToJSON ...
|
||||
func (x *Config) ToJSON() (data []byte, err error) {
|
||||
data, err = json.MarshalIndent(x, " ", " ")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse ...
|
||||
func (x *Config) Parse(data []byte) (err error) {
|
||||
err = json.Unmarshal(data, x)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright 2024 Brian Newman. All rights reserved.
|
||||
|
||||
// Package global ...
|
||||
package global
|
||||
|
||||
var (
|
||||
// DateFormat ...
|
||||
DateFormat = "2006-01-02 15:04:05 -0700 UTC"
|
||||
|
||||
// ConfigFileName ...
|
||||
ConfigFileName = "./config.json"
|
||||
)
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2024 Brian Newman. All rights reserved.
|
||||
|
||||
// Package response ...
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Return codes
|
||||
const (
|
||||
OK = "200"
|
||||
ERROR = "500"
|
||||
)
|
||||
|
||||
// New ...
|
||||
func New(code string, message string, data []byte) *Response {
|
||||
return &Response{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Response ...
|
||||
type Response struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data []byte `json:"json"`
|
||||
}
|
||||
|
||||
// ToJSON ...
|
||||
func (x *Response) ToJSON() (data []byte, err error) {
|
||||
data, err = json.Marshal(x)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse ...
|
||||
func (x *Response) Parse(data []byte) (err error) {
|
||||
err = json.Unmarshal(data, x)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2024 Brian Newman. All rights reserved.
|
||||
|
||||
// Package sitevars ...
|
||||
package sitevars
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
|
||||
"cdmnky.io/net/patreon-ui/local.pkg/src/config"
|
||||
)
|
||||
|
||||
// Sitevars ...
|
||||
type Sitevars struct {
|
||||
Sitename string `json:"sitename"`
|
||||
Copyright string `json:"copyright"`
|
||||
Port string `json:"port"`
|
||||
Dbfile string `json:"dbfile"`
|
||||
Templates []config.Template `json:"templates"`
|
||||
}
|
||||
|
||||
// GetCopyright ...
|
||||
func (x *Sitevars) GetCopyright() template.HTML {
|
||||
return template.HTML(x.Copyright)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
# Copyright 2024 Brian Newman. All rights reserved.
|
||||
# ################################################################################
|
||||
# GOARCH=amd64 GOOS=windows go build -o ${BINNAME}-linux-${VERSION}.exe -ldflags "-X main.appname=${BINNAME} -X main.version=${VERSION}" *.go
|
||||
# rm ${BINNAME}-linux-${VERSION}.exe
|
||||
# ################################################################################
|
||||
APPNAME := patreon-ui
|
||||
BINNAME := io.cdmnky.net.patreon-ui
|
||||
VERSION := $(shell cat VERSION)
|
||||
BUILD := $(shell date +"%Y.%m.%d")
|
||||
LINKNAME := ${HOME}/.bin/patreon-ui/patreon-ui
|
||||
|
||||
build:
|
||||
./local.bin/io.cdmnky.dev.mkassets -d webassets/ -o ./src/assets.go
|
||||
GOARCH=amd64 GOOS=linux go build -o ./bin/${BINNAME}-linux-${VERSION}.bin -ldflags "-X main.appname=${APPNAME} -X main.version=${VERSION} -X main.build=${BUILD}" ./src/*.go
|
||||
./local.bin/io.cdmnky.dev.mkassets -d /dev/null -o ./src/assets.go
|
||||
|
||||
run:
|
||||
GOARCH=amd64 GOOS=linux go build -o ./bin/${BINNAME} ./src/*.go
|
||||
clear; ./bin/${BINNAME}
|
||||
|
||||
install:
|
||||
ifneq ("$(wildcard $(LINKNAME))","")
|
||||
rm ${LINKNAME}
|
||||
endif
|
||||
cp ./bin/${BINNAME}-linux-${VERSION}.bin ${HOME}/.bin/patreon-ui/
|
||||
ln -s ${HOME}/.bin/patreon-ui/${BINNAME}-linux-${VERSION}.bin ${LINKNAME}
|
||||
|
||||
clean:
|
||||
go clean
|
||||
ifneq ("$(wildcard assets.go)","")
|
||||
rm assets.go*
|
||||
endif
|
||||
ifneq ("$(wildcard ./bin/${BINNAME}-linux-${VERSION}.bin)","")
|
||||
rm ./bin/${BINNAME}-linux-${VERSION}.bin
|
||||
endif
|
||||
ifneq ("$(wildcard ./bin/${BINNAME})","")
|
||||
rm ./bin/${BINNAME}
|
||||
endif
|
||||
all: build
|
|
@ -0,0 +1,5 @@
|
|||
# Copyright 2024 Brian Newman. All rights reserved.
|
||||
PS1="\u@\W) "
|
||||
export GOPATH="$GOPATH:`pwd`/local.pkg"
|
||||
export GO111MODULE=off
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package main
|
||||
|
||||
var mapOfAssets = map[string]string{
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2024 Brian Newman. All rights reserved.
|
||||
/*
|
||||
A few words about this application.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"cdmnky.io/net/patreon-ui/handlers"
|
||||
"cdmnky.io/net/patreon-ui/local.pkg/src/config"
|
||||
"cdmnky.io/net/patreon-ui/local.pkg/src/global"
|
||||
)
|
||||
|
||||
var (
|
||||
appname = "template"
|
||||
version = "xx.yy.zz"
|
||||
build = "yyyy.mm.dd"
|
||||
|
||||
globConfig *config.Config
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Load config.json values
|
||||
// -----------------------------------------------------------------------------
|
||||
binpath, _ := os.Executable()
|
||||
binpath = path.Dir(binpath)
|
||||
data, err := os.ReadFile(filepath.Join(binpath, global.ConfigFileName))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
globConfig = config.New()
|
||||
err = globConfig.Parse(data)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
globConfig.Appname = fmt.Sprintf("%s v%s, %s", appname, version, build)
|
||||
|
||||
(handlers.NewServer(
|
||||
globConfig,
|
||||
)).Start()
|
||||
}
|
Loading…
Reference in New Issue