Compare commits

..

3 commits

Author SHA1 Message Date
f4497aef7e Add mkflex command 2025-07-16 11:40:25 -05:00
72295c53cf 🚨 Silence linter warnings I don't care about 2025-07-16 11:13:53 -05:00
aaa92bf151 Get pattern from name 2025-07-14 14:14:04 -05:00
11 changed files with 350 additions and 3 deletions

View file

@ -35,6 +35,7 @@ tasks:
internal: true
cmds:
- go build -o build/ ./cmd/{{.CMD}}
build-convids:
desc: Builds the convids command
sources:
@ -45,6 +46,7 @@ tasks:
- task: cmd-build
vars:
CMD: convids
build-cool-down:
desc: Builds the cool-down command
source:
@ -58,6 +60,7 @@ tasks:
- task: cmd-build
vars:
CMD: cool-down
build-all:
desc: Builds all available commands
sources:
@ -70,6 +73,19 @@ tasks:
vars:
CMD: "*"
install-mkflex:
desc: Installs the mkflex command
source:
- cmd/mkflex/**/*.go
- convids/**/*.go
- internal/cli/mkflex/**/*.go
- mkflex/**/*.go
- types/**/*.go
generates:
- '{{.BIN}}/mkflex'
cmds:
- go install ./cmd/mkflex
install-cool-down:
desc: Installs the cool-down command
source:

27
cmd/mkflex/main.go Normal file
View file

@ -0,0 +1,27 @@
package main
import (
"fmt"
"os"
c "codeberg.org/danjones000/utils/cli/context"
e "codeberg.org/danjones000/utils/cli/err"
mkcli "codeberg.org/danjones000/utils/internal/cli/mkflex"
)
const dataPath = "/home/drj/WeboNextCloud/Computer/dotfiles/bigbad/shows.yml"
const flexPath = "/home/drj/.flexget/config.yml"
const flexTemp = "/home/drj/.flexget/config.temp.yml"
func main() {
ctx, done := c.SelfCancelingContextFromBackground()
defer done()
app, err := mkcli.NewApp(ctx, os.Args[0], os.Args[1:], dataPath, flexTemp, flexPath)
e.HandleErr(err)
err = app.Run(ctx)
e.HandleErr(err)
fmt.Println("Generated flexget config")
}

View file

@ -18,6 +18,9 @@ func (s *Show) Match(path string) (bool, error) {
if s == nil {
return false, ErrNilPointer
}
if !s.Anime && s.Name != "" && s.Pattern == "" {
s.Pattern = strings.ReplaceAll(s.Name, " ", ".")
}
if s.Pattern != "" {
return s.matchRegexp(path)
}

View file

@ -1,6 +1,10 @@
package convids
import "regexp"
import (
"regexp"
"codeberg.org/danjones000/utils/types"
)
type Data struct {
Config *Config
@ -28,6 +32,13 @@ type Show struct {
Url bool
Backup string
Sources []string
Flexget struct {
Name string
Begin types.IntOrString
AlternateName []string `yaml:"alternate_name"`
Exact bool
Skip bool
}
re *regexp.Regexp
}

View file

@ -172,6 +172,7 @@ func (m *Merger) getRecipes(ctx context.Context, items []Item) (err error) {
return
}
//nolint:errcheck // I don't care
defer rows.Close()
rm := make(map[int32]map[int32][]int32)
var found bool
@ -214,11 +215,11 @@ func addRecs(recs *[][]int32, rec []int32) {
func recSorter(rec []int32) func([]int32) bool {
return func(v []int32) bool {
slices.Sort(v)
if !(len(v) == len(rec)) {
if len(v) != len(rec) {
return false
}
for idx, i := range v {
if !(i == rec[idx]) {
if i != rec[idx] {
return false
}
}
@ -256,6 +257,7 @@ func (m *Merger) insertGames(ctx context.Context) (mp map[int64]Game, err error)
if err != nil {
return
}
//nolint:errcheck // I don't care
defer stmt.Close()
for _, g := range m.games {
res, err = stmt.ExecContext(ctx, g)
@ -289,11 +291,13 @@ func (m *Merger) insertItems(ctx context.Context, gameID int64, items []Item) (i
if err != nil {
return
}
//nolint:errcheck // I don't care
defer stmt.Close()
fetchIDStmt, err = m.db.PreparexContext(ctx, `SELECT id FROM items WHERE name = ?`)
if err != nil {
return
}
//nolint:errcheck // I don't care
defer fetchIDStmt.Close()
relStmt, err = m.db.PreparexContext(ctx, `
INSERT INTO games_items
@ -303,6 +307,7 @@ func (m *Merger) insertItems(ctx context.Context, gameID int64, items []Item) (i
if err != nil {
return
}
//nolint:errcheck // I don't care
defer relStmt.Close()
for _, it := range items {
_, err = stmt.ExecContext(ctx, it)
@ -334,6 +339,7 @@ func (m *Merger) insertRecipes(ctx context.Context, gameID, itemID int64, recipe
if err != nil {
return
}
//nolint:errcheck // I don't care
defer insertStmt.Close()
insertRecItemStmt, err = m.db.PreparexContext(ctx, `
INSERT INTO recipes_items
@ -343,6 +349,7 @@ func (m *Merger) insertRecipes(ctx context.Context, gameID, itemID int64, recipe
if err != nil {
return
}
//nolint:errcheck // I don't care
defer insertRecItemStmt.Close()
selStmt, err = m.db.PreparexContext(ctx, `
SELECT item_id FROM games_items
@ -351,6 +358,7 @@ func (m *Merger) insertRecipes(ctx context.Context, gameID, itemID int64, recipe
if err != nil {
return
}
//nolint:errcheck // I don't care
defer selStmt.Close()
for _, recipe := range recipes {
var recipeID int64

View file

@ -14,6 +14,7 @@ func NewMerger(args []string) (*Merger, error) {
}
fs := make([]*os.File, len(args))
for idx, pth := range args {
//nolint:gosec // Yes, of course
fs[idx], err = os.Open(pth)
if err != nil {
return nil, err

View file

@ -14,12 +14,15 @@ func (m *Merger) Write(ctx context.Context, g Game) (out string, err error) {
out = base + ".ic"
var f *os.File
//nolint:gosec // Yes, of course I'm getting the file from user input
if f, err = os.Create(out); err != nil {
return
}
//nolint:errcheck // I don't care
defer f.Close()
gz := gzip.NewWriter(f)
//nolint:errcheck // I don't care
defer gz.Close()
gz.Name = base
gz.ModTime = time.Unix(g.Updated/1000, g.Updated%1000)

View file

@ -0,0 +1,53 @@
package mkflex
import (
"context"
"os"
conutils "codeberg.org/danjones000/utils/convids"
mkutils "codeberg.org/danjones000/utils/mkflex"
"gopkg.in/yaml.v3"
)
func NewApp(ctx context.Context, name string, args []string, dataPath, templatePath, outPath string) (*App, error) {
var err error
a := App{
Name: name,
Path: outPath,
}
a.Data, err = conutils.NewData(dataPath)
if err != nil {
return nil, err
}
a.Config, err = mkutils.NewConfig(templatePath)
if err != nil {
return nil, err
}
return &a, nil
}
type App struct {
Name string
Data *conutils.Data
Path string
Config *mkutils.Config
}
func (a *App) Run(ctx context.Context) error {
out, err := os.Create(a.Path)
if err != nil {
return err
}
enc := yaml.NewEncoder(out)
enc.SetIndent(2)
if err := mkutils.AddShows(a.Config, a.Data); err != nil {
return err
}
delete(a.Config.Templates, "x-aliases")
return enc.Encode(a.Config)
}

142
mkflex/logic.go Normal file
View file

@ -0,0 +1,142 @@
package mkflex
import (
"cmp"
"errors"
"fmt"
"os"
conutils "codeberg.org/danjones000/utils/convids"
"gopkg.in/yaml.v3"
)
var ErrMissingAliases = errors.New("missing aliases")
var ErrMissingShowGrp = errors.New("missing show group")
func NewConfig(path string) (*Config, error) {
//nolint:gosec // I don't care if this is user input
f, err := os.Open(path)
if err != nil {
return nil, err
}
ydec := yaml.NewDecoder(f)
var conf Config
err = ydec.Decode(&conf)
if err != nil {
return nil, err
}
return &conf, nil
}
func AddShows(conf *Config, data *conutils.Data) error {
aliases, ok := conf.Templates["x-aliases"]
reg, anime, err := getAliases(aliases, ok)
if err != nil {
return nil
}
nonEng := (*data.Shows)["non-eng"]
aniShow := (*data.Shows)["anime"]
dor, err := getDorama(nonEng, reg)
if err != nil {
return nil
}
conf.Templates["dorama"] = Template{Series: dor}
aniSer, err := getAnime(aniShow, anime)
if err != nil {
return nil
}
conf.Templates["anime"] = Template{Series: aniSer}
all, err := getAll(data, reg)
if err != nil {
return nil
}
conf.Templates["shows"] = Template{Series: all}
return nil
}
func getAliases(aliases Template, ok bool) (reg, anime *Series, err error) {
if !ok {
err = ErrMissingAliases
return
}
for _, alias := range aliases.Series {
if rF, ok := alias["x-regular"]; ok {
reg = &rF
}
if rA, ok := alias["x-anime"]; ok {
anime = &rA
}
}
if reg == nil {
err = errors.Join(err, fmt.Errorf("%w: %s", ErrMissingAliases, "x-regular"))
}
if anime == nil {
err = errors.Join(err, fmt.Errorf("%w: %s", ErrMissingAliases, "x-anime"))
}
return
}
func getShow(show conutils.Show, sh Series) SeriesGroups {
if !show.Flexget.Begin.IsZero() {
sh.Begin = show.Flexget.Begin
}
sh.AlternameName = show.Flexget.AlternateName
sh.Exact = show.Flexget.Exact
name := cmp.Or(show.Flexget.Name, show.Name, show.Pattern)
return SeriesGroups{name: sh}
}
func getDorama(nonEng *conutils.Shows, tmp *Series) ([]SeriesGroups, error) {
if len(*nonEng) == 0 {
return nil, fmt.Errorf("%w: %s", ErrMissingShowGrp, "non-eng")
}
grp := make([]SeriesGroups, 0, len(*nonEng))
for _, show := range *nonEng {
if !show.Flexget.Skip {
grp = append(grp, getShow(*show, *tmp))
}
}
return grp, nil
}
func getAnime(anime *conutils.Shows, tmp *Series) ([]SeriesGroups, error) {
grp := make([]SeriesGroups, 0, len(*anime))
for _, show := range *anime {
if !show.Flexget.Skip {
grp = append(grp, getShow(*show, *tmp))
}
}
return grp, nil
}
func getAll(data *conutils.Data, tmp *Series) ([]SeriesGroups, error) {
grp := make([]SeriesGroups, 0, 100)
for _, group := range data.Config.Groups {
shGrp, ok := (*data.Shows)[group]
if group == "old" || shGrp == nil || !ok {
continue
}
for _, show := range *shGrp {
if !show.Flexget.Skip && !show.Anime {
grp = append(grp, getShow(*show, *tmp))
}
}
}
return grp, nil
}

38
mkflex/models.go Normal file
View file

@ -0,0 +1,38 @@
package mkflex
import "codeberg.org/danjones000/utils/types"
type Config struct {
Templates map[string]Template `yaml:"templates"`
Tasks map[string]any `yaml:"tasks"`
}
type Template struct {
Series []SeriesGroups `yaml:"series,omitempty"`
Aria2 map[string]any `yaml:"aria2,omitempty"`
ConMag map[string]any `yaml:"convert_magnet,omitempty"`
Download string `yaml:"download,omitempty"`
Notify map[string]any `yaml:"notify,omitempty"`
}
type SeriesGroups map[string]Series
type Identifier string
const (
Sequence Identifier = "sequence"
Episode Identifier = "ep"
)
type Series struct {
Quality string `yaml:"quality,omitempty"`
IdentifiedBy Identifier `yaml:"identified_by,omitempty"`
Begin types.IntOrString `yaml:"begin,omitempty"`
Exact bool `yaml:"exact,omitempty"`
AlternameName []string `yaml:"alternate_name,omitempty"`
}
type Aria struct {
Path string `yaml:"path"`
Server string `yaml:"server"`
}

45
types/yaml.go Normal file
View file

@ -0,0 +1,45 @@
package types
import "gopkg.in/yaml.v3"
type YamlTypeError struct {
Node *yaml.Node
}
func (ye *YamlTypeError) Error() string {
return ye.Node.Tag + " is not a valid type for this field"
}
type IntOrString struct {
intVal int
strVal string
isInt bool
}
func (is IntOrString) MarshalYAML() (any, error) {
if is.isInt {
return is.intVal, nil
}
return is.strVal, nil
}
func (is *IntOrString) UnmarshalYAML(value *yaml.Node) error {
if value.Tag == `!!int` {
is.isInt = true
return value.Decode(&is.intVal)
}
if value.Tag == `!!str` {
is.isInt = false
return value.Decode(&is.strVal)
}
return &YamlTypeError{Node: value}
}
func (is IntOrString) IsZero() bool {
if is.isInt {
return is.intVal == 0
}
return is.strVal == ""
}