first version themoviedb export
This commit is contained in:
commit
bfe5550739
|
@ -0,0 +1 @@
|
|||
*.md
|
|
@ -0,0 +1,34 @@
|
|||
module codeberg.org/ewintr/scripts/tmdb-export
|
||||
|
||||
go 1.21.5
|
||||
|
||||
require (
|
||||
github.com/cyruzin/golang-tmdb v1.6.1
|
||||
github.com/erikgeiser/promptkit v0.9.0
|
||||
github.com/muesli/termenv v0.15.2
|
||||
)
|
||||
|
||||
require (
|
||||
code.ewintr.nl/go-kit v0.0.0-20240308074309-a1328c3c44c6 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/bubbles v0.16.1 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.24.2 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.7.1 // indirect
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/term v0.11.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
|
@ -0,0 +1,71 @@
|
|||
code.ewintr.nl/go-kit v0.0.0-20240308074309-a1328c3c44c6 h1:qZwAicZOd18o9qWCU3pSj/QnrSNLCAMAOPJvyLgBQ0U=
|
||||
code.ewintr.nl/go-kit v0.0.0-20240308074309-a1328c3c44c6/go.mod h1:Yk8Mdn1f4/L9tcymurtItqevVkac6P9ljh2Sd3T+FS8=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY=
|
||||
github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
|
||||
github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY=
|
||||
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
|
||||
github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
|
||||
github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/cyruzin/golang-tmdb v1.6.1 h1:LH0MDPYDb2TEZH3dfNjQGgH6mIlWt2fzpjnnTk+jifA=
|
||||
github.com/cyruzin/golang-tmdb v1.6.1/go.mod h1:ZSryJLCcY+9TiKU+LbouXKns++YBrM8Tizannr05c+I=
|
||||
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/erikgeiser/promptkit v0.9.0 h1:3qL1mS/ntCrXdb8sTP/ka82CJ9kEQaGuYXNrYJkWYBc=
|
||||
github.com/erikgeiser/promptkit v0.9.0/go.mod h1:pU9dtogSe3Jlc2AY77EP7R4WFP/vgD4v+iImC83KsCo=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
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/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
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.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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=
|
|
@ -0,0 +1,112 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/erikgeiser/promptkit/selection"
|
||||
"github.com/erikgeiser/promptkit/textinput"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
const (
|
||||
promptTemplate = `
|
||||
{{- if .Prompt -}}
|
||||
{{ Bold .Prompt }}
|
||||
{{ end -}}
|
||||
{{ if .IsFiltered }}
|
||||
{{- print .FilterPrompt " " .FilterInput }}
|
||||
{{ end }}
|
||||
|
||||
{{- range $i, $choice := .Choices }}
|
||||
{{- if IsScrollUpHintPosition $i }}
|
||||
{{- print "⇡ " -}}
|
||||
{{- else if IsScrollDownHintPosition $i -}}
|
||||
{{- print "⇣ " -}}
|
||||
{{- else -}}
|
||||
{{- print " " -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if eq $.SelectedIndex $i }}
|
||||
{{- print "[" (Foreground "32" (Bold "x")) "] " (Selected $choice) "\n" }}
|
||||
{{- else }}
|
||||
{{- print "[ ] " (Unselected $choice) "\n" }}
|
||||
{{- end }}
|
||||
{{- end}}
|
||||
`
|
||||
resultTemplate = `
|
||||
{{- print .Prompt " " (Foreground "32" (name .FinalChoice)) "\n" -}}
|
||||
`
|
||||
)
|
||||
|
||||
type Movie struct {
|
||||
TMDBID string
|
||||
IMDBID string
|
||||
Title string
|
||||
EnglishTitle string
|
||||
Year int
|
||||
Directors []string
|
||||
Summary string
|
||||
}
|
||||
|
||||
func main() {
|
||||
tmdb, err := NewTMDB(os.Getenv("TMDB_API_KEY"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
input := textinput.New("TheMovieDB search term:")
|
||||
//input.InitialValue = os.Getenv("USER")
|
||||
//input.Placeholder = "Your name cannot be empty"
|
||||
|
||||
searchTerm, err := input.RunPrompt()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
movies, err := tmdb.Search(searchTerm)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(movies) == 0 {
|
||||
fmt.Println("No results found")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
blue := termenv.String().Foreground(termenv.ANSI256Color(32)) //nolint:gomnd
|
||||
|
||||
sp := selection.New("Choose a movie!", movies)
|
||||
sp.FilterPrompt = "Filter by ID:"
|
||||
sp.FilterPlaceholder = "Type to filter"
|
||||
sp.PageSize = 3
|
||||
sp.LoopCursor = true
|
||||
sp.Filter = func(filter string, choice *selection.Choice[Movie]) bool {
|
||||
return strings.HasPrefix(choice.Value.TMDBID, filter)
|
||||
}
|
||||
sp.Template = promptTemplate
|
||||
sp.ResultTemplate = resultTemplate
|
||||
sp.SelectedChoiceStyle = func(c *selection.Choice[Movie]) string {
|
||||
return fmt.Sprintf("%s (%s)", blue.Bold().Styled(c.Value.Title), termenv.String(fmt.Sprintf("%d", c.Value.Year)).Faint().String())
|
||||
}
|
||||
sp.UnselectedChoiceStyle = func(c *selection.Choice[Movie]) string {
|
||||
return fmt.Sprintf("%s (%s)", c.Value.Title, termenv.String(fmt.Sprintf("%d", c.Value.Year)).Faint().String())
|
||||
}
|
||||
sp.ExtendedTemplateFuncs = map[string]interface{}{
|
||||
"name": func(c *selection.Choice[Movie]) string { return c.Value.Title },
|
||||
}
|
||||
|
||||
choice, err := sp.RunPrompt()
|
||||
if err != nil {
|
||||
fmt.Printf("could not select: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := Export(choice); err != nil {
|
||||
fmt.Printf("could not export to markdown: %v\n", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
markdownTemplate = `---
|
||||
tmdb: {{ .TMDBID }}
|
||||
emdb: {{ .IMDBID }}
|
||||
englishTitle: {{ .EnglishTitle }}
|
||||
title: {{ .Title }}
|
||||
year: {{ .Year }}
|
||||
directors: {{ .DirectorsYAML }}
|
||||
inCollection: no
|
||||
watchedOn:
|
||||
rating:
|
||||
---
|
||||
|
||||
# {{ .EnglishTitle }} ({{ .Year }})
|
||||
|
||||
Director(s): {{ .Directors }}
|
||||
|
||||
{{ .Summary }}
|
||||
|
||||
## Comment
|
||||
`
|
||||
)
|
||||
|
||||
func Export(movie Movie) error {
|
||||
tpl, err := template.New("page").Parse(markdownTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("%s (%d).md", movie.EnglishTitle, movie.Year)
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := struct {
|
||||
TMDBID string
|
||||
IMDBID string
|
||||
EnglishTitle string
|
||||
Title string
|
||||
Year int
|
||||
DirectorsYAML string
|
||||
Directors string
|
||||
Summary string
|
||||
}{
|
||||
TMDBID: movie.TMDBID,
|
||||
IMDBID: movie.IMDBID,
|
||||
EnglishTitle: movie.EnglishTitle,
|
||||
Title: movie.Title,
|
||||
Year: movie.Year,
|
||||
DirectorsYAML: strings.Join(movie.Directors, ", "),
|
||||
Directors: fmt.Sprintf("[[%s]]", strings.Join(movie.Directors, "]], [[")),
|
||||
Summary: movie.Summary,
|
||||
}
|
||||
|
||||
if err := tpl.Execute(f, data); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
tmdb "github.com/cyruzin/golang-tmdb"
|
||||
)
|
||||
|
||||
type TMDB struct {
|
||||
c *tmdb.Client
|
||||
}
|
||||
|
||||
func NewTMDB(apikey string) (*TMDB, error) {
|
||||
tmdbClient, err := tmdb.Init(apikey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tmdbClient.SetClientAutoRetry()
|
||||
tmdbClient.SetAlternateBaseURL()
|
||||
|
||||
return &TMDB{
|
||||
c: tmdbClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t TMDB) Search(query string) ([]Movie, error) {
|
||||
results, err := t.c.GetSearchMovies(query, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
movies := make([]Movie, len(results.Results))
|
||||
for i, result := range results.Results {
|
||||
movies[i], err = t.GetMovie(result.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return movies, nil
|
||||
}
|
||||
|
||||
func (t TMDB) GetMovie(id int64) (Movie, error) {
|
||||
result, err := t.c.GetMovieDetails(int(id), map[string]string{
|
||||
"append_to_response": "credits",
|
||||
})
|
||||
if err != nil {
|
||||
return Movie{}, err
|
||||
}
|
||||
|
||||
var year int
|
||||
if release, err := time.Parse("2006-01-02", result.ReleaseDate); err == nil {
|
||||
year = release.Year()
|
||||
}
|
||||
|
||||
directors := make([]string, 0)
|
||||
for crew := range result.Credits.Crew {
|
||||
if result.Credits.Crew[crew].Job == "Director" {
|
||||
directors = append(directors, result.Credits.Crew[crew].Name)
|
||||
}
|
||||
}
|
||||
|
||||
return Movie{
|
||||
Title: result.OriginalTitle,
|
||||
EnglishTitle: result.Title,
|
||||
TMDBID: fmt.Sprintf("%d", result.ID),
|
||||
IMDBID: result.IMDbID,
|
||||
Year: year,
|
||||
Directors: directors,
|
||||
Summary: result.Overview,
|
||||
}, nil
|
||||
|
||||
}
|
Loading…
Reference in New Issue