Skip to content
Merged
21 changes: 8 additions & 13 deletions internal/app/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,15 @@ func downloadEntries(
return
}

maxNum := 0
digits := 3

for _, entry := range entries {
if entry.Number > maxNum {
maxNum = entry.Number
}
digits = max(
digits,
entry.Number.IntegerDigits(),
)
}

digits := chapterDigits(maxNum)

if opts.EbookFriendly {
if err := os.MkdirAll(
opts.ScanDir,
Expand All @@ -162,10 +161,7 @@ func downloadEntries(
}

for _, entry := range entries {
chStr := fmt.Sprintf(
"%d",
entry.Number,
)
chStr := entry.Number.String()

log.Info(
"Downloading %s...\n",
Expand Down Expand Up @@ -205,10 +201,9 @@ func downloadEntries(
entryDir := filepath.Join(
opts.ScanDir,
fmt.Sprintf(
"%s %0*d",
"%s %s",
prefix,
digits,
entry.Number,
entry.Number.Padded(digits),
),
)

Expand Down
96 changes: 31 additions & 65 deletions internal/app/range_available.go
Original file line number Diff line number Diff line change
@@ -1,98 +1,64 @@
package app

import (
"fmt"
"math/big"
"sort"
"strings"

"github.com/EliasLd/goweeb/internal/source/common"
sourcetypes "github.com/EliasLd/goweeb/internal/source/types"
)

type AvailableRange struct {
Start int
End int
type AvailableRange struct{ Start, End common.ChapterNumber }

func nextInteger(previous, next common.ChapterNumber) bool {
if !previous.IsInteger() || !next.IsInteger() {
return false
}
value, ok := new(big.Int).SetString(previous.String(), 10)
if !ok {
return false
}
value.Add(value, big.NewInt(1))
return value.String() == next.String()
}

func AvailableChapterRanges(
entries []sourcetypes.Entry,
) []AvailableRange {
func AvailableChapterRanges(entries []sourcetypes.Entry) []AvailableRange {
if len(entries) == 0 {
return nil
}

numbers := make([]int, 0, len(entries))
seen := make(map[int]struct{})

for _, entry := range entries {
if _, exists := seen[entry.Number]; exists {
seen := make(map[string]struct{}, len(entries))
numbers := make([]common.ChapterNumber, 0, len(entries))
for _, e := range entries {
if _, ok := seen[e.Number.String()]; ok {
continue
}

seen[entry.Number] = struct{}{}
numbers = append(numbers, entry.Number)
seen[e.Number.String()] = struct{}{}
numbers = append(numbers, e.Number)
}

sort.Ints(numbers)

ranges := make([]AvailableRange, 0)

start := numbers[0]
previous := numbers[0]

sort.Slice(numbers, func(i, j int) bool { return numbers[i].Compare(numbers[j]) < 0 })
ranges := make([]AvailableRange, 0, len(numbers))
start, previous := numbers[0], numbers[0]
for _, number := range numbers[1:] {
if number == previous+1 {
if nextInteger(previous, number) {
previous = number
continue
}

ranges = append(
ranges,
AvailableRange{
Start: start,
End: previous,
},
)

start = number
previous = number
ranges = append(ranges, AvailableRange{Start: start, End: previous})
start, previous = number, number
}

ranges = append(
ranges,
AvailableRange{
Start: start,
End: previous,
},
)

return ranges
return append(ranges, AvailableRange{Start: start, End: previous})
}

// Joints ranges of available chapters/volumes in a single string
// according to the format:
// n_1-m_1,n_2-m_2,...,n_n-m_n
func FormatAvailableRanges(
entries []sourcetypes.Entry,
) string {
func FormatAvailableRanges(entries []sourcetypes.Entry) string {
ranges := AvailableChapterRanges(entries)

parts := make([]string, 0, len(ranges))

for _, r := range ranges {
if r.Start == r.End {
parts = append(
parts,
fmt.Sprintf("%d", r.Start),
)

parts = append(parts, r.Start.String())
continue
}

parts = append(
parts,
fmt.Sprintf("%d-%d", r.Start, r.End),
)
parts = append(parts, r.Start.String()+"-"+r.End.String())
}

return strings.Join(parts, ", ")
}
82 changes: 32 additions & 50 deletions internal/app/range_filter.go
Original file line number Diff line number Diff line change
@@ -1,71 +1,53 @@
package app

import (
"sort"

sourcetypes "github.com/EliasLd/goweeb/internal/source/types"
)

func FilterEntriesBySelection(
entries []sourcetypes.Entry,
selection RangeSelection,
) []sourcetypes.Entry {
if selection.All {
return entries
func FilterEntriesBySelection(entries []sourcetypes.Entry, selection RangeSelection) []sourcetypes.Entry {
if len(entries) == 0 {
return nil
}

ordered := append([]sourcetypes.Entry(nil), entries...)
sort.SliceStable(ordered, func(i, j int) bool { return ordered[i].Number.Compare(ordered[j].Number) < 0 })
unique := make([]sourcetypes.Entry, 0, len(ordered))
seen := make(map[string]struct{}, len(ordered))
for _, e := range ordered {
key := e.Number.String()
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
unique = append(unique, e)
}

if selection.All {
return unique
}
if len(selection.Ranges) == 0 {
return nil
}

// Last N is intentionally only allowed as a standalone selector.
if selection.Ranges[0].Mode == RangeLastN {
n := selection.Ranges[0].Range[1]

if n > len(entries) {
n = len(entries)
count := selection.Ranges[0].Count
if count > len(unique) {
count = len(unique)
}

return entries[len(entries)-n:]
return unique[len(unique)-count:]
}

selected := make(
[]sourcetypes.Entry,
0,
len(entries),
)

seen := make(map[int]struct{})

for _, entry := range entries {
for _, requested := range selection.Ranges {
start := requested.Range[0]
end := requested.Range[1]

matches := false

switch requested.Mode {
case RangeOpenEnded:
matches = entry.Number >= start

case RangeNormal:
matches =
entry.Number >= start &&
entry.Number <= end
}

if !matches {
continue
}

if _, exists := seen[entry.Number]; exists {
selected := make([]sourcetypes.Entry, 0, len(unique))
for _, e := range unique {
for _, r := range selection.Ranges {
matches := r.Mode == RangeOpenEnded && e.Number.Compare(r.Start) >= 0 ||
r.Mode == RangeNormal && e.Number.Compare(r.Start) >= 0 && e.Number.Compare(r.End) <= 0
if matches {
selected = append(selected, e)
break
}

seen[entry.Number] = struct{}{}
selected = append(selected, entry)

break
}
}

return selected
}
Loading
Loading