main: fold the two infohash-dedup paths into one
dedupeTorrents and markInfoHash were the same map-of-infohash logic, one batch and one incremental, with the same hazard explained in prose four times. Both now go through a single seenInfoHash helper.
This commit is contained in:
BIN
13DL.me_Sho Hagoromo Isu v01-07.rar
Normal file
BIN
13DL.me_Sho Hagoromo Isu v01-07.rar
Normal file
Binary file not shown.
62
main.go
62
main.go
@@ -187,13 +187,27 @@ func (f *failedDownload) Stat() download.Stat {
|
|||||||
return download.Stat{Name: f.name, Status: download.Errored, Total: -1}
|
return download.Stat{Name: f.name, Status: download.Errored, Total: -1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dedupeTorrents finds torrent/magnet sources that name the same torrent as an
|
// seenInfoHash records source under its torrent infohash in seen and reports
|
||||||
// earlier source (same infohash), returning each duplicate URI mapped to the
|
// whether that infohash was already recorded, returning the source that first
|
||||||
// earlier source it repeats. HTTP(S) URLs and any source whose infohash can't be
|
// claimed it. The shared client keys torrents by infohash and does not refcount,
|
||||||
// read without a network fetch are never duplicates here. The shared client keys
|
// so two sources naming one torrent would have the first to finish Drop it out
|
||||||
// torrents by infohash and does not refcount, so build() turns each duplicate
|
// from under the other; callers fail the duplicate instead. A source whose
|
||||||
// into a failed download (duplicate infohash) rather than letting two jobs
|
// infohash can't be known without a network fetch (an HTTP URL), or that is
|
||||||
// share — and Drop — one torrent.
|
// v2-only or unreadable, is never a duplicate: it isn't recorded, so it runs.
|
||||||
|
func seenInfoHash(seen map[metainfo.Hash]string, source string, isFile bool) (first string, dup bool) {
|
||||||
|
h, ok := bt.SourceInfoHash(source, isFile)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if first, dup = seen[h]; dup {
|
||||||
|
return first, true
|
||||||
|
}
|
||||||
|
seen[h] = source
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// dedupeTorrents finds torrent/magnet sources that repeat an earlier source's
|
||||||
|
// infohash, returning each duplicate URI mapped to the source it repeats.
|
||||||
func dedupeTorrents(uris []string, follow bool) map[string]string {
|
func dedupeTorrents(uris []string, follow bool) map[string]string {
|
||||||
seen := map[metainfo.Hash]string{}
|
seen := map[metainfo.Hash]string{}
|
||||||
dup := map[string]string{}
|
dup := map[string]string{}
|
||||||
@@ -202,37 +216,13 @@ func dedupeTorrents(uris []string, follow bool) map[string]string {
|
|||||||
if k != kindMagnet && k != kindTorrent {
|
if k != kindMagnet && k != kindTorrent {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
h, ok := bt.SourceInfoHash(u, k == kindTorrent)
|
if first, isDup := seenInfoHash(seen, u, k == kindTorrent); isDup {
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if first, isDup := seen[h]; isDup {
|
|
||||||
dup[u] = first
|
dup[u] = first
|
||||||
} else {
|
|
||||||
seen[h] = u
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dup
|
return dup
|
||||||
}
|
}
|
||||||
|
|
||||||
// markInfoHash records the v1 infohash of the .torrent at path in seen and
|
|
||||||
// reports whether it repeats one already seen, returning the path first recorded
|
|
||||||
// under that infohash. Callers fail the duplicate rather than let two jobs share
|
|
||||||
// one torrent on the client (see bt.SourceInfoHash for why that aliasing is
|
|
||||||
// unsafe). A v2-only or unreadable infohash is never a duplicate — the dedup
|
|
||||||
// can't see it, so it runs.
|
|
||||||
func markInfoHash(seen map[metainfo.Hash]string, path string) (first string, dup bool) {
|
|
||||||
h, ok := bt.SourceInfoHash(path, true)
|
|
||||||
if !ok {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
if first, dup = seen[h]; dup {
|
|
||||||
return first, true
|
|
||||||
}
|
|
||||||
seen[h] = path
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func build(opts *cli.Options, targets []target) ([]job, *torrent.Client, error) {
|
func build(opts *cli.Options, targets []target) ([]job, *torrent.Client, error) {
|
||||||
flat := allURIs(targets)
|
flat := allURIs(targets)
|
||||||
// Two sources naming the same torrent (a magnet and its own .torrent, say)
|
// Two sources naming the same torrent (a magnet and its own .torrent, say)
|
||||||
@@ -383,11 +373,9 @@ func followUps(jobs []job, client *torrent.Client, bo bt.Options) []job {
|
|||||||
if _, err := bt.Files(path); err != nil {
|
if _, err := bt.Files(path); err != nil {
|
||||||
continue // downloaded file is not a valid torrent
|
continue // downloaded file is not a valid torrent
|
||||||
}
|
}
|
||||||
// Two fetched .torrent files that name the same torrent would share one
|
// build() dedupes pass-1 sources but cannot reach a not-yet-fetched HTTP
|
||||||
// torrent on the refcount-less client: the first to finish Drops it out
|
// .torrent, so dedupe the fetched ones here too against the same hazard.
|
||||||
// from under the other, stranding it at 0%. build() dedupes pass-1 sources
|
if first, dup := seenInfoHash(seen, path, true); dup {
|
||||||
// but cannot reach a not-yet-fetched HTTP .torrent, so dedupe here too.
|
|
||||||
if first, dup := markInfoHash(seen, path); dup {
|
|
||||||
out = append(out, job{source: path, dl: &failedDownload{
|
out = append(out, job{source: path, dl: &failedDownload{
|
||||||
name: path,
|
name: path,
|
||||||
err: fmt.Errorf("%w: same torrent as %s", errDuplicate, first),
|
err: fmt.Errorf("%w: same torrent as %s", errDuplicate, first),
|
||||||
|
|||||||
@@ -241,13 +241,13 @@ func TestFollowUpInfoHashDedup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
seen := map[metainfo.Hash]string{}
|
seen := map[metainfo.Hash]string{}
|
||||||
if first, dup := markInfoHash(seen, a); dup {
|
if first, dup := seenInfoHash(seen, a, true); dup {
|
||||||
t.Fatalf("a is the first occurrence, wrongly flagged a duplicate of %s", first)
|
t.Fatalf("a is the first occurrence, wrongly flagged a duplicate of %s", first)
|
||||||
}
|
}
|
||||||
if first, dup := markInfoHash(seen, b); !dup || first != a {
|
if first, dup := seenInfoHash(seen, b, true); !dup || first != a {
|
||||||
t.Errorf("b should duplicate a: got first=%q dup=%v, want %q true", first, dup, a)
|
t.Errorf("b should duplicate a: got first=%q dup=%v, want %q true", first, dup, a)
|
||||||
}
|
}
|
||||||
if _, dup := markInfoHash(seen, c); dup {
|
if _, dup := seenInfoHash(seen, c, true); dup {
|
||||||
t.Errorf("c is a distinct torrent, wrongly flagged a duplicate")
|
t.Errorf("c is a distinct torrent, wrongly flagged a duplicate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user