A little more updating.

This commit is contained in:
Matt Burchett 2018-11-15 05:34:24 +00:00
parent bc9461359f
commit 145fe10513
11 changed files with 170 additions and 57 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
config.json
config.json
housekeeper

View File

@ -1,2 +1,21 @@
# Housekeeper
Housekeeper is a cleanup tool for Plex environements.
It requires two things - Plex and PlexPy (now known as Tautulli). It has a option for communicating with Telegram for cleanup purposes.
Please use the example configuration file for configuration.
The command line args that can be passed:
```Usage of ./housekeeper:
-c string
Configuration to load
-check
Perform only a check. Do not delete. (default true)
-days int
days to poll
-delete
Perform the delete task.
-sectionid int
pick a section ID```

View File

@ -4,7 +4,9 @@ import (
"flag"
"log"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/communicator"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/eraser"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/locator"
)
@ -39,10 +41,14 @@ func main() {
var c string
var days int
var sectionID int
var check bool
var delete bool
flag.StringVar(&c, "c", "", "Configuration to load")
flag.IntVar(&days, "days", 0, "days to poll")
flag.IntVar(&sectionID, "sectionid", 0, "pick a section ID")
flag.BoolVar(&check, "check", true, "Perform only a check. Do not delete.")
flag.BoolVar(&delete, "delete", false, "Perform the delete task.")
flag.Parse()
if c == "" {
log.Fatal("You need to specify a configuration file.")
@ -55,5 +61,22 @@ func main() {
if err != nil {
log.Fatal(err)
}
locator.GetTitles(cfg, sectionID, days)
ids, titles := locator.GetTitles(cfg, sectionID, days)
if check {
err = communicator.TelegramPost(cfg, titles)
if err != nil {
log.Fatal(err)
}
}
if delete {
files := eraser.LookupFileLocation(cfg, ids)
err = eraser.DeleteMedia(delete, files)
if err != nil {
log.Println(err)
}
}
}

11
config_example.json Normal file
View File

@ -0,0 +1,11 @@
{
"baseURL": "http://dvr.example.com",
"plexPyContext": "/plexpy",
"plexPyAPIKey": "abc1234",
"plexToken": "ABC1234ABC1234",
"plexHost": "http://192.168.1.1",
"plexPort": 32400,
"telegramToken": "123456789:ABCDEFG",
"telegramChatID": "12345678",
"serverName": "Plex"
}

View File

@ -1 +1,38 @@
package communicator
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
)
// 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"
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")
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))
return err
}

View File

@ -9,13 +9,15 @@ import (
// Config - This struct will hold configuration components.
type Config struct {
BaseURL string `json:"baseURL"`
PlexPyContext string `json:"plexPyContext"`
PlexPyAPIKey string `json:"plexPyAPIKey"`
PlexToken string `json:"plexToken"`
PlexHost string `json:"plexHost"`
PlexPort int `json:"plexPort"`
TelegramToken string `json:"telegramToken"`
BaseURL string `json:"baseURL"`
PlexPyContext string `json:"plexPyContext"`
PlexPyAPIKey string `json:"plexPyAPIKey"`
PlexToken string `json:"plexToken"`
PlexHost string `json:"plexHost"`
PlexPort int `json:"plexPort"`
TelegramToken string `json:"telegramToken"`
TelegramChatID string `json:"telegramChatID"`
ServerName string `json:"serverName"`
}
//GetConfig gets the configuration values for the api using the file in the supplied configPath.

View File

@ -1 +1,64 @@
package eraser
import (
"encoding/xml"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/model"
)
// LookupFileLocation will gather a list of Information based on IDs returned by locator.GetTitles
func LookupFileLocation(config config.Config, ids []int) []string {
fileList := make([]string, 0)
for _, i := range ids {
plexURL := fmt.Sprintf("%s:%d%s%d%s%s", config.PlexHost, config.PlexPort, "/library/metadata/", i, "/?X-Plex-Token=", config.PlexToken)
req, err := http.NewRequest(http.MethodGet, plexURL, 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)
}
if err != nil {
log.Fatal(err)
}
plexModel := model.XMLPlexAPI{}
xml.Unmarshal(body, &plexModel)
fileList = append(fileList, filepath.Dir(plexModel.Video.Media.Part.File))
}
return fileList
}
// DeleteMedia will actually perform the deletion.
func DeleteMedia(delete bool, files []string) error {
var err error
if delete {
for _, i := range files {
fmt.Printf("Removing %v\n", i)
err = os.RemoveAll(i)
if err != nil {
log.Fatal(err)
}
}
}
return err
}

View File

@ -2,20 +2,19 @@ package locator
import (
"encoding/json"
"encoding/xml"
"fmt"
"io/ioutil"
"log"
"net/http"
"sort"
"strconv"
"strings"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/model"
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/util"
)
// GetCount will gather a count of media in a specific library, required for GetTitles.
func GetCount(config config.Config, sectionID int) int {
countURL := fmt.Sprintf("%s%s%s%s%s%d", config.BaseURL, config.PlexPyContext, "/api/v2?apikey=", config.PlexPyAPIKey, "&cmd=get_library&section_id=", sectionID)
req, err := http.NewRequest(http.MethodGet, countURL, nil)
@ -48,7 +47,7 @@ func GetCount(config config.Config, sectionID int) int {
}
// GetTitles will gather a list of information from the media in the library, based on the previous count.
func GetTitles(config config.Config, sectionID int, days int) {
func GetTitles(config config.Config, sectionID int, days int) ([]int, []string) {
count := GetCount(config, sectionID)
titlesURL := fmt.Sprintf("%s%s%s%s%s%d%s%d", config.BaseURL, config.PlexPyContext, "/api/v2?apikey=", config.PlexPyAPIKey, "&cmd=get_library_media_info&section_id=", sectionID, "&order_column=last_played&refresh=true&order_dir=asc&length=", count)
@ -108,47 +107,5 @@ func GetTitles(config config.Config, sectionID int, days int) {
}
}
sort.Strings(titles)
LookupFileLocation(config, ids)
return
}
func LookupFileLocation(config config.Config, ids []int) {
// fileLocations := make([]string, 0)
for _, i := range ids {
plexURL := fmt.Sprintf("%s:%d%s%d%s%s", config.PlexHost, config.PlexPort, "/library/metadata/", i, "/?X-Plex-Token=", config.PlexToken)
req, err := http.NewRequest(http.MethodGet, plexURL, 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)
}
if err != nil {
log.Fatal(err)
}
plexModel := model.XMLPlexAPI{}
xml.Unmarshal(body, &plexModel)
fileList := strings.Split(plexModel.Video.Media.Part.File, "/")
fmt.Printf("/%v/%v/%v/%v/%v\n", fileList[1], fileList[2], fileList[3], fileList[4], fileList[5])
}
// http://172.19.0.105:32400/library/metadata/9/?X-Plex-Token=K1WCALqRK5HVzSQ1J3bM
return ids, titles
}

View File

@ -79,7 +79,7 @@ type XMLPlexAPI struct {
Text string `xml:",chardata"`
RatingKey string `xml:"ratingKey,attr"`
Key string `xml:"key,attr"`
Guid string `xml:"guid,attr"`
GUID string `xml:"guid,attr"`
LibrarySectionTitle string `xml:"librarySectionTitle,attr"`
LibrarySectionID string `xml:"librarySectionID,attr"`
LibrarySectionKey string `xml:"librarySectionKey,attr"`

View File

@ -1 +0,0 @@
package rotator

View File

@ -2,6 +2,7 @@ package util
import "time"
// SubtractedEpoch will take the current time in epoch and subtract (days) worth of seconds from the current epoch time.
func SubtractedEpoch(days int) int64 {
now := time.Now()
unix := now.Unix()