Compare commits
13 Commits
fd7dcafd4e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18ac13fd57 | ||
|
|
375fc1000a | ||
|
|
47c7aa74d3 | ||
|
|
a6aad2820d | ||
|
|
b26afcb325 | ||
|
|
613bfdf96e | ||
|
|
81507319dd | ||
|
|
50ad3bb3db | ||
|
|
07992b6636 | ||
|
|
9583234df5 | ||
|
|
2943362587 | ||
|
|
cbd4e32022 | ||
|
|
727b4fdea6 |
3
install_scratch.sh
Normal file
3
install_scratch.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
CGO_ENABLED=1 CC=x86_64-linux-musl-gcc go install -ldflags="-linkmode external -extldflags '-static'"
|
||||
@@ -11,21 +11,22 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"show-rss/src/slow"
|
||||
"strconv"
|
||||
"golang.org/x/time/rate"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
var EnvCksumBPS = func() int {
|
||||
s := os.Getenv("CKSUM_BPS")
|
||||
if s == "" {
|
||||
return 50_000_000
|
||||
}
|
||||
n, err := strconv.Atoi(s)
|
||||
if err != nil || n < 1 {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
s := os.Getenv("CKSUM_BPS")
|
||||
if s == "" {
|
||||
return 50_000_000
|
||||
}
|
||||
n, err := strconv.Atoi(s)
|
||||
if err != nil || n < 1 {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
}()
|
||||
|
||||
func One(ctx context.Context, p string) error {
|
||||
@@ -36,17 +37,17 @@ func One(ctx context.Context, p string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
threshold := 20 + rand.New(rand.NewSource(func() int64{
|
||||
b := md5.New().Sum([]byte(p))
|
||||
var sum int64
|
||||
for _, c := range b {
|
||||
sum += int64(c)
|
||||
sum *= int64(c)
|
||||
}
|
||||
return sum
|
||||
}())).Int()%10
|
||||
if daysSince := int(time.Since(last.T).Hours()/24); daysSince > threshold {
|
||||
log.Printf("asses.One(%s) // no modified check as %vd since last check", shortp, daysSince)
|
||||
threshold := 20 + rand.New(rand.NewSource(func() int64 {
|
||||
b := md5.New().Sum([]byte(p))
|
||||
var sum int64
|
||||
for _, c := range b {
|
||||
sum += int64(c)
|
||||
sum *= int64(c)
|
||||
}
|
||||
return sum
|
||||
}())).Int()%10
|
||||
if daysSince := int(time.Since(last.T).Hours() / 24); daysSince > threshold {
|
||||
log.Printf("asses.One(%s) // no modified check as %vd since last check", shortp, daysSince)
|
||||
} else if stat, err := os.Stat(p); err != nil {
|
||||
return fmt.Errorf("cannot stat %s: %w", p, err)
|
||||
} else if stat.ModTime() == last.Modified {
|
||||
@@ -54,16 +55,16 @@ func One(ctx context.Context, p string) error {
|
||||
return nil
|
||||
} else {
|
||||
log.Printf("asses.One(%s) // modified (%v) is now %v", shortp, last.Modified, stat.ModTime())
|
||||
}
|
||||
}
|
||||
|
||||
doCksum := true
|
||||
doCksum := true
|
||||
if err := func() error {
|
||||
if len(last.Cksum) > 0 {
|
||||
if last.Modified.IsZero() {
|
||||
doCksum = false
|
||||
if last.Modified.IsZero() {
|
||||
doCksum = false
|
||||
log.Printf("asses.One(%s) // assume cksum unchanged given null modified ", shortp)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
cksum, err := Cksum(ctx, p)
|
||||
if err != nil {
|
||||
@@ -81,29 +82,34 @@ func One(ctx context.Context, p string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("asses.transcode(%s)...", shortp)
|
||||
if err := transcode(ctx, p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var cksum string
|
||||
if doCksum {
|
||||
var err error
|
||||
cksum, err = Cksum(ctx, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var cksum string
|
||||
if doCksum {
|
||||
var err error
|
||||
cksum, err = Cksum(ctx, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
stat, err := os.Stat(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checked(ctx, p, cksum, stat.ModTime()); err != nil {
|
||||
log.Printf("failed to mark %s checked: %v", shortp, err)
|
||||
return err
|
||||
}
|
||||
log.Printf("failed to mark %s checked: %v", shortp, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func Cksum(ctx context.Context, p string) (string, error) {
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
func TestOne(t *testing.T) {
|
||||
ctx := db.Test(t, context.Background())
|
||||
|
||||
os.Setenv("DO_TRANSCODE", "true")
|
||||
|
||||
d := t.TempDir()
|
||||
b, _ := os.ReadFile(path.Join("testdata", "survivor_au_S11E12.smoller.mkv"))
|
||||
p := path.Join(d, "f.mkv")
|
||||
|
||||
64
src/asses/transcode.go
Normal file
64
src/asses/transcode.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package asses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func EntrypointTranscode(ctx context.Context, p string) error {
|
||||
return transcode(ctx, p)
|
||||
}
|
||||
|
||||
func transcode(ctx context.Context, p string) error {
|
||||
if os.Getenv("NO_TRANSCODE") != "" || os.Getenv("DO_TRANSCODE") == "" {
|
||||
log.Printf("would transcode %s but $NO_TRANSCODE=x or $DO_TRANSCODE=", p)
|
||||
return nil
|
||||
}
|
||||
|
||||
output, err := ffprobe(ctx, "-i", p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h264 := slices.ContainsFunc(strings.Split(output, "\n"), func(line string) bool {
|
||||
return strings.Contains(line, "tream #") && strings.Contains(line, "Video: ") && strings.Contains(line, "h264")
|
||||
})
|
||||
aac := slices.ContainsFunc(strings.Split(output, "\n"), func(line string) bool {
|
||||
return strings.Contains(line, "tream #") && strings.Contains(line, "Audio: ") && strings.Contains(line, "aac")
|
||||
})
|
||||
|
||||
if h264 && aac {
|
||||
return nil
|
||||
}
|
||||
|
||||
p2 := p + ".en" + path.Ext(p)
|
||||
if err := ffmpeg(ctx, "-y",
|
||||
"-i", p,
|
||||
"-vcodec", "libx264",
|
||||
"-acodec", "aac",
|
||||
p2,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output2, err := ffprobe(ctx, "-i", p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
df := func(line string) bool {
|
||||
return !strings.Contains(line, "tream #")
|
||||
}
|
||||
originalStreams := slices.DeleteFunc(strings.Split(output, "\n"), df)
|
||||
newStreams := slices.DeleteFunc(strings.Split(output2, "\n"), df)
|
||||
if len(originalStreams) != len(newStreams) {
|
||||
return fmt.Errorf("stream count changed from transcode")
|
||||
}
|
||||
|
||||
return os.Rename(p2, p)
|
||||
}
|
||||
@@ -60,6 +60,14 @@ func Main(ctx context.Context, args []string) error {
|
||||
return fmt.Errorf("errors: %+v", errs)
|
||||
}
|
||||
return nil
|
||||
case Transcode:
|
||||
for _, pos := range flags.Pos {
|
||||
log.Printf("transcoding %q...", pos)
|
||||
if err := inass.EntrypointTranscode(ctx, pos); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
panic(flags.Entrypoint.String())
|
||||
}
|
||||
@@ -72,6 +80,7 @@ const (
|
||||
Defacto Entrypoint = iota
|
||||
DeportAss
|
||||
BestAssToSRT
|
||||
Transcode
|
||||
)
|
||||
|
||||
func (e *Entrypoint) Set(s string) error {
|
||||
@@ -82,11 +91,14 @@ func (e *Entrypoint) Set(s string) error {
|
||||
*e = DeportAss
|
||||
case BestAssToSRT.String():
|
||||
*e = BestAssToSRT
|
||||
case Transcode.String():
|
||||
*e = Transcode
|
||||
default:
|
||||
return fmt.Errorf("%s nin (%s)", s, strings.Join([]string{
|
||||
Defacto.String(),
|
||||
DeportAss.String(),
|
||||
BestAssToSRT.String(),
|
||||
Transcode.String(),
|
||||
}, ", "))
|
||||
}
|
||||
return nil
|
||||
@@ -100,6 +112,8 @@ func (e Entrypoint) String() string {
|
||||
return "deport-ass"
|
||||
case BestAssToSRT:
|
||||
return "best-ass-to-srt"
|
||||
case Transcode:
|
||||
return "transcode"
|
||||
}
|
||||
panic("cannot serialize entrypoint")
|
||||
}
|
||||
@@ -118,6 +132,5 @@ func runner(ctx context.Context, k string, foo func(context.Context) error) func
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,25 @@ import (
|
||||
|
||||
func (h Handler) feeds(w http.ResponseWriter, r *http.Request) error {
|
||||
switch r.Method {
|
||||
case http.MethodDelete:
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.feedsDelete(r.Context(), r.URL.Query().Get("id")); err != nil {
|
||||
return err
|
||||
}
|
||||
case http.MethodPost, http.MethodPut:
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.feedsUpsert(r.Context(), r.URL.Query().Get("id"), r.Form); err != nil {
|
||||
return err
|
||||
if r.URL.Query().Has("delete") {
|
||||
if err := h.feedsDelete(r.Context(), r.URL.Query().Get("id")); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := h.feedsUpsert(r.Context(), r.URL.Query().Get("id"), r.Form); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
@@ -29,6 +42,10 @@ func (h Handler) feeds(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h Handler) feedsDelete(ctx context.Context, id string) error {
|
||||
return feeds.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (h Handler) feedsUpsert(ctx context.Context, id string, form url.Values) error {
|
||||
var req feeds.Version
|
||||
for k, v := range map[string]*string{
|
||||
|
||||
@@ -23,7 +23,13 @@
|
||||
{{ if eq "" .editing.ID }}
|
||||
New
|
||||
{{ else }}
|
||||
Update <code><a target="_blank" href="{{ .editing_url }}">{{ .editing.URL }}</a></code> (<a href="?">clear</a>)
|
||||
Updating <code><a target="_blank" href="{{ .editing_url }}">{{ .editing.URL }}</a></code> (<a href="?">clear</a>)
|
||||
<br>
|
||||
<div style="scale: 0.85">
|
||||
<form method="POST" action="/v1/feeds?id={{ .editing.ID }}&delete">
|
||||
<button type="submit">DELETE</button>
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
</h3>
|
||||
<form method="POST" action="/v1/feeds?id={{ .editing.ID }}">
|
||||
@@ -32,9 +38,9 @@
|
||||
<div>
|
||||
<label for="{{ $k }}">
|
||||
{{ $k }}
|
||||
{{- if eq $k "URL" }}
|
||||
{{- if eq $k "URL" }}
|
||||
(hint: nyaa://?q=show)
|
||||
{{ else if eq $k "WebhookURL" }}
|
||||
{{ else if eq $k "WebhookURL" }}
|
||||
(hint: vpntor:///outdir)
|
||||
{{ end }}
|
||||
</label>
|
||||
@@ -17,17 +17,17 @@ import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed testdata/index.tmpl
|
||||
//go:embed public/index.tmpl
|
||||
var embeddedIndexTMPL string
|
||||
|
||||
//go:embed testdata/*
|
||||
//go:embed public/*
|
||||
var embeddedDir embed.FS
|
||||
|
||||
var dir = func() string {
|
||||
if v := os.Getenv("UI_D"); v != "" {
|
||||
return v
|
||||
}
|
||||
return "./src/cmd/server/handler/testdata"
|
||||
return "./src/cmd/server/handler/public"
|
||||
}()
|
||||
|
||||
func (h Handler) ui(w http.ResponseWriter, r *http.Request) error {
|
||||
@@ -37,7 +37,7 @@ func (h Handler) ui(w http.ResponseWriter, r *http.Request) error {
|
||||
|
||||
w.Header().Set("Cache-Control", "max-age=2592000")
|
||||
fs := http.FileServer(http.FS(embeddedDir))
|
||||
r.URL.Path = fmt.Sprintf("/testdata/%s", strings.TrimPrefix(r.URL.Path, "/experimental/ui"))
|
||||
r.URL.Path = fmt.Sprintf("/public/%s", strings.TrimPrefix(r.URL.Path, "/experimental/ui"))
|
||||
fs.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
@@ -57,7 +57,9 @@ func (h Handler) uiIndex(w http.ResponseWriter, r *http.Request) error {
|
||||
editing.Version.WebhookURL = "vpntor:///data/completed-rss/TITLE"
|
||||
all := []feeds.Feed{}
|
||||
if err := feeds.ForEach(ctx, func(f feeds.Feed) error {
|
||||
all = append(all, f)
|
||||
if deleted := f.Version.URL == ""; !deleted {
|
||||
all = append(all, f)
|
||||
}
|
||||
if f.Entry.ID == r.URL.Query().Get("edit") {
|
||||
editing.Entry = f.Entry
|
||||
editing.Version = f.Version
|
||||
|
||||
@@ -72,18 +72,27 @@ func ForEach(ctx context.Context, cb func(Feed) error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var errs []string
|
||||
for _, id := range ids {
|
||||
feed, err := Get(ctx, id.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if err := cb(feed); err != nil {
|
||||
return err
|
||||
errs = append(errs, fmt.Sprintf(`failed to fetch %s: %v`, id.ID, err))
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("failed some callbacks: %+v", errs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Delete(ctx context.Context, id string) error {
|
||||
return Update(ctx, id, "", "", "", "", "", "")
|
||||
}
|
||||
|
||||
func Get(ctx context.Context, id string) (Feed, error) {
|
||||
if err := initDB(ctx); err != nil {
|
||||
return Feed{}, err
|
||||
|
||||
@@ -28,7 +28,7 @@ var (
|
||||
)
|
||||
|
||||
func (feed Feed) ShouldExecute() (bool, error) {
|
||||
if !feed.Entry.Deleted.IsZero() {
|
||||
if !feed.Entry.Deleted.IsZero() || feed.Version.URL == "" {
|
||||
return false, nil
|
||||
}
|
||||
next, err := feed.Next()
|
||||
|
||||
Reference in New Issue
Block a user