commit
6bc7923d16
@ -0,0 +1,8 @@
|
||||
## syntax=docker/dockerfile:1
|
||||
FROM golang:1.23
|
||||
WORKDIR /app
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY *.go ./
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /sonarr-mal-importer
|
||||
CMD ["/sonarr-mal-importer"]
|
||||
@ -0,0 +1,10 @@
|
||||
# sonarr-mal-importer
|
||||
This is basically a wrapper for [Jikan](jikan.moe) that converts a Jikan API call to a list with TVDB IDs that Sonarr can import the results.
|
||||
|
||||
**This API will spam calls that have pagination so make sure you set a limit in the query parameters so you don't get rate limited or IP banned!!**
|
||||
|
||||
Pulls MyAnimeList and TVDB ID associations from https://raw.githubusercontent.com/Kometa-Team/Anime-IDs/master/anime_ids.json.
|
||||
|
||||
## Supported Requests
|
||||
### GET /anime
|
||||
See https://docs.api.jikan.moe/#tag/anime/operation/getAnimeSearch for parameters.
|
||||
@ -0,0 +1,5 @@
|
||||
module github.com/gabehf/sonarr-mal-importer
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require github.com/darenliang/jikan-go v1.2.3
|
||||
@ -0,0 +1,2 @@
|
||||
github.com/darenliang/jikan-go v1.2.3 h1:Nw6ykJU47QW3rwiIBWHyy1cBNM1Cxsz0AVCdqIN278A=
|
||||
github.com/darenliang/jikan-go v1.2.3/go.mod h1:rv7ksvNqc1b0UK7mf1Uc3swPToJXd9EZQLz5C38jk9Q=
|
||||
@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/darenliang/jikan-go"
|
||||
)
|
||||
|
||||
type ResponseItem struct {
|
||||
Title string `json:"title"`
|
||||
MalId int `json:"malId"`
|
||||
TvdbId int `json:"tvdbId"`
|
||||
}
|
||||
type AnimeEntry struct {
|
||||
TvdbId int `json:"tvdb_id"`
|
||||
MalId interface{} `json:"mal_id"`
|
||||
}
|
||||
type IdList struct {
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.Println("Building Anime ID Associations...")
|
||||
malToTvdb := buildIdMap()
|
||||
http.HandleFunc("/anime", handleAnimeSearch(malToTvdb))
|
||||
log.Println("Listening on :3333")
|
||||
log.Fatal(http.ListenAndServe(":3333", nil))
|
||||
}
|
||||
|
||||
func handleAnimeSearch(malToTvdb map[int]int) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte(getAnimeSearch(malToTvdb, r)))
|
||||
}
|
||||
}
|
||||
|
||||
func getAnimeSearch(malToTvdb map[int]int, r *http.Request) string {
|
||||
q := r.URL.Query()
|
||||
|
||||
hasNextPage := true
|
||||
page := 0
|
||||
resp := []ResponseItem{}
|
||||
for hasNextPage {
|
||||
page++
|
||||
q.Set("page", strconv.Itoa(page))
|
||||
result, err := jikan.GetAnimeSearch(q)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// map the data
|
||||
for _, item := range result.Data {
|
||||
resp = append(resp,
|
||||
ResponseItem{
|
||||
item.Title,
|
||||
item.MalId,
|
||||
malToTvdb[item.MalId],
|
||||
})
|
||||
}
|
||||
hasNextPage = result.Pagination.HasNextPage
|
||||
}
|
||||
|
||||
respJson, err := json.MarshalIndent(resp, "", " ")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return string(respJson)
|
||||
}
|
||||
|
||||
func buildIdMap() map[int]int {
|
||||
// build the mal -> tvdb association table
|
||||
var idListBytes []byte
|
||||
resp, err := http.Get("https://raw.githubusercontent.com/Kometa-Team/Anime-IDs/master/anime_ids.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
idListBytes, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var animeMap map[string]AnimeEntry
|
||||
err = json.Unmarshal(idListBytes, &animeMap)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
malToTvdb := make(map[int]int, 0)
|
||||
for _, entry := range animeMap {
|
||||
if entry.MalId == nil {
|
||||
continue
|
||||
}
|
||||
var malIdList []int
|
||||
switch t := reflect.TypeOf(entry.MalId); t.Kind() {
|
||||
case reflect.String:
|
||||
s := strings.Split(entry.MalId.(string), ",")
|
||||
for _, ss := range s {
|
||||
id, err := strconv.Atoi(ss)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
malIdList = append(malIdList, id)
|
||||
}
|
||||
case reflect.Float64:
|
||||
malIdList = append(malIdList, int(entry.MalId.(float64)))
|
||||
}
|
||||
for _, val := range malIdList {
|
||||
malToTvdb[val] = entry.TvdbId
|
||||
}
|
||||
}
|
||||
return malToTvdb
|
||||
}
|
||||
Loading…
Reference in new issue