Make the GET /api/jobs/types
endpoint work
This commit is contained in:
parent
6520dc2d66
commit
d0fafb5063
@ -49,7 +49,6 @@ func main() {
|
|||||||
|
|
||||||
log.Info().Str("version", appinfo.ApplicationVersion).Msgf("starting %v", appinfo.ApplicationName)
|
log.Info().Str("version", appinfo.ApplicationVersion).Msgf("starting %v", appinfo.ApplicationName)
|
||||||
|
|
||||||
gojaPoC()
|
|
||||||
echoOpenAPIPoC()
|
echoOpenAPIPoC()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +112,12 @@ func echoOpenAPIPoC() {
|
|||||||
})
|
})
|
||||||
e.Use(validator)
|
e.Use(validator)
|
||||||
|
|
||||||
flamenco := api_impl.NewFlamenco()
|
compiler, err := job_compilers.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("error loading job compilers")
|
||||||
|
}
|
||||||
|
|
||||||
|
flamenco := api_impl.NewFlamenco(compiler)
|
||||||
api.RegisterHandlers(e, flamenco)
|
api.RegisterHandlers(e, flamenco)
|
||||||
|
|
||||||
// Log available routes
|
// Log available routes
|
||||||
|
@ -27,12 +27,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Flamenco struct {
|
type Flamenco struct {
|
||||||
|
jobCompiler JobCompiler
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobCompiler interface {
|
||||||
|
ListJobTypes() api.AvailableJobTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ api.ServerInterface = (*Flamenco)(nil)
|
var _ api.ServerInterface = (*Flamenco)(nil)
|
||||||
|
|
||||||
func NewFlamenco() *Flamenco {
|
// NewFlamenco creates a new Flamenco service, using the given JobCompiler.
|
||||||
return &Flamenco{}
|
func NewFlamenco(jc JobCompiler) *Flamenco {
|
||||||
|
return &Flamenco{
|
||||||
|
jobCompiler: jc,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendPetstoreError wraps sending of an error in the Error format, and
|
// sendPetstoreError wraps sending of an error in the Error format, and
|
||||||
|
@ -24,52 +24,15 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"gitlab.com/blender/flamenco-goja-test/pkg/api"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (f *Flamenco) GetJobTypes(e echo.Context) error {
|
func (f *Flamenco) GetJobTypes(e echo.Context) error {
|
||||||
// Some helper functions because Go doesn't allow taking the address of a literal.
|
if f.jobCompiler == nil {
|
||||||
defaultString := func(s string) *interface{} {
|
log.Error().Msg("Flamenco is running without job compiler")
|
||||||
var iValue interface{} = s
|
return sendAPIError(e, http.StatusInternalServerError, "no job types available")
|
||||||
return &iValue
|
|
||||||
}
|
|
||||||
defaultInt32 := func(i int32) *interface{} {
|
|
||||||
var iValue interface{} = i
|
|
||||||
return &iValue
|
|
||||||
}
|
|
||||||
defaultBool := func(b bool) *interface{} {
|
|
||||||
var iValue interface{} = b
|
|
||||||
return &iValue
|
|
||||||
}
|
|
||||||
boolPtr := func(b bool) *bool {
|
|
||||||
return &b
|
|
||||||
}
|
|
||||||
choicesStr := func(choices ...string) *[]string {
|
|
||||||
return &choices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: dynamically build based on the actually registered job types.
|
jobTypes := f.jobCompiler.ListJobTypes()
|
||||||
types := api.AvailableJobTypes{
|
return e.JSON(http.StatusOK, &jobTypes)
|
||||||
JobTypes: []api.AvailableJobType{{
|
|
||||||
Name: "simple-blender-render",
|
|
||||||
Settings: []api.AvailableJobSetting{
|
|
||||||
{Key: "blender_cmd", Type: "string", Default: defaultString("{blender}")},
|
|
||||||
{Key: "chunk_size", Type: "int32", Default: defaultInt32(1)},
|
|
||||||
{Key: "frames", Type: "string", Required: boolPtr(true)},
|
|
||||||
{Key: "render_output", Type: "string", Required: boolPtr(true)},
|
|
||||||
{Key: "fps", Type: "int32"},
|
|
||||||
{Key: "extract_audio", Type: "bool", Default: defaultBool(true)},
|
|
||||||
{Key: "images_or_video",
|
|
||||||
Type: "string",
|
|
||||||
Required: boolPtr(true),
|
|
||||||
Choices: choicesStr("images", "video"),
|
|
||||||
Visible: boolPtr(false),
|
|
||||||
},
|
|
||||||
{Key: "format", Type: "string", Required: boolPtr(true)},
|
|
||||||
{Key: "output_file_extension", Type: "string", Required: boolPtr(true)},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.JSON(http.StatusOK, &types)
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// Package job_compilers contains functionality to convert a Flamenco job
|
||||||
|
// definition into concrete tasks and commands to execute by Workers.
|
||||||
package job_compilers
|
package job_compilers
|
||||||
|
|
||||||
/* ***** BEGIN GPL LICENSE BLOCK *****
|
/* ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
@ -28,24 +30,32 @@ import (
|
|||||||
"github.com/dop251/goja_nodejs/require"
|
"github.com/dop251/goja_nodejs/require"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"gitlab.com/blender/flamenco-goja-test/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrJobTypeUnknown = errors.New("job type unknown")
|
var ErrJobTypeUnknown = errors.New("job type unknown")
|
||||||
var ErrScriptIncomplete = errors.New("job compiler script incomplete")
|
var ErrScriptIncomplete = errors.New("job compiler script incomplete")
|
||||||
|
|
||||||
type GojaJobCompiler struct {
|
// Service contains job compilers defined in JavaScript.
|
||||||
jobtypes map[string]JobType // Mapping from job type name to jobType struct.
|
type Service struct {
|
||||||
registry *require.Registry // Goja module registry.
|
compilers map[string]Compiler // Mapping from job type name to the job compiler of that type.
|
||||||
|
registry *require.Registry // Goja module registry.
|
||||||
}
|
}
|
||||||
|
|
||||||
type JobType struct {
|
type Compiler struct {
|
||||||
|
jobType string
|
||||||
program *goja.Program // Compiled JavaScript file.
|
program *goja.Program // Compiled JavaScript file.
|
||||||
filename string // The filename of that JS file.
|
filename string // The filename of that JS file.
|
||||||
}
|
}
|
||||||
|
|
||||||
func Load() (*GojaJobCompiler, error) {
|
type VM struct {
|
||||||
compiler := GojaJobCompiler{
|
runtime *goja.Runtime // Goja VM containing the job compiler script.
|
||||||
jobtypes: map[string]JobType{},
|
compiler Compiler // Program loaded into this VM.
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load() (*Service, error) {
|
||||||
|
compiler := Service{
|
||||||
|
compilers: map[string]Compiler{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := compiler.loadScripts(); err != nil {
|
if err := compiler.loadScripts(); err != nil {
|
||||||
@ -53,7 +63,7 @@ func Load() (*GojaJobCompiler, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
staticFileLoader := func(path string) ([]byte, error) {
|
staticFileLoader := func(path string) ([]byte, error) {
|
||||||
content, err := compiler.loadScript(path)
|
content, err := compiler.loadScriptBytes(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The 'require' module uses this to try different variations of the path
|
// The 'require' module uses this to try different variations of the path
|
||||||
// in order to find it (without .js, with .js, etc.), so don't log any of
|
// in order to find it (without .js, with .js, etc.), so don't log any of
|
||||||
@ -71,34 +81,15 @@ func Load() (*GojaJobCompiler, error) {
|
|||||||
return &compiler, nil
|
return &compiler, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GojaJobCompiler) newGojaVM() *goja.Runtime {
|
func (s *Service) Run(jobTypeName string) error {
|
||||||
vm := goja.New()
|
vm, err := s.compilerForJobType(jobTypeName)
|
||||||
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
|
if err != nil {
|
||||||
|
return err
|
||||||
mustSet := func(name string, value interface{}) {
|
|
||||||
err := vm.Set(name, value)
|
|
||||||
if err != nil {
|
|
||||||
log.Panic().Err(err).Msgf("unable to register '%s' in Goja VM", name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set some global functions for script debugging purposes.
|
compileJob, err := vm.getCompileJob()
|
||||||
mustSet("print", jsPrint)
|
if err != nil {
|
||||||
mustSet("alert", jsAlert)
|
return err
|
||||||
|
|
||||||
// Pre-import some useful modules.
|
|
||||||
c.registry.Enable(vm)
|
|
||||||
mustSet("author", require.Require(vm, "author"))
|
|
||||||
mustSet("path", require.Require(vm, "path"))
|
|
||||||
mustSet("process", require.Require(vm, "process"))
|
|
||||||
|
|
||||||
return vm
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GojaJobCompiler) Run(jobTypeName string) error {
|
|
||||||
jobType, ok := c.jobtypes[jobTypeName]
|
|
||||||
if !ok {
|
|
||||||
return ErrJobTypeUnknown
|
|
||||||
}
|
}
|
||||||
|
|
||||||
created, err := time.Parse(time.RFC3339, "2022-01-03T18:53:00+01:00")
|
created, err := time.Parse(time.RFC3339, "2022-01-03T18:53:00+01:00")
|
||||||
@ -130,24 +121,7 @@ func (c *GojaJobCompiler) Run(jobTypeName string) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
vm := c.newGojaVM()
|
if err := compileJob(&job); err != nil {
|
||||||
|
|
||||||
// This should register the `compileJob()` function called below:
|
|
||||||
if _, err := vm.RunProgram(jobType.program); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
compileJob, isCallable := goja.AssertFunction(vm.Get("compileJob"))
|
|
||||||
if !isCallable {
|
|
||||||
log.Error().
|
|
||||||
Str("jobType", jobTypeName).
|
|
||||||
Str("script", jobType.filename).
|
|
||||||
Msg("script does not define a compileJob(job) function")
|
|
||||||
return ErrScriptIncomplete
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := compileJob(nil, vm.ToValue(&job)); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,3 +133,60 @@ func (c *GojaJobCompiler) Run(jobTypeName string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListJobTypes returns the list of available job types.
|
||||||
|
func (s *Service) ListJobTypes() api.AvailableJobTypes {
|
||||||
|
jobTypes := make([]api.AvailableJobType, 0)
|
||||||
|
for typeName := range s.compilers {
|
||||||
|
compiler, err := s.compilerForJobType(typeName)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Str("jobType", typeName).Msg("unable to determine job type settings")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
description, err := compiler.getJobTypeInfo()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Str("jobType", typeName).Msg("unable to determine job type settings")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
jobTypes = append(jobTypes, *description)
|
||||||
|
}
|
||||||
|
return api.AvailableJobTypes{JobTypes: jobTypes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VM) getCompileJob() (func(job *AuthoredJob) error, error) {
|
||||||
|
compileJob, isCallable := goja.AssertFunction(vm.runtime.Get("compileJob"))
|
||||||
|
if !isCallable {
|
||||||
|
// TODO: construct a more elaborate Error object that contains this info, instead of logging here.
|
||||||
|
log.Error().
|
||||||
|
Str("jobType", vm.compiler.jobType).
|
||||||
|
Str("script", vm.compiler.filename).
|
||||||
|
Msg("script does not define a compileJob(job) function")
|
||||||
|
return nil, ErrScriptIncomplete
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: wrap this in a nicer way.
|
||||||
|
return func(job *AuthoredJob) error {
|
||||||
|
_, err := compileJob(nil, vm.runtime.ToValue(job))
|
||||||
|
return err
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VM) getJobTypeInfo() (*api.AvailableJobType, error) {
|
||||||
|
jtValue := vm.runtime.Get("JOB_TYPE")
|
||||||
|
|
||||||
|
var ajt api.AvailableJobType
|
||||||
|
if err := vm.runtime.ExportTo(jtValue, &ajt); err != nil {
|
||||||
|
// TODO: construct a more elaborate Error object that contains this info, instead of logging here.
|
||||||
|
log.Error().
|
||||||
|
Err(err).
|
||||||
|
Str("jobType", vm.compiler.jobType).
|
||||||
|
Str("script", vm.compiler.filename).
|
||||||
|
Msg("script does not define a proper JOB_TYPE object")
|
||||||
|
return nil, ErrScriptIncomplete
|
||||||
|
}
|
||||||
|
|
||||||
|
ajt.Name = vm.compiler.jobType
|
||||||
|
return &ajt, nil
|
||||||
|
}
|
||||||
|
@ -28,13 +28,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
|
"github.com/dop251/goja_nodejs/require"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed scripts
|
//go:embed scripts
|
||||||
var scriptsFS embed.FS
|
var scriptsFS embed.FS
|
||||||
|
|
||||||
func (c *GojaJobCompiler) loadScripts() error {
|
func (s *Service) loadScripts() error {
|
||||||
scripts, err := scriptsFS.ReadDir("scripts")
|
scripts, err := scriptsFS.ReadDir("scripts")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find scripts: %w", err)
|
return fmt.Errorf("failed to find scripts: %w", err)
|
||||||
@ -46,7 +47,7 @@ func (c *GojaJobCompiler) loadScripts() error {
|
|||||||
}
|
}
|
||||||
filename := path.Join("scripts", script.Name())
|
filename := path.Join("scripts", script.Name())
|
||||||
|
|
||||||
script_bytes, err := c.loadScript(filename)
|
script_bytes, err := s.loadScriptBytes(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Str("filename", filename).Msg("failed to read script")
|
log.Error().Err(err).Str("filename", filename).Msg("failed to read script")
|
||||||
continue
|
continue
|
||||||
@ -59,7 +60,7 @@ func (c *GojaJobCompiler) loadScripts() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jobTypeName := filenameToJobType(script.Name())
|
jobTypeName := filenameToJobType(script.Name())
|
||||||
c.jobtypes[jobTypeName] = JobType{
|
s.compilers[jobTypeName] = Compiler{
|
||||||
program: program,
|
program: program,
|
||||||
filename: script.Name(),
|
filename: script.Name(),
|
||||||
}
|
}
|
||||||
@ -70,7 +71,7 @@ func (c *GojaJobCompiler) loadScripts() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GojaJobCompiler) loadScript(path string) ([]byte, error) {
|
func (s *Service) loadScriptBytes(path string) ([]byte, error) {
|
||||||
file, err := scriptsFS.Open(path)
|
file, err := scriptsFS.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open embedded script: %w", err)
|
return nil, fmt.Errorf("failed to open embedded script: %w", err)
|
||||||
@ -83,3 +84,46 @@ func filenameToJobType(filename string) string {
|
|||||||
stem := filename[:len(filename)-len(extension)]
|
stem := filename[:len(filename)-len(extension)]
|
||||||
return strings.ReplaceAll(stem, "_", "-")
|
return strings.ReplaceAll(stem, "_", "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) newGojaVM() *goja.Runtime {
|
||||||
|
vm := goja.New()
|
||||||
|
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
|
||||||
|
|
||||||
|
mustSet := func(name string, value interface{}) {
|
||||||
|
err := vm.Set(name, value)
|
||||||
|
if err != nil {
|
||||||
|
log.Panic().Err(err).Msgf("unable to register '%s' in Goja VM", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set some global functions for script debugging purposes.
|
||||||
|
mustSet("print", jsPrint)
|
||||||
|
mustSet("alert", jsAlert)
|
||||||
|
|
||||||
|
// Pre-import some useful modules.
|
||||||
|
s.registry.Enable(vm)
|
||||||
|
mustSet("author", require.Require(vm, "author"))
|
||||||
|
mustSet("path", require.Require(vm, "path"))
|
||||||
|
mustSet("process", require.Require(vm, "process"))
|
||||||
|
|
||||||
|
return vm
|
||||||
|
}
|
||||||
|
|
||||||
|
// compilerForJobType returns a Goja *Runtime that has the job compiler script for
|
||||||
|
// the given job type loaded up.
|
||||||
|
func (s *Service) compilerForJobType(jobTypeName string) (*VM, error) {
|
||||||
|
program, ok := s.compilers[jobTypeName]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrJobTypeUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
vm := s.newGojaVM()
|
||||||
|
if _, err := vm.RunProgram(program.program); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &VM{
|
||||||
|
runtime: vm,
|
||||||
|
compiler: program,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
@ -18,6 +18,28 @@
|
|||||||
*
|
*
|
||||||
* ***** END GPL LICENSE BLOCK ***** */
|
* ***** END GPL LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
const JOB_TYPE = {
|
||||||
|
label: "Simple Blender Render",
|
||||||
|
settings: [
|
||||||
|
{ key: "blender_cmd", type: "string", default: "{blender}" },
|
||||||
|
{ key: "chunk_size", type: "int32", default: 1 },
|
||||||
|
{ key: "frames", type: "string", required: true },
|
||||||
|
{ key: "render_output", type: "string", required: true },
|
||||||
|
{ key: "fps", type: "int32" },
|
||||||
|
{ key: "extract_audio", type: "bool", default: true },
|
||||||
|
{
|
||||||
|
key: "images_or_video",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
choices: ["images", "video"],
|
||||||
|
visible: false,
|
||||||
|
},
|
||||||
|
{ key: "format", type: "string", required: true },
|
||||||
|
{ key: "output_file_extension", type: "string", required: true },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Set of scene.render.image_settings.file_format values that produce
|
// Set of scene.render.image_settings.file_format values that produce
|
||||||
// files which FFmpeg is known not to handle as input.
|
// files which FFmpeg is known not to handle as input.
|
||||||
const ffmpegIncompatibleImageFormats = new Set([
|
const ffmpegIncompatibleImageFormats = new Set([
|
||||||
|
@ -77,6 +77,8 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- name: worker
|
- name: worker
|
||||||
description: API for Flamenco Workers to communicate with Flamenco Manager.
|
description: API for Flamenco Workers to communicate with Flamenco Manager.
|
||||||
|
- name: jobs
|
||||||
|
description: Blablabla for users to get job metadata balbla
|
||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
@ -154,10 +156,11 @@ components:
|
|||||||
description: Job type supported by this Manager, and its parameters.
|
description: Job type supported by this Manager, and its parameters.
|
||||||
properties:
|
properties:
|
||||||
"name": {type: string}
|
"name": {type: string}
|
||||||
|
"label": {type: string}
|
||||||
"settings":
|
"settings":
|
||||||
type: array
|
type: array
|
||||||
items: {$ref: "#/components/schemas/AvailableJobSetting"}
|
items: {$ref: "#/components/schemas/AvailableJobSetting"}
|
||||||
required: [name, settings]
|
required: [name, label, settings]
|
||||||
|
|
||||||
AvailableJobSetting:
|
AvailableJobSetting:
|
||||||
type: object
|
type: object
|
||||||
|
@ -18,35 +18,36 @@ import (
|
|||||||
// Base64 encoded, gzipped, json marshaled Swagger object
|
// Base64 encoded, gzipped, json marshaled Swagger object
|
||||||
var swaggerSpec = []string{
|
var swaggerSpec = []string{
|
||||||
|
|
||||||
"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",
|
||||||
|
"MSpkhCWapcH+wNhV97f7fwcAAP//KSYvdP0WAAA=",
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSwagger returns the content of the embedded swagger specification file
|
// GetSwagger returns the content of the embedded swagger specification file
|
||||||
|
@ -81,6 +81,7 @@ type AvailableJobSettingType string
|
|||||||
|
|
||||||
// Job type supported by this Manager, and its parameters.
|
// Job type supported by this Manager, and its parameters.
|
||||||
type AvailableJobType struct {
|
type AvailableJobType struct {
|
||||||
|
Label string `json:"label"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Settings []AvailableJobSetting `json:"settings"`
|
Settings []AvailableJobSetting `json:"settings"`
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user