mirror of
https://github.com/5rahim/seanime
synced 2026-04-25 22:34:56 +02:00
feat: internal api docs page
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
# Contribution Guide
|
||||
|
||||
All contributions are welcome. If you're not sure about something, feel free to ask.
|
||||
All contributions are welcome _if_ they are in the scope of the project. If you're not sure about something, feel free to ask.
|
||||
|
||||
## Guidelines
|
||||
|
||||
Note that you should try and make your changes against the **most active branch**, which is usually the `main` branch but
|
||||
may be different when a new minor version is being developed.
|
||||
- Make sure you are familiar with Go and React.
|
||||
- Your contributions must be small and focused. If you want to add a new feature that requires substantial changes or additions to the codebase, please contact the dev first.
|
||||
- Make sure your changes are in line with the project's goals (Create a feature request if you're unsure).
|
||||
- Make sure your changes are well tested and do not introduce any new issues or regressions.
|
||||
- You should try and make your changes against the **most active branch**, which is usually the `main` branch but
|
||||
may be different when a new version is being developed.
|
||||
|
||||
## How to contribute
|
||||
|
||||
1. Create an issue before starting work on a feature or a bug fix.
|
||||
2. Fork the repository, clone it, and create a new branch.
|
||||
|
||||
@@ -47,13 +47,13 @@ Note that the web interface should be built first before building the server.
|
||||
|
||||
# Development
|
||||
|
||||
To get started, you will need to be familiar with Go and React.
|
||||
1. To get started, you **must be familiar with Go and React**.
|
||||
|
||||
I recommend creating a dummy AniList account for testing purposes.
|
||||
2. I recommend creating a dummy AniList account to use for testing. This will prevent the tests from affecting your actual account.
|
||||
|
||||
## Server
|
||||
|
||||
1. Choose a data directory for testing.
|
||||
1. Create/choose a dummy data directory for testing.
|
||||
This will prevent the server from writing to your actual data directory.
|
||||
|
||||
2. Create a dummy `web` folder if you want to develop the web interface too or build the web interface first and move the contents to a `web` directory at the root of the project before running the server.
|
||||
@@ -85,20 +85,24 @@ Either way, a `web` directory should be present at the root of the project.
|
||||
During development, the web interface is served by the Next.js development server instead of the Go server,
|
||||
leading to different ports.
|
||||
|
||||
## Codegen
|
||||
## Handlers & Codegen
|
||||
|
||||
1. All routes are declared in `internal/handlers/routes.go` where a `handler` method is passed to each route.
|
||||
2. Route handlers are defined in `internal/handlers`.
|
||||
> The following points are very important for understanding the codebase.
|
||||
|
||||
- All routes are declared in `internal/handlers/routes.go` where a `handler` method is passed to each route.
|
||||
- Route handlers are defined in `internal/handlers`.
|
||||
- The comments above each route handler declaration is a form of documentation similar to OpenAPI
|
||||
- These comments allow the internal code generator (`codegen/main.go`) to generate endpoint objects & types for the client.
|
||||
- Types for the frontend are auto-generated in `seanime-web/api/generated/types.ts` based on the `@returns` directive and request body variables of each route handler. The code generator will analyze the entire Go codebase and convert _returned_ public structs into Typescript types.
|
||||
- Types for the frontend are auto-generated in `seanime-web/api/generated/types.ts`, `seanime-web/api/generated/endpoint.types.ts`, `seanime-web/api/generated/hooks_template.ts` based on the comments above route handlers and all public Go structs.
|
||||
|
||||
Run the `go generate` command at the top of `codegen/main.go` to generate the necessary types for the frontend.
|
||||
This should be done after making changes to the route handlers or structs returned by the route handlers.
|
||||
This should be done **each time you make changes** to the **route handlers** or **structs** that are used in the frontend.
|
||||
|
||||
|
||||
## AniList GraphQL API
|
||||
|
||||
> The following points are for understanding the AniList API integration.
|
||||
|
||||
Anilist queries are defined in `internal/anilist/queries/*.graphql` and generated using `gqlgenc`.
|
||||
|
||||
Run this when you make changes to the GraphQL schema.
|
||||
@@ -119,11 +123,11 @@ cd ../../..
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
`gqlgenc` will generate the different queries, mutations and structs associated with the AniList API and the queries we defined.
|
||||
These are located in `internal/api/anilist/clieng_gen.go`.
|
||||
- `gqlgenc` will generate the different queries, mutations and structs associated with the AniList API and the queries we defined.
|
||||
These are located in `internal/api/anilist/client_gen.go`.
|
||||
|
||||
|
||||
In `internal/api/anilist/client.go`, we manually reimplements the different queries and mutations into a `ClientWrapper` struct and a `MockClientWrapper` for testing.
|
||||
- In `internal/api/anilist/client.go`, we manually reimplements the different queries and mutations into a `ClientWrapper` struct and a `MockClientWrapper` for testing.
|
||||
This is done to avoid using the generated code directly in the business logic. It also allows us to mock the AniList API for testing.
|
||||
|
||||
## Tests
|
||||
|
||||
@@ -2327,7 +2327,7 @@
|
||||
"",
|
||||
"\t@summary returns the API documentation",
|
||||
"\t@route /api/v1/internal/docs [GET]",
|
||||
"\t@returns docs.Docs",
|
||||
"\t@returns []handlers.ApiDocsGroup",
|
||||
""
|
||||
],
|
||||
"filepath": "internal/handlers/docs.go",
|
||||
@@ -2341,9 +2341,9 @@
|
||||
],
|
||||
"params": [],
|
||||
"bodyFields": [],
|
||||
"returns": "docs.Docs",
|
||||
"returnGoType": "docs.Docs",
|
||||
"returnTypescriptType": "INTERNAL_Docs"
|
||||
"returns": "[]handlers.ApiDocsGroup",
|
||||
"returnGoType": "handlers.ApiDocsGroup",
|
||||
"returnTypescriptType": "Array\u003cApiDocsGroup\u003e"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2956,7 +2956,7 @@
|
||||
"\t@summary returns the total size of cache files.",
|
||||
"\t@desc The total size of the cache files is returned in human-readable format.",
|
||||
"\t@route /api/v1/filecache/total-size [GET]",
|
||||
"\t@returns bool",
|
||||
"\t@returns string",
|
||||
""
|
||||
],
|
||||
"filepath": "internal/handlers/filecache.go",
|
||||
@@ -2972,9 +2972,9 @@
|
||||
],
|
||||
"params": [],
|
||||
"bodyFields": [],
|
||||
"returns": "bool",
|
||||
"returnGoType": "bool",
|
||||
"returnTypescriptType": "boolean"
|
||||
"returns": "string",
|
||||
"returnGoType": "string",
|
||||
"returnTypescriptType": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -3028,7 +3028,7 @@
|
||||
"\t@summary returns the total size of cached video file data.",
|
||||
"\t@desc The total size of the cache video file data is returned in human-readable format.",
|
||||
"\t@route /api/v1/filecache/mediastream/videofiles/total-size [GET]",
|
||||
"\t@returns bool",
|
||||
"\t@returns string",
|
||||
""
|
||||
],
|
||||
"filepath": "internal/handlers/filecache.go",
|
||||
@@ -3044,9 +3044,9 @@
|
||||
],
|
||||
"params": [],
|
||||
"bodyFields": [],
|
||||
"returns": "bool",
|
||||
"returnGoType": "bool",
|
||||
"returnTypescriptType": "boolean"
|
||||
"returns": "string",
|
||||
"returnGoType": "string",
|
||||
"returnTypescriptType": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -28290,6 +28290,16 @@
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "torrentRepository",
|
||||
"jsonName": "torrentRepository",
|
||||
"goType": "torrent.Repository",
|
||||
"typescriptType": "Torrent_Repository",
|
||||
"usedStructName": "torrent.Repository",
|
||||
"required": false,
|
||||
"public": false,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "playbackManager",
|
||||
"jsonName": "playbackManager",
|
||||
@@ -28380,6 +28390,16 @@
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "TorrentRepository",
|
||||
"jsonName": "TorrentRepository",
|
||||
"goType": "torrent.Repository",
|
||||
"typescriptType": "Torrent_Repository",
|
||||
"usedStructName": "torrent.Repository",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "PlaybackManager",
|
||||
"jsonName": "PlaybackManager",
|
||||
@@ -28614,6 +28634,15 @@
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "AutoSelect",
|
||||
"jsonName": "AutoSelect",
|
||||
"goType": "bool",
|
||||
"typescriptType": "boolean",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
@@ -29014,6 +29043,17 @@
|
||||
"formattedName": "Debrid_TorrentInfo",
|
||||
"package": "debrid",
|
||||
"fields": [
|
||||
{
|
||||
"name": "ID",
|
||||
"jsonName": "id",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": [
|
||||
" ID of the torrent if added to the debrid service"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"jsonName": "name",
|
||||
@@ -33271,6 +33311,281 @@
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/handlers/docs.go",
|
||||
"filename": "docs.go",
|
||||
"name": "ApiDocsGroup",
|
||||
"formattedName": "ApiDocsGroup",
|
||||
"package": "handlers",
|
||||
"fields": [
|
||||
{
|
||||
"name": "Filename",
|
||||
"jsonName": "filename",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"jsonName": "name",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Handlers",
|
||||
"jsonName": "handlers",
|
||||
"goType": "[]RouteHandler",
|
||||
"typescriptType": "Array\u003cRouteHandler\u003e",
|
||||
"usedStructName": "handlers.RouteHandler",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/handlers/docs.go",
|
||||
"filename": "docs.go",
|
||||
"name": "RouteHandler",
|
||||
"formattedName": "RouteHandler",
|
||||
"package": "handlers",
|
||||
"fields": [
|
||||
{
|
||||
"name": "Name",
|
||||
"jsonName": "name",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "TrimmedName",
|
||||
"jsonName": "trimmedName",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Comments",
|
||||
"jsonName": "comments",
|
||||
"goType": "[]string",
|
||||
"typescriptType": "Array\u003cstring\u003e",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Filepath",
|
||||
"jsonName": "filepath",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Filename",
|
||||
"jsonName": "filename",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Api",
|
||||
"jsonName": "api",
|
||||
"goType": "RouteHandlerApi",
|
||||
"typescriptType": "RouteHandlerApi",
|
||||
"usedStructName": "handlers.RouteHandlerApi",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/handlers/docs.go",
|
||||
"filename": "docs.go",
|
||||
"name": "RouteHandlerApi",
|
||||
"formattedName": "RouteHandlerApi",
|
||||
"package": "handlers",
|
||||
"fields": [
|
||||
{
|
||||
"name": "Summary",
|
||||
"jsonName": "summary",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Descriptions",
|
||||
"jsonName": "descriptions",
|
||||
"goType": "[]string",
|
||||
"typescriptType": "Array\u003cstring\u003e",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Endpoint",
|
||||
"jsonName": "endpoint",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Methods",
|
||||
"jsonName": "methods",
|
||||
"goType": "[]string",
|
||||
"typescriptType": "Array\u003cstring\u003e",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Params",
|
||||
"jsonName": "params",
|
||||
"goType": "[]RouteHandlerParam",
|
||||
"typescriptType": "Array\u003cRouteHandlerParam\u003e",
|
||||
"usedStructName": "handlers.RouteHandlerParam",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "BodyFields",
|
||||
"jsonName": "bodyFields",
|
||||
"goType": "[]RouteHandlerParam",
|
||||
"typescriptType": "Array\u003cRouteHandlerParam\u003e",
|
||||
"usedStructName": "handlers.RouteHandlerParam",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Returns",
|
||||
"jsonName": "returns",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "ReturnGoType",
|
||||
"jsonName": "returnGoType",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "ReturnTypescriptType",
|
||||
"jsonName": "returnTypescriptType",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/handlers/docs.go",
|
||||
"filename": "docs.go",
|
||||
"name": "RouteHandlerParam",
|
||||
"formattedName": "RouteHandlerParam",
|
||||
"package": "handlers",
|
||||
"fields": [
|
||||
{
|
||||
"name": "Name",
|
||||
"jsonName": "name",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "JsonName",
|
||||
"jsonName": "jsonName",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "GoType",
|
||||
"jsonName": "goType",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": [
|
||||
" e.g., []models.User"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "UsedStructType",
|
||||
"jsonName": "usedStructType",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": [
|
||||
" e.g., models.User"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TypescriptType",
|
||||
"jsonName": "typescriptType",
|
||||
"goType": "string",
|
||||
"typescriptType": "string",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": [
|
||||
" e.g., Array\u003cUser\u003e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Required",
|
||||
"jsonName": "required",
|
||||
"goType": "bool",
|
||||
"typescriptType": "boolean",
|
||||
"required": true,
|
||||
"public": true,
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"name": "Descriptions",
|
||||
"jsonName": "descriptions",
|
||||
"goType": "[]string",
|
||||
"typescriptType": "Array\u003cstring\u003e",
|
||||
"required": false,
|
||||
"public": true,
|
||||
"comments": []
|
||||
}
|
||||
],
|
||||
"comments": []
|
||||
},
|
||||
{
|
||||
"filepath": "../internal/handlers/download.go",
|
||||
"filename": "download.go",
|
||||
|
||||
@@ -21,9 +21,9 @@ var additionalStructNamesForEndpoints = []string{
|
||||
"vendor_hibike_torrent.AnimeTorrent",
|
||||
}
|
||||
|
||||
func GenerateTypescriptEndpointsFile(docsPath string, structsPath string, outDir string) []string {
|
||||
handlers := LoadHandlers(docsPath)
|
||||
structs := LoadPublicStructs(structsPath)
|
||||
func GenerateTypescriptEndpointsFile(handlersJsonPath string, structsJsonPath string, outDir string) []string {
|
||||
handlers := LoadHandlers(handlersJsonPath)
|
||||
structs := LoadPublicStructs(structsJsonPath)
|
||||
|
||||
_ = os.MkdirAll(outDir, os.ModePerm)
|
||||
f, err := os.Create(filepath.Join(outDir, typescriptEndpointsFileName))
|
||||
|
||||
@@ -1,27 +1,96 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/goccy/go-json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
ApiDocsGroup struct {
|
||||
Filename string `json:"filename"`
|
||||
Name string `json:"name"`
|
||||
Handlers []*RouteHandler `json:"handlers"`
|
||||
}
|
||||
|
||||
RouteHandler struct {
|
||||
Name string `json:"name"`
|
||||
TrimmedName string `json:"trimmedName"`
|
||||
Comments []string `json:"comments"`
|
||||
Filepath string `json:"filepath"`
|
||||
Filename string `json:"filename"`
|
||||
Api *RouteHandlerApi `json:"api"`
|
||||
}
|
||||
|
||||
RouteHandlerApi struct {
|
||||
Summary string `json:"summary"`
|
||||
Descriptions []string `json:"descriptions"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Methods []string `json:"methods"`
|
||||
Params []*RouteHandlerParam `json:"params"`
|
||||
BodyFields []*RouteHandlerParam `json:"bodyFields"`
|
||||
Returns string `json:"returns"`
|
||||
ReturnGoType string `json:"returnGoType"`
|
||||
ReturnTypescriptType string `json:"returnTypescriptType"`
|
||||
}
|
||||
|
||||
RouteHandlerParam struct {
|
||||
Name string `json:"name"`
|
||||
JsonName string `json:"jsonName"`
|
||||
GoType string `json:"goType"` // e.g., []models.User
|
||||
UsedStructType string `json:"usedStructType"` // e.g., models.User
|
||||
TypescriptType string `json:"typescriptType"` // e.g., Array<User>
|
||||
Required bool `json:"required"`
|
||||
Descriptions []string `json:"descriptions"`
|
||||
}
|
||||
)
|
||||
|
||||
var cachedDocs []*ApiDocsGroup
|
||||
|
||||
// HandleGetDocs
|
||||
//
|
||||
// @summary returns the API documentation
|
||||
// @route /api/v1/internal/docs [GET]
|
||||
// @returns docs.Docs
|
||||
// @returns []handlers.ApiDocsGroup
|
||||
func HandleGetDocs(c *RouteCtx) error {
|
||||
|
||||
//// Read the file
|
||||
//wd, _ := os.Getwd()
|
||||
//buf, err := os.ReadFile(filepath.Join(wd, "seanime-docs/routes.json"))
|
||||
//if err != nil {
|
||||
// return c.RespondWithError(err)
|
||||
//}
|
||||
//
|
||||
//var data *docs.Docs
|
||||
//// Unmarshal the data
|
||||
//err = json.Unmarshal(buf, &data)
|
||||
//if err != nil {
|
||||
// return c.RespondWithError(err)
|
||||
//}
|
||||
//
|
||||
//return c.RespondWithData(data)
|
||||
|
||||
return c.RespondWithData("Hello, World!")
|
||||
if len(cachedDocs) > 0 {
|
||||
return c.RespondWithData(cachedDocs)
|
||||
}
|
||||
|
||||
// Read the file
|
||||
wd, _ := os.Getwd()
|
||||
buf, err := os.ReadFile(filepath.Join(wd, "codegen/generated/handlers.json"))
|
||||
if err != nil {
|
||||
return c.RespondWithError(err)
|
||||
}
|
||||
|
||||
var data []*RouteHandler
|
||||
// Unmarshal the data
|
||||
err = json.Unmarshal(buf, &data)
|
||||
if err != nil {
|
||||
return c.RespondWithError(err)
|
||||
}
|
||||
|
||||
// Group the data
|
||||
groups := make(map[string]*ApiDocsGroup)
|
||||
for _, handler := range data {
|
||||
group, ok := groups[handler.Filename]
|
||||
if !ok {
|
||||
group = &ApiDocsGroup{
|
||||
Filename: handler.Filename,
|
||||
Name: strings.TrimPrefix(handler.Filename, ".go"),
|
||||
}
|
||||
groups[handler.Filename] = group
|
||||
}
|
||||
group.Handlers = append(group.Handlers, handler)
|
||||
}
|
||||
|
||||
cachedDocs = make([]*ApiDocsGroup, 0, len(groups))
|
||||
for _, group := range groups {
|
||||
cachedDocs = append(cachedDocs, group)
|
||||
}
|
||||
|
||||
return c.RespondWithData(groups)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
// @summary returns the total size of cache files.
|
||||
// @desc The total size of the cache files is returned in human-readable format.
|
||||
// @route /api/v1/filecache/total-size [GET]
|
||||
// @returns bool
|
||||
// @returns string
|
||||
func HandleGetFileCacheTotalSize(c *RouteCtx) error {
|
||||
// Get the cache size
|
||||
size, err := c.App.FileCacher.GetTotalSize()
|
||||
@@ -59,7 +59,7 @@ func HandleRemoveFileCacheBucket(c *RouteCtx) error {
|
||||
// @summary returns the total size of cached video file data.
|
||||
// @desc The total size of the cache video file data is returned in human-readable format.
|
||||
// @route /api/v1/filecache/mediastream/videofiles/total-size [GET]
|
||||
// @returns bool
|
||||
// @returns string
|
||||
func HandleGetFileCacheMediastreamVideoFilesTotalSize(c *RouteCtx) error {
|
||||
// Get the cache size
|
||||
size, err := c.App.FileCacher.GetMediastreamVideoFilesTotalSize()
|
||||
|
||||
@@ -564,7 +564,7 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// export function useGetDocs() {
|
||||
// return useServerQuery<INTERNAL_Docs>({
|
||||
// return useServerQuery<Array<ApiDocsGroup>>({
|
||||
// endpoint: API_ENDPOINTS.DOCS.GetDocs.endpoint,
|
||||
// method: API_ENDPOINTS.DOCS.GetDocs.methods[0],
|
||||
// queryKey: [API_ENDPOINTS.DOCS.GetDocs.key],
|
||||
@@ -755,7 +755,7 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// export function useGetFileCacheTotalSize() {
|
||||
// return useServerQuery<boolean>({
|
||||
// return useServerQuery<string>({
|
||||
// endpoint: API_ENDPOINTS.FILECACHE.GetFileCacheTotalSize.endpoint,
|
||||
// method: API_ENDPOINTS.FILECACHE.GetFileCacheTotalSize.methods[0],
|
||||
// queryKey: [API_ENDPOINTS.FILECACHE.GetFileCacheTotalSize.key],
|
||||
@@ -775,7 +775,7 @@
|
||||
// }
|
||||
|
||||
// export function useGetFileCacheMediastreamVideoFilesTotalSize() {
|
||||
// return useServerQuery<boolean>({
|
||||
// return useServerQuery<string>({
|
||||
// endpoint: API_ENDPOINTS.FILECACHE.GetFileCacheMediastreamVideoFilesTotalSize.endpoint,
|
||||
// method: API_ENDPOINTS.FILECACHE.GetFileCacheMediastreamVideoFilesTotalSize.methods[0],
|
||||
// queryKey: [API_ENDPOINTS.FILECACHE.GetFileCacheMediastreamVideoFilesTotalSize.key],
|
||||
|
||||
@@ -1783,6 +1783,10 @@ export type Debrid_CachedFile = {
|
||||
* - Package: debrid
|
||||
*/
|
||||
export type Debrid_TorrentInfo = {
|
||||
/**
|
||||
* ID of the torrent if added to the debrid service
|
||||
*/
|
||||
id?: string
|
||||
name: string
|
||||
hash: string
|
||||
size: number
|
||||
@@ -2186,6 +2190,17 @@ export type ExtensionRepo_UpdateData = {
|
||||
// Handlers
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* - Filepath: internal/handlers/docs.go
|
||||
* - Filename: docs.go
|
||||
* - Package: handlers
|
||||
*/
|
||||
export type ApiDocsGroup = {
|
||||
filename: string
|
||||
name: string
|
||||
handlers?: Array<RouteHandler>
|
||||
}
|
||||
|
||||
/**
|
||||
* - Filepath: internal/handlers/directory_selector.go
|
||||
* - Filename: directory_selector.go
|
||||
@@ -2231,6 +2246,61 @@ export type MalAuthResponse = {
|
||||
token_type: string
|
||||
}
|
||||
|
||||
/**
|
||||
* - Filepath: internal/handlers/docs.go
|
||||
* - Filename: docs.go
|
||||
* - Package: handlers
|
||||
*/
|
||||
export type RouteHandler = {
|
||||
name: string
|
||||
trimmedName: string
|
||||
comments?: Array<string>
|
||||
filepath: string
|
||||
filename: string
|
||||
api?: RouteHandlerApi
|
||||
}
|
||||
|
||||
/**
|
||||
* - Filepath: internal/handlers/docs.go
|
||||
* - Filename: docs.go
|
||||
* - Package: handlers
|
||||
*/
|
||||
export type RouteHandlerApi = {
|
||||
summary: string
|
||||
descriptions?: Array<string>
|
||||
endpoint: string
|
||||
methods?: Array<string>
|
||||
params?: Array<RouteHandlerParam>
|
||||
bodyFields?: Array<RouteHandlerParam>
|
||||
returns: string
|
||||
returnGoType: string
|
||||
returnTypescriptType: string
|
||||
}
|
||||
|
||||
/**
|
||||
* - Filepath: internal/handlers/docs.go
|
||||
* - Filename: docs.go
|
||||
* - Package: handlers
|
||||
*/
|
||||
export type RouteHandlerParam = {
|
||||
name: string
|
||||
jsonName: string
|
||||
/**
|
||||
* e.g., []models.User
|
||||
*/
|
||||
goType: string
|
||||
/**
|
||||
* e.g., models.User
|
||||
*/
|
||||
usedStructType: string
|
||||
/**
|
||||
* e.g., Array<User>
|
||||
*/
|
||||
typescriptType: string
|
||||
required: boolean
|
||||
descriptions?: Array<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* - Filepath: internal/handlers/status.go
|
||||
* - Filename: status.go
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useServerQuery } from "@/api/client/requests"
|
||||
import { API_ENDPOINTS } from "@/api/generated/endpoints"
|
||||
import { ApiDocsGroup } from "@/api/generated/types"
|
||||
|
||||
export function useGetDocs() {
|
||||
return useServerQuery<any>({
|
||||
return useServerQuery<ApiDocsGroup[]>({
|
||||
endpoint: API_ENDPOINTS.DOCS.GetDocs.endpoint,
|
||||
method: API_ENDPOINTS.DOCS.GetDocs.methods[0],
|
||||
queryKey: [API_ENDPOINTS.DOCS.GetDocs.key],
|
||||
|
||||
@@ -97,7 +97,7 @@ export function MonthCalendar(props: WeekCalendarProps) {
|
||||
})
|
||||
|
||||
daysArray.push({
|
||||
date: format(day, "yyyy-MM-dd"),
|
||||
date: "Aired",
|
||||
isCurrentMonth: isSameMonth(day, currentDate),
|
||||
isToday: isToday(day),
|
||||
isSelected: false,
|
||||
|
||||
@@ -131,6 +131,16 @@ export function DebridSettings(props: DebridSettingsProps) {
|
||||
help="Let Seanime find the best torrent automatically, based on cache and resolution."
|
||||
/>
|
||||
|
||||
{f.watch("streamAutoSelect") && f.watch("provider") === "torbox" && (
|
||||
<Alert
|
||||
intent="warning-basic"
|
||||
title="Auto-select with TorBox"
|
||||
description={<p>
|
||||
Avoid using auto-select if you have a limited amount of downloads on your Debrid service.
|
||||
</p>}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Field.Select
|
||||
name="streamPreferredResolution"
|
||||
label="Preferred resolution"
|
||||
|
||||
@@ -1,104 +1,109 @@
|
||||
"use client"
|
||||
|
||||
|
||||
import { DatePicker } from "@/components/ui/date-picker"
|
||||
import { normalizeDate } from "@/lib/helpers/date"
|
||||
import React from "react"
|
||||
import { useGetDocs } from "@/api/hooks/docs.hooks"
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { LoadingSpinner } from "@/components/ui/loading-spinner"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import React, { useEffect } from "react"
|
||||
|
||||
export default function Page() {
|
||||
|
||||
const [value, setValue] = React.useState<Date | undefined>(undefined)
|
||||
const { data, isLoading } = useGetDocs()
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
setValue(normalizeDate("2024-04-15T00:00:00Z"))
|
||||
console.log(normalizeDate("2024-04-15T00:00:00Z"))
|
||||
console.log(new Date("2024-04-15T00:00:00Z"))
|
||||
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log(value)
|
||||
}, [value])
|
||||
useEffect(() => {
|
||||
console.log(data)
|
||||
})
|
||||
|
||||
if (isLoading) return <LoadingSpinner />
|
||||
|
||||
return (
|
||||
<div className="space-y-4 container py-10">
|
||||
<DatePicker
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
/>
|
||||
{/*{data?.routeGroups?.map((group, i) => (*/}
|
||||
{/* <div key={group.filename + i} className="space-y-4">*/}
|
||||
{/* <h4 className="">{group.filename}</h4>*/}
|
||||
{data?.toSorted((a, b) => a.filename?.localeCompare(b.filename)).map((group, i) => (
|
||||
<div key={group.filename + i} className="space-y-4">
|
||||
<h4 className=""><span>{group.filename}</span> <span className="text-gray-300">/</span>
|
||||
<span className="text-[--muted]"> {group.filename.replace(".go", "")}.hooks.ts</span></h4>
|
||||
<Accordion type="multiple" defaultValue={[]}>
|
||||
{group.handlers?.toSorted((a, b) => a.filename?.localeCompare(b.filename)).map((route, i) => (
|
||||
<AccordionItem value={route.name} key={route.name + i} className="space-y-2">
|
||||
<AccordionTrigger className="rounded flex-none w-full">
|
||||
<p className="flex gap-2 items-center">
|
||||
<Badge
|
||||
className="w-24 py-4"
|
||||
intent={(route.api!.methods?.includes("GET") && route.api!.methods?.length === 1) ? "success"
|
||||
: route.api!.methods?.includes("GET") ? "warning"
|
||||
: route.api!.methods?.includes("DELETE") ? "alert"
|
||||
: route.api!.methods?.includes("PATCH") ? "warning" : "primary"}
|
||||
>
|
||||
{route.api!.methods?.join(", ")}
|
||||
</Badge>
|
||||
<span className="font-semibold flex-none whitespace-nowrap">{route.api!.endpoint}</span>
|
||||
<span className="font-normal text-sm text-[--muted] flex-none whitespace-nowrap">{route.name}</span>
|
||||
{/*<span className="font-medium text-[--muted] text-sm truncate flex-shrink">({route.name.replace("Handle", "")})</span>*/}
|
||||
<span className="text-[--muted] text-[.97rem] whitespace-nowrap truncate text-ellipsis"> - {route.api!.summary}</span>
|
||||
</p>
|
||||
</AccordionTrigger>
|
||||
|
||||
{/* <Accordion type="multiple" defaultValue={[]}>*/}
|
||||
{/* {group.routes.toSorted((a, b) => a.endpoint.length - b.endpoint.length).map((route, i) => (*/}
|
||||
{/* <AccordionItem value={route.name} key={route.name + i} className="space-y-2">*/}
|
||||
{/* <AccordionTrigger className="rounded flex-none w-full">*/}
|
||||
{/* <p className="flex gap-2 items-center">*/}
|
||||
{/* <Badge*/}
|
||||
{/* className="w-24 py-4"*/}
|
||||
{/* intent={(route.methods.includes("GET") && route.methods.length === 1) ? "success"*/}
|
||||
{/* : route.methods.includes("GET") ? "warning"*/}
|
||||
{/* : route.methods.includes("DELETE") ? "alert"*/}
|
||||
{/* : route.methods.includes("PATCH") ? "warning" : "primary"}*/}
|
||||
{/* >*/}
|
||||
{/* {route.methods.join(", ")}*/}
|
||||
{/* </Badge>*/}
|
||||
{/* <span className="font-semibold flex-none whitespace-nowrap">{route.endpoint}</span>*/}
|
||||
{/* /!*<span className="font-medium text-[--muted] text-sm truncate flex-shrink">({route.name.replace("Handle", "")})</span>*!/*/}
|
||||
{/* <span className="text-[--muted] text-[.97rem] whitespace-nowrap truncate text-ellipsis"> - {route.summary}</span>*/}
|
||||
{/* </p>*/}
|
||||
{/* </AccordionTrigger>*/}
|
||||
|
||||
{/* <AccordionContent className="space-y-4 bg-gray-50 border rounded mb-4">*/}
|
||||
<AccordionContent className="space-y-4 border rounded mb-4">
|
||||
{/*<p className="font-bold">*/}
|
||||
{/* {route.name.replace("Handle", "")}*/}
|
||||
{/* {route.name}*/}
|
||||
{/*</p>*/}
|
||||
{/* {!!route.description && <p className="">{route.description}</p>}*/}
|
||||
|
||||
{/* <div className="space-y-2">*/}
|
||||
{/* <h5>Params</h5>*/}
|
||||
{/* <ul className="list-disc pl-4">*/}
|
||||
{/* {route.params.map((param, i) => (*/}
|
||||
{/* <li key={param.name + i} className="flex gap-2 items-center">*/}
|
||||
{/* <p className="font-medium">*/}
|
||||
{/* {param.name}*/}
|
||||
{/* {param.required && <span className="text-red-500">*</span>}*/}
|
||||
{/*<p className="">*/}
|
||||
{/* Used in: <span className="font-bold">{route.filename.replace(".go", "")}.hooks.ts</span>*/}
|
||||
{/*</p>*/}
|
||||
{/* <p className="text-[--muted]">{param.type}</p>*/}
|
||||
{/* <p>{param.description}</p>*/}
|
||||
{/* </li>*/}
|
||||
{/* ))}*/}
|
||||
{/* </ul>*/}
|
||||
{/* </div>*/}
|
||||
{!!route.api!.descriptions?.length && <div>
|
||||
{route.api!.descriptions?.map((desc, i) => (
|
||||
<p key={desc + i}>{desc}</p>
|
||||
))}
|
||||
</div>}
|
||||
|
||||
{/* <div className="space-y-2">*/}
|
||||
{/* <h5>Request Body Fields</h5>*/}
|
||||
{/* <ul className="list-disc pl-4">*/}
|
||||
{/* {route.requestBodyFields.map((field, i) => (*/}
|
||||
{/* <li key={field.name + i} className="flex gap-2 items-center">*/}
|
||||
{/* <p className="font-medium">{field.name}</p>*/}
|
||||
{/* <p className="text-[--muted]">{field.type}</p>*/}
|
||||
{/* <p>{field.description}</p>*/}
|
||||
{/* </li>*/}
|
||||
{/* ))}*/}
|
||||
{/* </ul>*/}
|
||||
{/* </div>*/}
|
||||
{!!route.api!.params?.length && <div className="space-y-2">
|
||||
<h5>URL Params</h5>
|
||||
<ul className="list-disc pl-4">
|
||||
{route.api!.params?.map((param, i) => (
|
||||
<li key={param.name + i} className="flex gap-2 items-center">
|
||||
<p className="font-medium">
|
||||
{param.name}
|
||||
{param.required && <span className="text-red-500">*</span>}
|
||||
</p>
|
||||
<p className="text-[--muted]">{param.typescriptType}</p>
|
||||
{param.descriptions?.map((desc, i) => (
|
||||
<p key={desc + i}>{desc}</p>
|
||||
))}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>}
|
||||
|
||||
{/* <div className="flex gap-2 items-center">*/}
|
||||
{/* <p className="font-medium text-[--muted]">Returns</p>*/}
|
||||
{/* <p className="font-bold text-brand-900">{route.returns}</p>*/}
|
||||
{/* </div>*/}
|
||||
{/* </AccordionContent>*/}
|
||||
{/* </AccordionItem>*/}
|
||||
{/* ))}*/}
|
||||
{/* </Accordion>*/}
|
||||
{!!route.api?.bodyFields?.length && <div className="space-y-2">
|
||||
<h5>Body</h5>
|
||||
<ul className="list-disc pl-4">
|
||||
{route.api?.bodyFields?.map((field, i) => (
|
||||
<li key={field.name + i} className="flex gap-2 items-center">
|
||||
<p className="font-medium">{field.jsonName} {field.required &&
|
||||
<span className="text-[--red]">*</span>}</p>
|
||||
<p className="text-[--muted]">{field.typescriptType}</p>
|
||||
{field.descriptions?.map((desc, i) => (
|
||||
<p key={desc + i}>{desc}</p>
|
||||
))}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>}
|
||||
|
||||
{/* <Separator />*/}
|
||||
{/* </div>*/}
|
||||
{/*))}*/}
|
||||
<div className="flex gap-2 items-center">
|
||||
<p className="font-medium text-[--muted]">Returns</p>
|
||||
<p className="font-bold text-brand-900">{route.api!.returnTypescriptType}</p>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
|
||||
<Separator />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user