-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial version
- Loading branch information
Showing
28 changed files
with
2,559 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# misc | ||
/.idea/ | ||
/.vscode/ | ||
|
||
# configuration | ||
config.json | ||
*.db | ||
|
||
# vendor files | ||
/vendor/ | ||
|
||
# dist folder | ||
/dist/ | ||
|
||
# Web package | ||
web/rice-box.go | ||
web/trackarr-ui/package-lock.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
.DEFAULT_GOAL := build | ||
CMD := wantarr | ||
GOARCH := $(shell go env GOARCH) | ||
GOOS := $(shell go env GOOS) | ||
TARGET := ${GOOS}_${GOARCH} | ||
DIST_PATH := dist | ||
BUILD_PATH := ${DIST_PATH}/${CMD}_${TARGET} | ||
DESTDIR := /usr/local/bin | ||
GO_FILES := $(shell find . -path ./vendor -prune -or -type f -name '*.go' -print) | ||
GO_PACKAGES := $(shell go list -mod vendor ./...) | ||
GIT_COMMIT := $(shell git rev-parse --short HEAD) | ||
# GIT_BRANCH := $(shell git symbolic-ref --short HEAD) | ||
TIMESTAMP := $(shell date +%s) | ||
VERSION ?= 0.0.0-dev | ||
|
||
# Deps | ||
.PHONY: check_golangci | ||
check_golangci: | ||
@command -v golangci-lint >/dev/null || (echo "golangci-lint is required."; exit 1) | ||
.PHONY: check_goreleaser | ||
check_goreleaser: | ||
@command -v goreleaser >/dev/null || (echo "goreleaser is required."; exit 1) | ||
|
||
.PHONY: all ## Run tests, linting and build | ||
all: test lint build | ||
|
||
.PHONY: test-all ## Run tests and linting | ||
test-all: test lint | ||
|
||
.PHONY: test | ||
test: ## Run tests | ||
@echo "*** go test ***" | ||
go test -cover -mod vendor -v -race ${GO_PACKAGES} | ||
|
||
.PHONY: lint | ||
lint: check_golangci ## Run linting | ||
@echo "*** golangci-lint ***" | ||
golangci-lint run | ||
|
||
.PHONY: vendor | ||
vendor: ## Vendor files and tidy go.mod | ||
go mod vendor | ||
go mod tidy | ||
|
||
.PHONY: vendor_update | ||
vendor_update: ## Update vendor dependencies | ||
go get -u ./... | ||
${MAKE} vendor | ||
|
||
.PHONY: build | ||
build: fetch ${BUILD_PATH}/${CMD} ## Build application | ||
|
||
# Binary | ||
${BUILD_PATH}/${CMD}: ${GO_FILES} go.sum | ||
@echo "Building for ${TARGET}..." && \ | ||
mkdir -p ${BUILD_PATH} && \ | ||
CGO_ENABLED=1 go build \ | ||
-mod vendor \ | ||
-trimpath \ | ||
-ldflags "-s -w -X github.com/l3uddz/wantarr/build.Version=${VERSION} -X github.com/l3uddz/wantarr/build.GitCommit=${GIT_COMMIT} -X github.com/l3uddz/wantarr/build.Timestamp=${TIMESTAMP}" \ | ||
-o ${BUILD_PATH}/${CMD} \ | ||
. | ||
|
||
.PHONY: install | ||
install: build ## Install binary | ||
install -m 0755 ${BUILD_PATH}/${CMD} ${DESTDIR}/${CMD} | ||
|
||
.PHONY: clean | ||
clean: ## Cleanup | ||
rm -rf ${DIST_PATH} | ||
|
||
.PHONY: fetch | ||
fetch: ## Fetch vendor files | ||
go mod vendor | ||
|
||
.PHONY: release | ||
release: check_goreleaser fetch ## Generate a release, but don't publish | ||
goreleaser --skip-validate --skip-publish --rm-dist | ||
|
||
.PHONY: publish | ||
publish: check_goreleaser fetch ## Generate a release, and publish | ||
goreleaser --rm-dist | ||
|
||
.PHONY: snapshot | ||
snapshot: check_goreleaser fetch ## Generate a snapshot release | ||
goreleaser --snapshot --skip-validate --skip-publish --rm-dist | ||
|
||
.PHONY: help | ||
help: | ||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
version: 1.0.{build} | ||
|
||
image: | ||
- Ubuntu1804 | ||
- macOS | ||
|
||
platform: x64 | ||
|
||
branches: | ||
only: | ||
- master | ||
- develop | ||
|
||
deploy: | ||
- provider: GitHub | ||
auth_token: | ||
secure: japCKJyRSrHvPJV0hqZZGcRM9ZSR9GsTVh8SCCwbqiyuKTyB3OCcRMc6ICztczfV | ||
artifact: wantarr | ||
force_update: true | ||
on: | ||
branch: master | ||
APPVEYOR_REPO_TAG: true | ||
|
||
cache: | ||
- vendor -> go.mod, appveyor.yml | ||
|
||
artifacts: | ||
- path: dist/**/*.tar.tgz | ||
name: wantarr | ||
|
||
environment: | ||
GO111MODULE: on | ||
|
||
install: | ||
- go version | ||
- go env | ||
|
||
before_build: | ||
- |- | ||
if [ ${APPVEYOR_REPO_TAG} = "true" ]; then | ||
export VERSION=${APPVEYOR_REPO_TAG_NAME} | ||
fi | ||
build_script: | ||
- make build | ||
|
||
after_build: | ||
- |- | ||
export build=$(find dist/* -type d | sed 's!.*/!!') | ||
cd dist/$build | ||
tar -czf $build.tar.tgz wantarr | ||
cd ../.. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package build | ||
|
||
var ( | ||
// Build Vars | ||
Version string | ||
Timestamp string | ||
GitCommit string | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/l3uddz/wantarr/database" | ||
pvrObj "github.com/l3uddz/wantarr/pvr" | ||
"github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
"github.com/tommysolsen/capitalise" | ||
"time" | ||
) | ||
|
||
var cutoffCmd = &cobra.Command{ | ||
Use: "cutoff [PVR]", | ||
Short: "Search for cutoff unmet media files", | ||
Long: `This command can be used to search for cutoff unmet media files from the respective arr wanted list.`, | ||
|
||
Args: cobra.ExactArgs(1), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
// validate inputs | ||
if err := parseValidateInputs(args); err != nil { | ||
log.WithError(err).Fatal("Failed validating inputs") | ||
} | ||
|
||
// init pvr object | ||
if err := pvr.Init(); err != nil { | ||
log.WithError(err).Fatalf("Failed initializing pvr object for: %s", pvrName) | ||
} | ||
|
||
// load database | ||
if err := database.Init(flagDatabaseFile); err != nil { | ||
log.WithError(err).Fatal("Failed opening database file") | ||
} | ||
defer database.Close() | ||
|
||
// retrieve cutoff records from pvr and stash in database | ||
existingItemsCount := database.GetItemsCount(lowerPvrName, "cutoff") | ||
if flagRefreshCache || existingItemsCount < 1 { | ||
log.Infof("Retrieving cutoff unmet media from %s: %q", capitalise.First(pvrConfig.Type), pvrName) | ||
|
||
cutoffRecords, err := pvr.GetWantedCutoff() | ||
if err != nil { | ||
log.WithError(err).Fatal("Failed retrieving wanted cutoff unmet pvr items...") | ||
} | ||
|
||
// stash cutoff media in database | ||
log.Debug("Stashing media items in database...") | ||
|
||
if err := database.SetMediaItems(lowerPvrName, "cutoff", cutoffRecords); err != nil { | ||
log.WithError(err).Fatal("Failed stashing media items in database") | ||
} | ||
|
||
log.Info("Stashed media items") | ||
|
||
// remove media no longer cutoff unmet | ||
if existingItemsCount >= 1 { | ||
log.Debug("Removing media items from database that are no longer cutoff unmet...") | ||
|
||
removedItems, err := database.DeleteMissingItems(lowerPvrName, "cutoff", cutoffRecords) | ||
if err != nil { | ||
log.WithError(err).Fatal("Failed removing media items from database that are no longer cutoff unmet...") | ||
} | ||
|
||
log.WithField("removed_items", removedItems). | ||
Info("Removed media items from database that are no longer cutoff unmet") | ||
} | ||
} | ||
|
||
// start queue monitor | ||
if maxQueueSize > 0 { | ||
go func() { | ||
log.Info("Started queue monitor") | ||
for { | ||
// retrieve queue size | ||
qs, err := pvr.GetQueueSize() | ||
if err != nil { | ||
log.WithError(err).Error("Failed retrieving queue size, aborting...") | ||
continueRunning.Store(false) | ||
break | ||
} | ||
|
||
// check queue size | ||
if qs >= maxQueueSize { | ||
log.Warnf("Queue size has been reached, aborting....") | ||
continueRunning.Store(false) | ||
break | ||
} | ||
|
||
// sleep before check | ||
time.Sleep(10 * time.Second) | ||
} | ||
log.Info("Finished queue monitor") | ||
}() | ||
} | ||
|
||
// get media items from database | ||
mediaItems, err := database.GetMediaItems(lowerPvrName, "cutoff", false) | ||
if err != nil { | ||
log.WithError(err).Fatal("Failed retrieving media items from database...") | ||
} | ||
log.WithField("media_items", len(mediaItems)).Debug("Retrieved media items from database") | ||
|
||
// start searching | ||
var searchItems []pvrObj.MediaItem | ||
searchedItemsCount := 0 | ||
|
||
for _, item := range mediaItems { | ||
// abort if required (queue monitor will set this) | ||
if !continueRunning.Load() { | ||
break | ||
} | ||
|
||
// dont search this item if we already searched it within N days | ||
if item.LastSearchDateUtc != nil && !item.LastSearchDateUtc.IsZero() { | ||
retryAfterDate := item.LastSearchDateUtc.Add((24 * time.Hour) * pvrConfig.RetryDaysAge.Cutoff) | ||
if time.Now().UTC().Before(retryAfterDate) { | ||
log.WithField("retry_min_date", retryAfterDate). | ||
Tracef("Skipping media item %v until allowed retry date", item.Id) | ||
continue | ||
} | ||
} | ||
|
||
// add item to batch | ||
searchItems = append(searchItems, pvrObj.MediaItem{ | ||
ItemId: item.Id, | ||
AirDateUtc: item.AirDateUtc, | ||
}) | ||
|
||
// not enough items batched yet | ||
batchedItemsCount := len(searchItems) | ||
if batchedItemsCount < searchBatchSize { | ||
continue | ||
} | ||
|
||
// do search | ||
log.WithFields(logrus.Fields{ | ||
"search_items": batchedItemsCount, | ||
}).Info("Searching...") | ||
|
||
searchedItemsCount += batchedItemsCount | ||
|
||
if _, err := searchForItems(searchItems, "cutoff"); err != nil { | ||
log.WithError(err).Error("Failed searching for items...") | ||
} else { | ||
log.WithFields(logrus.Fields{ | ||
"searched_items": searchedItemsCount, | ||
}).Info("Search complete") | ||
} | ||
|
||
// reset batch | ||
searchItems = []pvrObj.MediaItem{} | ||
|
||
// max search items reached? | ||
if maxSearchItems > 0 && searchedItemsCount >= maxSearchItems { | ||
log.WithField("searched_items", searchedItemsCount). | ||
Info("Max search items reached, aborting...") | ||
break | ||
} | ||
|
||
// sleep before next batch | ||
time.Sleep(5 * time.Second) | ||
} | ||
|
||
// search for any leftover items from batching | ||
if continueRunning.Load() && len(searchItems) > 0 { | ||
// search items | ||
log.WithFields(logrus.Fields{ | ||
"search_items": len(searchItems), | ||
}).Info("Searching...") | ||
|
||
searchedItemsCount += len(searchItems) | ||
|
||
if _, err := searchForItems(searchItems, "cutoff"); err != nil { | ||
log.WithError(err).Error("Failed searching for items...") | ||
} else { | ||
log.WithFields(logrus.Fields{ | ||
"searched_items": searchedItemsCount, | ||
}).Info("Search complete") | ||
} | ||
} | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(cutoffCmd) | ||
|
||
cutoffCmd.Flags().IntVarP(&maxQueueSize, "queue-size", "q", 0, "Exit when queue size reached.") | ||
cutoffCmd.Flags().IntVarP(&maxSearchItems, "max-search", "m", 0, "Exit when this many items have been searched.") | ||
cutoffCmd.Flags().IntVarP(&searchBatchSize, "search-size", "s", 10, "How many items to search at once.") | ||
cutoffCmd.Flags().BoolVarP(&flagRefreshCache, "refresh-cache", "r", false, "Refresh the locally stored cache.") | ||
} |
Oops, something went wrong.