A little more updating.
This commit is contained in:
parent
bc9461359f
commit
145fe10513
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
config.json
|
config.json
|
||||||
|
housekeeper
|
19
README.md
19
README.md
@ -1,2 +1,21 @@
|
|||||||
# Housekeeper
|
# 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```
|
25
cmd/main.go
25
cmd/main.go
@ -4,7 +4,9 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/communicator"
|
||||||
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
|
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
|
||||||
|
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/eraser"
|
||||||
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/locator"
|
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/locator"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,10 +41,14 @@ func main() {
|
|||||||
var c string
|
var c string
|
||||||
var days int
|
var days int
|
||||||
var sectionID int
|
var sectionID int
|
||||||
|
var check bool
|
||||||
|
var delete bool
|
||||||
|
|
||||||
flag.StringVar(&c, "c", "", "Configuration to load")
|
flag.StringVar(&c, "c", "", "Configuration to load")
|
||||||
flag.IntVar(&days, "days", 0, "days to poll")
|
flag.IntVar(&days, "days", 0, "days to poll")
|
||||||
flag.IntVar(§ionID, "sectionid", 0, "pick a section ID")
|
flag.IntVar(§ionID, "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()
|
flag.Parse()
|
||||||
if c == "" {
|
if c == "" {
|
||||||
log.Fatal("You need to specify a configuration file.")
|
log.Fatal("You need to specify a configuration file.")
|
||||||
@ -55,5 +61,22 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
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
11
config_example.json
Normal 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"
|
||||||
|
}
|
@ -1 +1,38 @@
|
|||||||
package communicator
|
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
|
||||||
|
}
|
||||||
|
@ -9,13 +9,15 @@ import (
|
|||||||
|
|
||||||
// Config - This struct will hold configuration components.
|
// Config - This struct will hold configuration components.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
BaseURL string `json:"baseURL"`
|
BaseURL string `json:"baseURL"`
|
||||||
PlexPyContext string `json:"plexPyContext"`
|
PlexPyContext string `json:"plexPyContext"`
|
||||||
PlexPyAPIKey string `json:"plexPyAPIKey"`
|
PlexPyAPIKey string `json:"plexPyAPIKey"`
|
||||||
PlexToken string `json:"plexToken"`
|
PlexToken string `json:"plexToken"`
|
||||||
PlexHost string `json:"plexHost"`
|
PlexHost string `json:"plexHost"`
|
||||||
PlexPort int `json:"plexPort"`
|
PlexPort int `json:"plexPort"`
|
||||||
TelegramToken string `json:"telegramToken"`
|
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.
|
//GetConfig gets the configuration values for the api using the file in the supplied configPath.
|
||||||
|
@ -1 +1,64 @@
|
|||||||
package eraser
|
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
|
||||||
|
}
|
||||||
|
@ -2,20 +2,19 @@ package locator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
|
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
|
||||||
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/model"
|
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/model"
|
||||||
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/util"
|
"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 {
|
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§ion_id=", sectionID)
|
countURL := fmt.Sprintf("%s%s%s%s%s%d", config.BaseURL, config.PlexPyContext, "/api/v2?apikey=", config.PlexPyAPIKey, "&cmd=get_library§ion_id=", sectionID)
|
||||||
req, err := http.NewRequest(http.MethodGet, countURL, nil)
|
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.
|
// 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)
|
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§ion_id=", sectionID, "&order_column=last_played&refresh=true&order_dir=asc&length=", count)
|
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§ion_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)
|
sort.Strings(titles)
|
||||||
|
return ids, 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
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ type XMLPlexAPI struct {
|
|||||||
Text string `xml:",chardata"`
|
Text string `xml:",chardata"`
|
||||||
RatingKey string `xml:"ratingKey,attr"`
|
RatingKey string `xml:"ratingKey,attr"`
|
||||||
Key string `xml:"key,attr"`
|
Key string `xml:"key,attr"`
|
||||||
Guid string `xml:"guid,attr"`
|
GUID string `xml:"guid,attr"`
|
||||||
LibrarySectionTitle string `xml:"librarySectionTitle,attr"`
|
LibrarySectionTitle string `xml:"librarySectionTitle,attr"`
|
||||||
LibrarySectionID string `xml:"librarySectionID,attr"`
|
LibrarySectionID string `xml:"librarySectionID,attr"`
|
||||||
LibrarySectionKey string `xml:"librarySectionKey,attr"`
|
LibrarySectionKey string `xml:"librarySectionKey,attr"`
|
||||||
|
@ -1 +0,0 @@
|
|||||||
package rotator
|
|
@ -2,6 +2,7 @@ package util
|
|||||||
|
|
||||||
import "time"
|
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 {
|
func SubtractedEpoch(days int) int64 {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
unix := now.Unix()
|
unix := now.Unix()
|
||||||
|
Loading…
Reference in New Issue
Block a user