daemons separation
Some checks failed
docker / build-docker (push) Failing after 34s

This commit is contained in:
royalcat 2024-11-24 20:32:26 +03:00
parent 98ee1dc6f1
commit 585f317478
61 changed files with 6616 additions and 791 deletions

View file

@ -27,11 +27,11 @@ models:
Torrent:
extraFields:
T:
type: "*git.kmsign.ru/royalcat/tstor/src/daemons/torrent.Controller"
type: "*git.kmsign.ru/royalcat/tstor/daemons/torrent.Controller"
TorrentFile:
extraFields:
F:
type: "*git.kmsign.ru/royalcat/tstor/src/daemons/torrent.FileController"
type: "*git.kmsign.ru/royalcat/tstor/daemons/torrent.FileController"
TorrentPeer:
extraFields:
F:
@ -45,7 +45,7 @@ models:
TorrentFS:
extraFields:
FS:
type: "*git.kmsign.ru/royalcat/tstor/src/daemons/torrent.TorrentFS"
type: "*git.kmsign.ru/royalcat/tstor/daemons/torrent.TorrentFS"
ResolverFS:
extraFields:
FS:

View file

@ -6,6 +6,7 @@ COPY go.mod ./
COPY go.sum ./
RUN --mount=type=cache,mode=0777,target=/go/pkg/mod go mod download all
COPY ./daemons ./daemons
COPY ./pkg ./pkg
COPY ./src ./src
COPY ./cmd ./cmd

View file

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"io/fs"
"log/slog"
"net"
@ -13,13 +14,13 @@ import (
"os/signal"
"syscall"
"git.kmsign.ru/royalcat/tstor/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/daemons/ytdlp"
"git.kmsign.ru/royalcat/tstor/pkg/ctxbilly"
wnfs "git.kmsign.ru/royalcat/tstor/pkg/go-nfs"
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
"git.kmsign.ru/royalcat/tstor/src/config"
"git.kmsign.ru/royalcat/tstor/src/daemons"
"git.kmsign.ru/royalcat/tstor/src/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/src/daemons/ytdlp"
"git.kmsign.ru/royalcat/tstor/src/delivery"
"git.kmsign.ru/royalcat/tstor/src/telemetry"
"git.kmsign.ru/royalcat/tstor/src/vfs"
@ -118,6 +119,10 @@ func run(configPath string) error {
return err
}
vfs.Walk(ctx, sfs, "/", func(path string, info fs.FileInfo, err error) error {
return nil
})
if conf.Mounts.Fuse.Enabled {
mh := fuse.NewHandler(conf.Mounts.Fuse.AllowOther, conf.Mounts.Fuse.Path)
err := mh.Mount(sfs)

View file

@ -0,0 +1,6 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json
package: client
output: client/client.gen.go
generate:
models: true
client: true

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
package client
import (
"encoding/json"
"time"
)
type Timestamp struct {
Time time.Time
}
func (t *Timestamp) UnmarshalJSON(b []byte) error {
var timestamp float64
err := json.Unmarshal(b, &timestamp)
if err != nil {
return err
}
t.Time = time.Unix(int64(timestamp), int64((timestamp-float64(int64(timestamp)))*1e9))
return nil
}

File diff suppressed because it is too large Load diff

42
daemons/kemono/daemon.go Normal file
View file

@ -0,0 +1,42 @@
package kemono
import (
"context"
"fmt"
"git.kmsign.ru/royalcat/tstor/daemons/kemono/client"
)
type Daemon struct {
client client.ClientWithResponsesInterface
}
type creator struct {
Service string
CreatorID string
}
func NewDaemon(server string) (*Daemon, error) {
cl, err := client.NewClientWithResponses(server)
if err != nil {
return nil, err
}
return &Daemon{
client: cl,
}, nil
}
func (d *Daemon) scrapCreator(ctx context.Context, creator creator) error {
resp, err := d.client.GetServiceUserCreatorIdWithResponse(ctx, creator.Service, creator.CreatorID, nil)
if err != nil {
return err
}
for _, post := range *resp.JSON200 {
fmt.Println(post)
}
return nil
}

View file

@ -0,0 +1,22 @@
package kemono_test
import (
"context"
"fmt"
"testing"
"git.kmsign.ru/royalcat/tstor/daemons/kemono"
)
func TestScrapCreator(t *testing.T) {
k := kemono.NewKemono("https://coomer.su/")
ctx := context.Background()
posts, err := k.FetchPosts(ctx, "onlyfans", "bigtittygothegg")
if err != nil {
t.Fatal(err)
}
for _, post := range posts {
fmt.Println(post)
}
}

69
daemons/kemono/fetch.go Normal file
View file

@ -0,0 +1,69 @@
package kemono
import (
"context"
"encoding/json"
"github.com/carlmjohnson/requests"
)
// FetchCreators fetch Creator list
func (k *kemono) FetchCreators() (creators []Creator, err error) {
// k.log.Print("fetching creator list...")
// url := fmt.Sprintf("https://%s/api/v1/creators", k.Site)
// resp, err := k.Downloader.Get(url)
// if err != nil {
// return nil, fmt.Errorf("fetch creator list error: %s", err)
// }
// reader, err := handleCompressedHTTPResponse(resp)
// if err != nil {
// return nil, err
// }
// data, err := ioutil.ReadAll(reader)
// if err != nil {
// return nil, fmt.Errorf("fetch creator list error: %s", err)
// }
// err = json.Unmarshal(data, &creators)
// if err != nil {
// return nil, fmt.Errorf("unmarshal creator list error: %s", err)
// }
return
}
// FetchPosts fetch post list
func (k *kemono) FetchPosts(ctx context.Context, service, id string) (posts []Post, err error) {
const perUnit = 50
page := 0
for {
k.log.Printf("fetching post list page %d...", page)
var ps []json.RawMessage
err = requests.URL(k.Site).
Pathf("/api/v1/%s/user/%s", service, id).
ParamInt("o", page*perUnit).
Client(k.client).
CheckStatus(200).
ToJSON(&ps).
Fetch(ctx)
if err != nil {
return nil, err
}
if len(ps) == 0 {
break
}
for _, raw := range ps {
var p Post
err := json.Unmarshal(raw, &p)
if err != nil {
return posts, nil
}
}
}
return posts, nil
}

32
daemons/kemono/go.mod Normal file
View file

@ -0,0 +1,32 @@
module git.kmsign.ru/royalcat/tstor/daemons/kemono
go 1.23.3
require (
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1
github.com/oapi-codegen/runtime v1.1.1
github.com/spf13/cast v1.7.0
)
require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/carlmjohnson/requests v0.24.2 // indirect
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
github.com/getkin/kin-openapi v0.127.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

189
daemons/kemono/go.sum Normal file
View file

@ -0,0 +1,189 @@
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso=
github.com/carlmjohnson/requests v0.24.2/go.mod h1:duYA/jDnyZ6f3xbcF5PpZ9N8clgopubP2nK5i6MVMhU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58=
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w=
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY=
github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/speakeasy-api/openapi-overlay v0.9.0 h1:Wrz6NO02cNlLzx1fB093lBlYxSI54VRhy1aSutx0PQg=
github.com/speakeasy-api/openapi-overlay v0.9.0/go.mod h1:f5FloQrHA7MsxYg9djzMD5h6dxrHjVVByWKh7an8TRc=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk=
github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

436
daemons/kemono/kemono.go Normal file
View file

@ -0,0 +1,436 @@
package kemono
import (
"log"
"net/http"
"os"
"time"
)
type Downloader interface {
Download(<-chan FileWithIndex, Creator, Post) <-chan error
Get(url string) (resp *http.Response, err error)
WriteContent(Creator, Post, string) error
}
type Log interface {
Printf(format string, v ...interface{})
Print(s string)
}
type DefaultLog struct {
log *log.Logger
}
func (d *DefaultLog) Printf(format string, v ...interface{}) {
d.log.Printf(format, v...)
}
func (d *DefaultLog) Print(s string) {
d.log.Print(s)
}
// Filter return true for continue, false for skip
type CreatorFilter func(i int, post Creator) bool
type PostFilter func(i int, post Post) bool
type AttachmentFilter func(i int, attachment File) bool
type Option func(*kemono)
type kemono struct {
// kemono or coomer ...
Site string
//download Banner
Banner bool
// All Creator
creators []Creator
// Creator filter
creatorFilters []CreatorFilter
// Post filter map[creator(<service>:<id>)][]PostFilter
postFilters map[string][]PostFilter
// Attachment filter map[creator(<service>:<id>)][]AttachmentFilter
attachmentFilters map[string][]AttachmentFilter
// Select a specific creator
// If not specified, all creators will be selected
users []Creator
client *http.Client
log Log
retry int
retryInterval time.Duration
}
func NewKemono(site string) *kemono {
k := &kemono{
Site: site,
Banner: true,
postFilters: make(map[string][]PostFilter),
attachmentFilters: make(map[string][]AttachmentFilter),
retry: 3,
retryInterval: 5 * time.Second,
}
if k.log == nil {
k.log = &DefaultLog{log: log.New(os.Stdout, "", log.LstdFlags)}
}
return k
}
// WithDomain Set Site
// func WithDomain(web string) Option {
// return func(k *kemono) {
// k.Site = web
// }
// }
// func WithBanner(banner bool) Option {
// return func(k *kemono) {
// k.Banner = banner
// }
// }
// Custom the creator list
// func WithCreators(creators []Creator) Option {
// return func(k *kemono) {
// k.creators = creators
// }
// }
// WithUsers Select a specific creator, if not specified, all creators will be selected
// func WithUsers(user ...Creator) Option {
// return func(k *kemono) {
// for _, u := range user {
// exist := false
// for _, c := range k.users {
// if c.Service == u.Service && c.Id == u.Id {
// exist = true
// break
// }
// }
// if !exist {
// k.users = append(k.users, u)
// }
// }
// }
// }
// WithUsersPair Select a specific creator, if not specified, all creators will be selected
// func WithUsersPair(serviceIdPairs ...string) Option {
// return func(k *kemono) {
// if len(serviceIdPairs)%2 != 0 {
// k.log.Printf("serviceIdPairs length must be even")
// return
// }
// for i := 0; i < len(serviceIdPairs); i += 2 {
// exist := false
// for _, c := range k.users {
// if c.Service == serviceIdPairs[i] && c.Id == serviceIdPairs[i+1] {
// exist = true
// break
// }
// }
// if !exist {
// k.users = append(k.users, NewCreator(serviceIdPairs[i], serviceIdPairs[i+1]))
// }
// }
// }
// }
// SetLog set log
func SetLog(log Log) Option {
return func(k *kemono) {
k.log = log
}
}
// // SetRetry set retry
// func SetRetry(retry int) Option {
// return func(k *kemono) {
// k.retry = retry
// }
// }
// // SetRetryInterval set retry interval
// func SetRetryInterval(retryInterval time.Duration) Option {
// return func(k *kemono) {
// k.retryInterval = retryInterval
// }
// }
// // WithCreatorFilter Creator filter
// func WithCreatorFilter(filter ...CreatorFilter) Option {
// return func(k *kemono) {
// k.addCreatorFilter(filter...)
// }
// }
// // WithPostFilter Post filter
// func WithPostFilter(filter ...PostFilter) Option {
// return func(k *kemono) {
// k.addPostFilter(filter...)
// }
// }
// func WithUserPostFilter(creator Creator, filter ...PostFilter) Option {
// return func(k *kemono) {
// k.addUserPostFilter(creator.PairString(), filter...)
// }
// }
// // WithAttachmentFilter Attachment filter
// func WithAttachmentFilter(filter ...AttachmentFilter) Option {
// return func(k *kemono) {
// k.addAttachmentFilter(filter...)
// }
// }
// func WithUserAttachmentFilter(creator Creator, filter ...AttachmentFilter) Option {
// return func(k *kemono) {
// k.addUserAttachmentFilter(creator.PairString(), filter...)
// }
// }
// // Start fetch and download
// func (k *kemono) Start() error {
// // initialize the creators
// if len(k.creators) == 0 {
// // fetch creators from kemono
// cs, err := k.FetchCreators()
// if err != nil {
// return err
// }
// k.creators = cs
// }
// //find creators
// if len(k.users) != 0 {
// var creators []Creator
// for _, user := range k.users {
// c, ok := FindCreator(k.creators, user.Id, user.Service)
// if !ok {
// k.log.Printf("Creator %s:%s not found", user.Service, user.Id)
// continue
// }
// creators = append(creators, c)
// }
// k.users = creators
// } else {
// k.users = k.creators
// }
// // Filter selected creators
// k.users = k.FilterCreators(k.users)
// // start download
// k.log.Printf("Start download %d creators", len(k.users))
// for _, creator := range k.users {
// // fetch posts
// posts, err := k.FetchPosts(creator.Service, creator.Id)
// if err != nil {
// return err
// }
// // filter posts
// posts = k.FilterPosts(posts)
// // filter attachments
// for i, post := range posts {
// // download banner if banner is true or file is not image
// if (k.Banner || !isImage(filepath.Ext(post.File.Name))) && post.File.Path != "" {
// res := make([]File, len(post.Attachments)+1)
// copy(res[1:], post.Attachments)
// res[0] = post.File
// post.Attachments = res
// }
// posts[i].Attachments = k.FilterAttachments(fmt.Sprintf("%s:%s", post.Service, post.User), post.Attachments)
// }
// // download posts
// err = k.DownloadPosts(creator, posts)
// if err != nil {
// return err
// }
// }
// return nil
// }
// func (k *kemono) addCreatorFilter(filter ...CreatorFilter) {
// k.creatorFilters = append(k.creatorFilters, filter...)
// }
// func (k *kemono) addPostFilter(filter ...PostFilter) {
// k.postFilters["*"] = append(k.postFilters["*"], filter...)
// }
// func (k *kemono) addUserPostFilter(user string, filter ...PostFilter) {
// k.postFilters[user] = append(k.postFilters[user], filter...)
// }
// func (k *kemono) addAttachmentFilter(filter ...AttachmentFilter) {
// k.attachmentFilters["*"] = append(k.attachmentFilters["*"], filter...)
// }
// func (k *kemono) addUserAttachmentFilter(user string, filter ...AttachmentFilter) {
// k.attachmentFilters[user] = append(k.attachmentFilters[user], filter...)
// }
// func (k *kemono) filterCreator(i int, creator Creator) bool {
// for _, filter := range k.creatorFilters {
// if !filter(i, creator) {
// return false
// }
// }
// return true
// }
// func (k *kemono) filterPost(i int, post Post) bool {
// for _, filter := range k.postFilters["*"] {
// if !filter(i, post) {
// return false
// }
// }
// for _, filter := range k.postFilters[fmt.Sprintf("%s:%s", post.Service, post.User)] {
// if !filter(i, post) {
// return false
// }
// }
// return true
// }
// func (k *kemono) filterAttachment(user string, i int, attachment File) bool {
// for _, filter := range k.attachmentFilters["*"] {
// if !filter(i, attachment) {
// return false
// }
// }
// for _, filter := range k.attachmentFilters[user] {
// if !filter(i, attachment) {
// return false
// }
// }
// return true
// }
// func (k *kemono) FilterCreators(creators []Creator) []Creator {
// var filteredCreators []Creator
// for i, creator := range creators {
// if k.filterCreator(i, creator) {
// filteredCreators = append(filteredCreators, creator)
// }
// }
// return filteredCreators
// }
// func (k *kemono) FilterPosts(posts []Post) []Post {
// var filteredPosts []Post
// for i, post := range posts {
// if k.filterPost(i, post) {
// filteredPosts = append(filteredPosts, post)
// }
// }
// return filteredPosts
// }
// func (k *kemono) FilterAttachments(user string, attachments []File) []File {
// var filteredAttachments []File
// for i, attachment := range attachments {
// if k.filterAttachment(user, i, attachment) {
// filteredAttachments = append(filteredAttachments, attachment)
// }
// }
// return filteredAttachments
// }
// // ReleaseDateFilter A Post filter that filters posts with release date
// func ReleaseDateFilter(from, to time.Time) PostFilter {
// return func(i int, post Post) bool {
// return post.Published.After(from) && post.Published.Before(to)
// }
// }
// // ReleaseDateAfterFilter A Post filter that filters posts with release date after
// func ReleaseDateAfterFilter(from time.Time) PostFilter {
// return func(i int, post Post) bool {
// return post.Published.After(from)
// }
// }
// // ReleaseDateBeforeFilter A Post filter that filters posts with release date before
// func ReleaseDateBeforeFilter(to time.Time) PostFilter {
// return func(i int, post Post) bool {
// return post.Published.Before(to)
// }
// }
// // EditDateFilter A Post filter that filters posts with edit date
// func EditDateFilter(from, to time.Time) PostFilter {
// return func(i int, post Post) bool {
// return post.Edited.After(from) && post.Edited.Before(to)
// }
// }
// // EditDateAfterFilter A Post filter that filters posts with edit date after
// func EditDateAfterFilter(from time.Time) PostFilter {
// return func(i int, post Post) bool {
// return post.Edited.After(from)
// }
// }
// // EditDateBeforeFilter A Post filter that filters posts with edit date before
// func EditDateBeforeFilter(to time.Time) PostFilter {
// return func(i int, post Post) bool {
// return post.Edited.Before(to)
// }
// }
// func IdFilter(ids ...string) PostFilter {
// return func(i int, post Post) bool {
// for _, id := range ids {
// if id == post.Id {
// return true
// }
// }
// return false
// }
// }
// // NumbFilter A Post filter that filters posts with a specific number
// func NumbFilter(f func(i int) bool) PostFilter {
// return func(i int, post Post) bool {
// return f(i)
// }
// }
// // ExtensionFilter A attachmentFilter filter that filters attachments with a specific extension
// func ExtensionFilter(extension ...string) AttachmentFilter {
// return func(i int, attachment File) bool {
// ext := filepath.Ext(attachment.Name)
// for _, e := range extension {
// if ext == e {
// return true
// }
// }
// return false
// }
// }
// // ExtensionExcludeFilter A attachmentFilter filter that filters attachments without a specific extension
// func ExtensionExcludeFilter(extension ...string) AttachmentFilter {
// return func(i int, attachment File) bool {
// ext := filepath.Ext(attachment.Name)
// for _, e := range extension {
// if ext == e {
// return false
// }
// }
// return true
// }
// }

10
daemons/kemono/tools.go Normal file
View file

@ -0,0 +1,10 @@
//go:build tools
// +build tools
package main
import (
_ "github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen"
)
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest -config client.cfg.yaml client/spec.json

201
daemons/kemono/types.go Normal file
View file

@ -0,0 +1,201 @@
package kemono
import (
"encoding/json"
"fmt"
"net/url"
"path/filepath"
"time"
"github.com/spf13/cast"
)
type Timestamp struct {
Time time.Time
}
func (t *Timestamp) UnmarshalJSON(b []byte) error {
var timestamp float64
err := json.Unmarshal(b, &timestamp)
if err != nil {
return err
}
t.Time = time.Unix(int64(timestamp), int64((timestamp-float64(int64(timestamp)))*1e9))
return nil
}
type Creator struct {
Favorited int `json:"favorited"`
Id string `json:"id"`
Indexed Timestamp `json:"indexed"`
Name string `json:"name"`
Service string `json:"service"`
Updated Timestamp `json:"updated"`
}
// GetID get creator id
func (c Creator) GetID() string {
return c.Id
}
// GetService get creator Service
func (c Creator) GetService() string {
return c.Service
}
func (c Creator) PairString() string {
return fmt.Sprintf("%s:%s", c.Service, c.Id)
}
func NewCreator(service, id string) Creator {
return Creator{
Service: service,
Id: id,
}
}
// FindCreator Get the Creator by ID and Service
func FindCreator(creators []Creator, id, service string) (Creator, bool) {
for _, creator := range creators {
if creator.Id == id && creator.Service == service {
return creator, true
}
}
return Creator{}, false
}
type File struct {
Name string `json:"name"`
Path string `json:"path"`
}
// GetURL return the url
func (f File) GetURL() string {
ext := filepath.Ext(f.Name)
name := f.Name[:len(f.Name)-len(ext)]
return fmt.Sprintf("%s?f=%s%s", f.Path, url.QueryEscape(name), ext)
}
// GetHash get hash from file path
func (f File) GetHash() (string, error) {
return SplitHash(f.Path)
}
func (f File) Index(n int) FileWithIndex {
return FileWithIndex{
Index: n,
File: f,
}
}
type FileWithIndex struct {
Index int
File
}
type Post struct {
ID string `json:"id,omitempty"`
Added time.Time `json:"added,omitempty"`
Attachments []Attachment `json:"attachments,omitempty"`
Author Author `json:"author,omitempty"`
Channel string `json:"channel,omitempty"`
Content string `json:"content,omitempty"`
Edited time.Time `json:"edited,omitempty"`
Embeds []any `json:"embeds,omitempty"`
Mentions []any `json:"mentions,omitempty"`
Published time.Time `json:"published,omitempty"`
Server string `json:"server,omitempty"`
}
type Attachment struct {
Name string `json:"name,omitempty"`
Path string `json:"path,omitempty"`
}
type Author struct {
ID string `json:"id,omitempty"`
Avatar string `json:"avatar,omitempty"`
Discriminator string `json:"discriminator,omitempty"`
PublicFlags int64 `json:"public_flags,omitempty"`
Username string `json:"username,omitempty"`
}
func (p *Post) UnmarshalJSON(data []byte) error {
var pr struct {
Added string `json:"added"`
Attachments []File `json:"attachments"`
Content string `json:"content"`
Edited string `json:"edited"`
Embed interface{} `json:"embed"`
File File `json:"file"`
Id string `json:"id"`
Published string `json:"published"`
Service string `json:"service"`
SharedFile bool `json:"shared_file"`
Title string `json:"title"`
User string `json:"user"`
}
err := json.Unmarshal(data, &pr)
if err != nil {
return err
}
// TODO
p.Added, err = cast.StringToDate(pr.Added)
if err != nil {
return fmt.Errorf("parse added time error: %s", err)
}
p.Edited, err = cast.StringToDate(pr.Edited)
if err != nil {
return fmt.Errorf("parse edited time error: %s", err)
}
p.Published, err = cast.StringToDate(pr.Published)
if err != nil {
return fmt.Errorf("parse published time error: %s", err)
}
p.ID = pr.Id
p.Service = pr.Service
return nil
}
// User a creator according to the service and id
type User struct {
Service string `json:"service"`
Id string `json:"id"`
}
// GetID get user id
func (u User) GetID() string {
return u.Id
}
// GetService get user Service
func (u User) GetService() string {
return u.Service
}
type FavoriteCreator struct {
FavedSeq int `json:"faved_seq"`
Id string `json:"id"`
Index string `json:"index"`
Name string `json:"name"`
Service string `json:"service"`
Update string `json:"update"`
}
var SiteMap = map[string]string{
"patreon": "kemono",
"fanbox": "kemono",
"gumroad": "kemono",
"subscribestar": "kemono",
"dlsite": "kemono",
"discord": "kemono",
"fantia": "kemono",
"boosty": "kemono",
"afdian": "kemono",
"onlyfans": "coomer",
"fansly": "coomer",
}

25
daemons/kemono/utils.go Normal file
View file

@ -0,0 +1,25 @@
package kemono
import (
"path/filepath"
"strings"
)
func isImage(ext string) bool {
switch ext {
case ".apng", ".avif", ".bmp", ".gif", ".ico", ".cur", ".jpg", ".jpeg", ".jfif", ".pjpeg", ".pjp", ".png", ".svg", ".tif", ".tiff", ".webp", ".jpe":
return true
default:
return false
}
}
func SplitHash(str string) (string, error) {
parts := strings.Split(str, "/")
if len(parts) < 4 {
return "", nil
}
ext := filepath.Ext(parts[3])
name := parts[3][:len(parts[3])-len(ext)]
return name, nil
}

View file

@ -12,7 +12,7 @@ import (
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
)
func (d *Daemon) Cleanup(ctx context.Context, dryRun bool) ([]string, error) {
func (d *Daemon) Cleanup(ctx context.Context, run bool) ([]string, error) {
d.log.Info(ctx, "cleanup started")
torrentInfos, err := d.client.qb.Torrent().GetTorrents(ctx, &qbittorrent.TorrentOption{})
@ -37,7 +37,7 @@ func (d *Daemon) Cleanup(ctx context.Context, dryRun bool) ([]string, error) {
slog.Any("infohashes", torrentToDelete),
)
if dryRun {
if !run {
d.log.Info(ctx, "dry run, skipping deletion")
return torrentToDelete, nil
}

View file

@ -26,7 +26,7 @@ import (
"go.opentelemetry.io/otel"
)
var trace = otel.Tracer("git.kmsign.ru/royalcat/tstor/src/daemons/qbittorrent")
var trace = otel.Tracer("git.kmsign.ru/royalcat/tstor/daemons/qbittorrent")
type Daemon struct {
proc *os.Process
@ -231,6 +231,9 @@ func (d *Daemon) syncTorrentState(ctx context.Context, file vfs.File, ih metainf
if err == nil {
break
}
if errors.Is(err, context.DeadlineExceeded) {
return err
}
log.Error(ctx, "waiting for torrent to be added", rlog.Error(err))
time.Sleep(time.Millisecond * 15)
}

View file

@ -1,5 +1,5 @@
type QBitTorrentDaemonMutation {
cleanup(dryRun: Boolean!): QBitCleanupResponse! @resolver
cleanup(run: Boolean!): QBitCleanupResponse! @resolver
}
type QBitCleanupResponse {

View file

@ -3,8 +3,8 @@ package daemons
import (
"context"
"git.kmsign.ru/royalcat/tstor/src/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/src/daemons/ytdlp"
"git.kmsign.ru/royalcat/tstor/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/daemons/ytdlp"
"git.kmsign.ru/royalcat/tstor/src/vfs"
)

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@ package model
import (
"context"
"git.kmsign.ru/royalcat/tstor/src/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/src/vfs"
)

View file

@ -3,7 +3,7 @@ package model
import (
"context"
"git.kmsign.ru/royalcat/tstor/src/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
atorrent "github.com/anacrolix/torrent"
)

View file

@ -5,7 +5,7 @@ package model
import (
"time"
"git.kmsign.ru/royalcat/tstor/src/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/src/vfs"
torrent1 "github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/types"

View file

@ -12,8 +12,8 @@ import (
)
// Cleanup is the resolver for the cleanup field.
func (r *qBitTorrentDaemonMutationResolver) Cleanup(ctx context.Context, obj *model.QBitTorrentDaemonMutation, dryRun bool) (*model.QBitCleanupResponse, error) {
hahses, err := r.QBitTorrentDaemon.Cleanup(ctx, dryRun)
func (r *qBitTorrentDaemonMutationResolver) Cleanup(ctx context.Context, obj *model.QBitTorrentDaemonMutation, run bool) (*model.QBitCleanupResponse, error) {
hahses, err := r.QBitTorrentDaemon.Cleanup(ctx, run)
if err != nil {
return nil, err
}

View file

@ -1,8 +1,8 @@
package resolver
import (
"git.kmsign.ru/royalcat/tstor/src/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/src/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/src/vfs"
"github.com/go-git/go-billy/v5"
)

View file

@ -10,7 +10,7 @@ import (
"strings"
"time"
"git.kmsign.ru/royalcat/tstor/src/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/model"
tinfohash "github.com/anacrolix/torrent/types/infohash"

View file

@ -5,10 +5,10 @@ import (
"log/slog"
"net/http"
"git.kmsign.ru/royalcat/tstor/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
"git.kmsign.ru/royalcat/tstor/src/config"
"git.kmsign.ru/royalcat/tstor/src/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/src/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/src/vfs"
echopprof "github.com/labstack/echo-contrib/pprof"
"github.com/labstack/echo/v4"

View file

@ -4,8 +4,8 @@ import (
"context"
"net/http"
"git.kmsign.ru/royalcat/tstor/src/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/src/daemons/torrent"
"git.kmsign.ru/royalcat/tstor/daemons/qbittorrent"
"git.kmsign.ru/royalcat/tstor/daemons/torrent"
graph "git.kmsign.ru/royalcat/tstor/src/delivery/graphql"
"git.kmsign.ru/royalcat/tstor/src/delivery/graphql/resolver"
"git.kmsign.ru/royalcat/tstor/src/vfs"

View file

@ -57,8 +57,12 @@ interface Progress {
current: Int!
total: Int!
}
type QBitCleanupResponse {
count: Int!
hashes: [String!]!
}
type QBitTorrentDaemonMutation {
cleanup(dryRun: Boolean!): Int! @resolver
cleanup(run: Boolean!): QBitCleanupResponse! @resolver
}
type QBitTorrentDaemonQuery {
torrents: [QTorrent!]! @resolver