Detect arithmetic progression
This commit is contained in:
@@ -4,8 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/debridmediamanager.com/zurg/pkg/utils"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
@@ -88,13 +90,23 @@ func (z *ZurgConfigV1) matchFilter(torrentName string, torrentIDs, fileNames []s
|
|||||||
if filter.RegexStr != "" {
|
if filter.RegexStr != "" {
|
||||||
regex, err := compilePattern(filter.RegexStr)
|
regex, err := compilePattern(filter.RegexStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
z.log.Errorf("Failed to compile regex: %v", err)
|
z.log.Errorf("Failed to compile regex %s error: %v", filter.RegexStr, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if regex.MatchString(torrentName) {
|
if regex.MatchString(torrentName) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if filter.NotRegexStr != "" {
|
||||||
|
regex, err := compilePattern(filter.NotRegexStr)
|
||||||
|
if err != nil {
|
||||||
|
z.log.Errorf("Failed to compile not_regex %s error: %v", filter.NotRegexStr, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !regex.MatchString(torrentName) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
if filter.ContainsStrict != "" && strings.Contains(torrentName, filter.ContainsStrict) {
|
if filter.ContainsStrict != "" && strings.Contains(torrentName, filter.ContainsStrict) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -127,7 +139,7 @@ func (z *ZurgConfigV1) matchFilter(torrentName string, torrentIDs, fileNames []s
|
|||||||
if filter.FileInsideRegexStr != "" {
|
if filter.FileInsideRegexStr != "" {
|
||||||
regex, err := compilePattern(filter.FileInsideRegexStr)
|
regex, err := compilePattern(filter.FileInsideRegexStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
z.log.Errorf("Failed to compile regex: %v", err)
|
z.log.Errorf("Failed to compile any_file_inside_regex %s error: %v", filter.FileInsideRegexStr, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, filename := range fileNames {
|
for _, filename := range fileNames {
|
||||||
@@ -136,12 +148,34 @@ func (z *ZurgConfigV1) matchFilter(torrentName string, torrentIDs, fileNames []s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if filter.FileInsideNotRegexStr != "" {
|
||||||
|
regex, err := compilePattern(filter.FileInsideNotRegexStr)
|
||||||
|
if err != nil {
|
||||||
|
z.log.Errorf("Failed to compile any_file_inside_not_regex %s error: %v", filter.FileInsideNotRegexStr, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, filename := range fileNames {
|
||||||
|
if !regex.MatchString(filename) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
if filter.FileInsideContains != "" {
|
if filter.FileInsideContains != "" {
|
||||||
for _, filename := range fileNames {
|
for _, filename := range fileNames {
|
||||||
if strings.Contains(strings.ToLower(filename), strings.ToLower(filter.FileInsideContains)) {
|
if strings.Contains(strings.ToLower(filename), strings.ToLower(filter.FileInsideContains)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if filter.FileInsideNotContains != "" {
|
||||||
|
for _, filename := range fileNames {
|
||||||
|
if !strings.Contains(strings.ToLower(filename), strings.ToLower(filter.FileInsideNotContains)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
if filter.FileInsideContainsStrict != "" {
|
if filter.FileInsideContainsStrict != "" {
|
||||||
for _, filename := range fileNames {
|
for _, filename := range fileNames {
|
||||||
@@ -149,6 +183,24 @@ func (z *ZurgConfigV1) matchFilter(torrentName string, torrentIDs, fileNames []s
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if filter.FileInsideNotContainsStrict != "" {
|
||||||
|
for _, filename := range fileNames {
|
||||||
|
if !strings.Contains(filename, filter.FileInsideNotContainsStrict) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if filter.HasEpisodes {
|
||||||
|
regex := regexp.MustCompile(`(?i)s\d\de\d\d`)
|
||||||
|
for _, filename := range fileNames {
|
||||||
|
if regex.MatchString(filename) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkArithmeticSequenceInFilenames(fileNames)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -188,3 +240,54 @@ func compilePattern(pattern string) (*regexp.Regexp, error) {
|
|||||||
|
|
||||||
return regexp.Compile(finalPattern)
|
return regexp.Compile(finalPattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasIncreasingSequence(arr []int) bool {
|
||||||
|
if len(arr) < 3 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < len(arr)-2; i++ {
|
||||||
|
if arr[i] < arr[i+1] && arr[i+1] < arr[i+2] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkArithmeticSequenceInFilenames(files []string) bool {
|
||||||
|
if len(files) < 3 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
r := regexp.MustCompile(`\d+`)
|
||||||
|
for _, file := range files {
|
||||||
|
if !utils.IsStreamable(file) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matches := r.FindAllStringIndex(file, -1)
|
||||||
|
for _, match := range matches {
|
||||||
|
numSet := make(map[int]struct{})
|
||||||
|
for _, file := range files {
|
||||||
|
if !utils.IsStreamable(file) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if match[0] >= 0 && match[1] <= len(file) {
|
||||||
|
num, err := strconv.Atoi(file[match[0]:match[1]])
|
||||||
|
if err == nil {
|
||||||
|
numSet[num] = struct{}{}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// out of bounds, ignore
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numList := make([]int, 0, len(numSet))
|
||||||
|
for num := range numSet {
|
||||||
|
numList = append(numList, num)
|
||||||
|
}
|
||||||
|
sort.Ints(numList)
|
||||||
|
if hasIncreasingSequence(numList) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,13 +18,19 @@ type FilterConditionsV1 struct {
|
|||||||
RegexStr string `yaml:"regex"`
|
RegexStr string `yaml:"regex"`
|
||||||
Contains string `yaml:"contains"`
|
Contains string `yaml:"contains"`
|
||||||
ContainsStrict string `yaml:"contains_strict"`
|
ContainsStrict string `yaml:"contains_strict"`
|
||||||
|
NotRegexStr string `yaml:"not_regex"`
|
||||||
NotContains string `yaml:"not_contains"`
|
NotContains string `yaml:"not_contains"`
|
||||||
NotContainsStrict string `yaml:"not_contains_strict"`
|
NotContainsStrict string `yaml:"not_contains_strict"`
|
||||||
|
|
||||||
And []*FilterConditionsV1 `yaml:"and"`
|
And []*FilterConditionsV1 `yaml:"and"`
|
||||||
Or []*FilterConditionsV1 `yaml:"or"`
|
Or []*FilterConditionsV1 `yaml:"or"`
|
||||||
|
|
||||||
FileInsideRegexStr string `yaml:"any_file_inside_regex"`
|
FileInsideRegexStr string `yaml:"any_file_inside_regex"`
|
||||||
FileInsideContains string `yaml:"any_file_inside_contains"`
|
FileInsideContains string `yaml:"any_file_inside_contains"`
|
||||||
FileInsideContainsStrict string `yaml:"any_file_inside_contains_strict"`
|
FileInsideContainsStrict string `yaml:"any_file_inside_contains_strict"`
|
||||||
|
FileInsideNotRegexStr string `yaml:"any_file_inside_not_regex"`
|
||||||
|
FileInsideNotContains string `yaml:"any_file_inside_not_contains"`
|
||||||
|
FileInsideNotContainsStrict string `yaml:"any_file_inside_not_contains_strict"`
|
||||||
|
|
||||||
|
HasEpisodes bool `yaml:"has_episodes"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/debridmediamanager.com/zurg/internal/config"
|
"github.com/debridmediamanager.com/zurg/internal/config"
|
||||||
"github.com/debridmediamanager.com/zurg/pkg/logutil"
|
"github.com/debridmediamanager.com/zurg/pkg/logutil"
|
||||||
"github.com/debridmediamanager.com/zurg/pkg/realdebrid"
|
"github.com/debridmediamanager.com/zurg/pkg/realdebrid"
|
||||||
|
"github.com/debridmediamanager.com/zurg/pkg/utils"
|
||||||
cmap "github.com/orcaman/concurrent-map/v2"
|
cmap "github.com/orcaman/concurrent-map/v2"
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -96,9 +97,7 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
anotherCt := 0
|
|
||||||
allTorrents.IterCb(func(accessKey string, torrent *Torrent) {
|
allTorrents.IterCb(func(accessKey string, torrent *Torrent) {
|
||||||
anotherCt++
|
|
||||||
// get IDs
|
// get IDs
|
||||||
var torrentIDs []string
|
var torrentIDs []string
|
||||||
for _, instance := range torrent.Instances {
|
for _, instance := range torrent.Instances {
|
||||||
@@ -293,11 +292,7 @@ func (t *TorrentManager) startRefreshJob() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get filenames
|
// get filenames
|
||||||
var filenames []string
|
filenames := torrent.SelectedFiles.Keys()
|
||||||
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
|
|
||||||
filenames = append(filenames, file.Path)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Map torrents to directories
|
// Map torrents to directories
|
||||||
switch t.cfg.GetVersion() {
|
switch t.cfg.GetVersion() {
|
||||||
case "v1":
|
case "v1":
|
||||||
@@ -593,7 +588,7 @@ func (t *TorrentManager) Repair(accessKey string) {
|
|||||||
var links []string
|
var links []string
|
||||||
streamableCount := 0
|
streamableCount := 0
|
||||||
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
|
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
|
||||||
if isStreamable(file.Path) {
|
if utils.IsStreamable(file.Path) {
|
||||||
streamableCount++
|
streamableCount++
|
||||||
}
|
}
|
||||||
fileCopy := &File{
|
fileCopy := &File{
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package torrent
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getFileIDs(files []File) []string {
|
func getFileIDs(files []File) []string {
|
||||||
@@ -16,14 +15,6 @@ func getFileIDs(files []File) []string {
|
|||||||
return fileIDs
|
return fileIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
func isStreamable(filePath string) bool {
|
|
||||||
return strings.HasSuffix(filePath, ".mkv") ||
|
|
||||||
strings.HasSuffix(filePath, ".mp4") ||
|
|
||||||
strings.HasSuffix(filePath, ".avi") ||
|
|
||||||
strings.HasSuffix(filePath, ".wmv") ||
|
|
||||||
strings.HasSuffix(filePath, ".m4v")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureDir(dirName string) error {
|
func ensureDir(dirName string) error {
|
||||||
if _, err := os.Stat(dirName); os.IsNotExist(err) {
|
if _, err := os.Stat(dirName); os.IsNotExist(err) {
|
||||||
err := os.Mkdir(dirName, 0755)
|
err := os.Mkdir(dirName, 0755)
|
||||||
|
|||||||
11
pkg/utils/streamable.go
Normal file
11
pkg/utils/streamable.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func IsStreamable(filePath string) bool {
|
||||||
|
return strings.HasSuffix(filePath, ".mkv") ||
|
||||||
|
strings.HasSuffix(filePath, ".mp4") ||
|
||||||
|
strings.HasSuffix(filePath, ".avi") ||
|
||||||
|
strings.HasSuffix(filePath, ".wmv") ||
|
||||||
|
strings.HasSuffix(filePath, ".m4v")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user