diff --git a/.gitignore b/.gitignore index d7b65e5..7d5834f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ config.json -housekeeper \ No newline at end of file +housekeeper +.vscode \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 483e209..d2b1fbc 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -57,6 +57,8 @@ func main() { } } else if libraryType == "show" { files := eraser.LookupTVFileLocation(cfg, ids) + sonarrIDs := locator.GetSonarrIDs(cfg, titles) + eraser.DeleteSeriesFromSonarr(cfg, sonarrIDs) err = eraser.DeleteFiles(delete, files) if err != nil { log.Println(err) diff --git a/pkg/communicator/communicator.go b/pkg/communicator/communicator.go index 5fde12b..e1cce68 100644 --- a/pkg/communicator/communicator.go +++ b/pkg/communicator/communicator.go @@ -11,28 +11,38 @@ import ( "git.linuxrocker.com/mattburchett/Housekeeper/pkg/config" ) +type error interface { + Error() string +} + // TelegramPost will send a message to a specific ChatID in Telegram containing the list of items to be cleaned with this cleaner. func TelegramPost(config config.Config, titles []string) error { - url := "https://api.telegram.org/bot" + config.TelegramToken + "/sendMessage" + var err error + if len(titles) != 0 { + url := "https://api.telegram.org/bot" + config.TelegramToken + "/sendMessage" - values := map[string]string{"chat_id": config.TelegramChatID, "text": "The following items are to be removed from " + config.ServerName + " in 24 hours. Please go to Plex and start the title to keep it on " + config.ServerName + ". You do not need to keep watching, just hit play and load a few seconds.\n\n" + fmt.Sprintf("%v", strings.Join(titles, "\n")), "disable_notifications": "true"} + values := map[string]string{"chat_id": config.TelegramChatID, "text": "The following items are to be removed from " + config.ServerName + " in 24 hours. Please go to Plex and start the title to keep it on " + config.ServerName + ". You do not need to keep watching, just hit play and load a few seconds.\n\n" + fmt.Sprintf("%v", strings.Join(titles, "\n")), "disable_notifications": "true"} - jsonValue, _ := json.Marshal(values) - req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonValue)) - req.Header.Set("X-Custom-Header", "Housekeeper") - req.Header.Set("Content-Type", "application/json") + jsonValue, _ := json.Marshal(values) + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonValue)) + req.Header.Set("X-Custom-Header", "Housekeeper") + req.Header.Set("Content-Type", "application/json") - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - panic(err) + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + fmt.Println("response Status:", resp.Status) + fmt.Println("response Headers:", resp.Header) + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println("response Body:", string(body)) + + } else { + fmt.Println("There are no titles, therefore no message to send!") } - defer resp.Body.Close() - - fmt.Println("response Status:", resp.Status) - fmt.Println("response Headers:", resp.Header) - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println("response Body:", string(body)) return err } diff --git a/pkg/config/config.go b/pkg/config/config.go index 938c246..0bf9643 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -18,6 +18,8 @@ type Config struct { TelegramToken string `json:"telegramToken"` TelegramChatID string `json:"telegramChatID"` ServerName string `json:"serverName"` + SonarrContext string `json:"sonarrContext"` + SonarrAPIKey string `json:"sonarrAPIKey"` } //GetConfig gets the configuration values for the api using the file in the supplied configPath. diff --git a/pkg/eraser/eraser.go b/pkg/eraser/eraser.go index 197e24c..f080cd2 100644 --- a/pkg/eraser/eraser.go +++ b/pkg/eraser/eraser.go @@ -1,6 +1,7 @@ package eraser import ( + "encoding/json" "encoding/xml" "fmt" "io/ioutil" @@ -8,6 +9,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "git.linuxrocker.com/mattburchett/Housekeeper/pkg/config" "git.linuxrocker.com/mattburchett/Housekeeper/pkg/model" @@ -92,6 +94,39 @@ func LookupTVFileLocation(config config.Config, ids []int) []string { return results } +func DeleteSeriesFromSonarr(config config.Config, ids []int) { + for _, i := range ids { + sonarrURL := fmt.Sprintf("%s%s%s%d%s%s", config.BaseURL, config.SonarrContext, "/api/series/", i, "/?deleteFiles=true&apikey=", config.SonarrAPIKey) + req, err := http.NewRequest(http.MethodDelete, sonarrURL, nil) + if err != nil { + log.Fatal(err) + } + + httpClient := http.Client{} + req.Header.Set("User-Agent", "Housekeeper") + + res, getErr := httpClient.Do(req) + if getErr != nil { + log.Fatal(getErr) + } + + body, readErr := ioutil.ReadAll(res.Body) + if readErr != nil { + log.Fatal(readErr) + } + + deleteModel := model.SonarrResponse{} + jsonErr := json.Unmarshal(body, &deleteModel) + if jsonErr != nil { + log.Fatal(jsonErr) + } + + if strings.Contains("does not exist", deleteModel.Message) { + log.Printf("The following ID does not exist: %v", i) + } + } +} + // DeleteFiles will actually perform the deletion. func DeleteFiles(delete bool, files []string) error { var err error diff --git a/pkg/locator/locator.go b/pkg/locator/locator.go index be3f1f3..8506f41 100644 --- a/pkg/locator/locator.go +++ b/pkg/locator/locator.go @@ -144,3 +144,42 @@ func GetTitles(config config.Config, sectionID int, days int) ([]int, []string) sort.Strings(titles) return ids, titles } + +// GetSonarrIDs gets the IDs to delete from the title list in PlexPy. +func GetSonarrIDs(config config.Config, titles []string) []int { + ids := make([]int, 0) + sonarrURL := fmt.Sprintf("%s%s%s%s", config.BaseURL, config.SonarrContext, "/api/series?apikey=", config.SonarrAPIKey) + req, err := http.NewRequest(http.MethodGet, sonarrURL, nil) + if err != nil { + log.Fatal(err) + } + + httpClient := http.Client{} + req.Header.Set("User-Agent", "Housekeeper") + + res, getErr := httpClient.Do(req) + if getErr != nil { + log.Fatal(getErr) + } + + body, readErr := ioutil.ReadAll(res.Body) + if readErr != nil { + log.Fatal(readErr) + } + + sonarrModel := model.SonarrSeries{} + jsonErr := json.Unmarshal(body, &sonarrModel) + if jsonErr != nil { + log.Fatal(jsonErr) + } + + for _, r := range sonarrModel { + for _, s := range titles { + if r.Title == s { + ids = append(ids, r.ID) + } + } + } + + return ids +} diff --git a/pkg/model/eraser_model.go b/pkg/model/eraser_model.go index a79a5f1..6fed8cd 100644 --- a/pkg/model/eraser_model.go +++ b/pkg/model/eraser_model.go @@ -230,3 +230,8 @@ type XMLPlexTVAPI struct { } `xml:"Writer"` } `xml:"Video"` } + +type SonarrResponse struct { + Message string `json:"message"` + Description string `json:"description"` +} diff --git a/pkg/model/locator_model.go b/pkg/model/locator_model.go index b3308e7..642f5ae 100644 --- a/pkg/model/locator_model.go +++ b/pkg/model/locator_model.go @@ -1,6 +1,9 @@ package model -import "encoding/xml" +import ( + "encoding/xml" + "time" +) // PlexPyLibraryInfo will gather all the library related info. We really just need the count from this... type PlexPyLibraryInfo struct { @@ -88,3 +91,66 @@ type XMLPlexLibraryType struct { Search string `xml:"search,attr"` } `xml:"Directory"` } + +// SonarrSeries type takes all the data from Sonarr and places it in a struct +type SonarrSeries []struct { + Title string `json:"title"` + AlternateTitles []struct { + Title string `json:"title"` + SeasonNumber int `json:"seasonNumber"` + } `json:"alternateTitles"` + SortTitle string `json:"sortTitle"` + SeasonCount int `json:"seasonCount"` + TotalEpisodeCount int `json:"totalEpisodeCount"` + EpisodeCount int `json:"episodeCount"` + EpisodeFileCount int `json:"episodeFileCount"` + SizeOnDisk int64 `json:"sizeOnDisk"` + Status string `json:"status"` + Overview string `json:"overview"` + PreviousAiring time.Time `json:"previousAiring"` + Network string `json:"network"` + AirTime string `json:"airTime,omitempty"` + Images []struct { + CoverType string `json:"coverType"` + URL string `json:"url"` + } `json:"images"` + Seasons []struct { + SeasonNumber int `json:"seasonNumber"` + Monitored bool `json:"monitored"` + Statistics struct { + PreviousAiring time.Time `json:"previousAiring"` + EpisodeFileCount int `json:"episodeFileCount"` + EpisodeCount int `json:"episodeCount"` + TotalEpisodeCount int `json:"totalEpisodeCount"` + SizeOnDisk int64 `json:"sizeOnDisk"` + PercentOfEpisodes float64 `json:"percentOfEpisodes"` + } `json:"statistics"` + } `json:"seasons"` + Year int `json:"year"` + Path string `json:"path"` + ProfileID int `json:"profileId"` + SeasonFolder bool `json:"seasonFolder"` + Monitored bool `json:"monitored"` + UseSceneNumbering bool `json:"useSceneNumbering"` + Runtime int `json:"runtime"` + TvdbID int `json:"tvdbId"` + TvRageID int `json:"tvRageId"` + TvMazeID int `json:"tvMazeId"` + FirstAired time.Time `json:"firstAired"` + LastInfoSync time.Time `json:"lastInfoSync"` + SeriesType string `json:"seriesType"` + CleanTitle string `json:"cleanTitle"` + ImdbID string `json:"imdbId,omitempty"` + TitleSlug string `json:"titleSlug"` + Certification string `json:"certification,omitempty"` + Genres []string `json:"genres"` + Tags []interface{} `json:"tags"` + Added time.Time `json:"added"` + Ratings struct { + Votes int `json:"votes"` + Value float64 `json:"value"` + } `json:"ratings"` + QualityProfileID int `json:"qualityProfileId"` + ID int `json:"id"` + NextAiring time.Time `json:"nextAiring,omitempty"` +}