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
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
#
|
#
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins
|
||||||
|
*.bin
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
*.exe
|
*.exe
|
||||||
*.exe~
|
*.exe~
|
||||||
*.dll
|
*.manifest
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.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