Initial commit

This commit is contained in:
cdmnky 2024-05-25 12:05:42 -04:00
parent 40c9af083e
commit fae249faae
18 changed files with 976 additions and 2 deletions

6
.gitignore vendored
View File

@ -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

1
patreon-dl/gui/VERSION Executable file
View File

@ -0,0 +1 @@
0.0.2

12
patreon-dl/gui/bin/config.json Executable file
View File

@ -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" }
]
}

View File

@ -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"))
}

View File

@ -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)
}
})
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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))
}

138
patreon-dl/gui/handlers/server.go Executable file
View File

@ -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()
}

View File

@ -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{})
}

View File

@ -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
}

View File

@ -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"
)

View File

@ -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
}

View File

@ -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)
}

39
patreon-dl/gui/makefile Normal file
View File

@ -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

5
patreon-dl/gui/setenv.sh Executable file
View File

@ -0,0 +1,5 @@
# Copyright 2024 Brian Newman. All rights reserved.
PS1="\u@\W) "
export GOPATH="$GOPATH:`pwd`/local.pkg"
export GO111MODULE=off

4
patreon-dl/gui/src/assets.go Executable file
View File

@ -0,0 +1,4 @@
package main
var mapOfAssets = map[string]string{
}

49
patreon-dl/gui/src/main.go Executable file
View File

@ -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()
}