Skip to content

Commit

Permalink
fix unsupported protocol version 303 when accessing some sites with j…
Browse files Browse the repository at this point in the history
…a3; improve public sites support
  • Loading branch information
sagan committed Mar 4, 2024
1 parent 36312f6 commit 7994795
Show file tree
Hide file tree
Showing 15 changed files with 296 additions and 72 deletions.
26 changes: 16 additions & 10 deletions cmd/add/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/sagan/ptool/client"
"github.com/sagan/ptool/cmd"
"github.com/sagan/ptool/config"
"github.com/sagan/ptool/constants"
"github.com/sagan/ptool/util"
"github.com/sagan/ptool/util/helper"
"github.com/sagan/ptool/util/torrentutil"
Expand Down Expand Up @@ -90,9 +91,7 @@ func add(cmd *cobra.Command, args []string) error {
}
if stdin, err := io.ReadAll(os.Stdin); err != nil {
return fmt.Errorf("failed to read stdin: %v", err)
} else if bytes.HasPrefix(stdin, []byte("d8:announce")) {
// Matches with .torrent file magic number.
// See: https://en.wikipedia.org/wiki/Torrent_file , https://en.wikipedia.org/wiki/Bencode .
} else if bytes.HasPrefix(stdin, []byte(constants.TORRENT_FILE_MAGIC_NUMBER)) {
stdinTorrentContents = stdin
} else if data, err := shlex.Split(string(stdin)); err != nil {
return fmt.Errorf("failed to parse stdin to tokens: %v", err)
Expand Down Expand Up @@ -133,12 +132,20 @@ func add(cmd *cobra.Command, args []string) error {
continue
}
content, tinfo, siteInstance, siteName, filename, id, err :=
helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, stdinTorrentContents)
helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, stdinTorrentContents, true)
if err != nil {
fmt.Printf("✕add (%d/%d) %s: %v\n", i+1, cntAll, torrent, err)
errorCnt++
continue
}
size := int64(0)
infoHash := ""
contentPath := ""
if tinfo != nil {
size = tinfo.Size
infoHash = tinfo.InfoHash
contentPath = tinfo.ContentPath
}
hr := false
if siteInstance != nil {
hr = siteInstance.GetSiteConfig().GlobalHnR
Expand Down Expand Up @@ -168,25 +175,24 @@ func add(cmd *cobra.Command, args []string) error {
err = clientInstance.AddTorrent(content, option, nil)
if err != nil {
fmt.Printf("✕add (%d/%d) %s (site=%s): failed to add torrent to client: %v // %s\n",
i+1, cntAll, torrent, siteName, err, tinfo.ContentPath)
i+1, cntAll, torrent, siteName, err, contentPath)
errorCnt++
continue
}
if siteInstance == nil && torrent != "-" {
if renameAdded {
if err := os.Rename(torrent, torrent+".added"); err != nil {
log.Debugf("Failed to rename %s to *.added: %v // %s", torrent, err, tinfo.ContentPath)
log.Debugf("Failed to rename %s to *.added: %v // %s", torrent, err, contentPath)
}
} else if deleteAdded {
if err := os.Remove(torrent); err != nil {
log.Debugf("Failed to delete %s: %v // %s", torrent, err, tinfo.ContentPath)
log.Debugf("Failed to delete %s: %v // %s", torrent, err, contentPath)
}
}
}
cntAdded++
sizeAdded += tinfo.Size
fmt.Printf("✓add (%d/%d) %s (site=%s). infoHash=%s // %s\n",
i+1, cntAll, torrent, siteName, tinfo.InfoHash, tinfo.ContentPath)
sizeAdded += size
fmt.Printf("✓add (%d/%d) %s (site=%s). infoHash=%s // %s\n", i+1, cntAll, torrent, siteName, infoHash, contentPath)
}
fmt.Printf("\nDone. Added torrent (Size/Cnt): %s / %d; ErrorCnt: %d\n",
util.BytesSize(float64(sizeAdded)), cntAdded, errorCnt)
Expand Down
2 changes: 1 addition & 1 deletion cmd/dltorrent/dltorrent.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func dltorrent(cmd *cobra.Command, args []string) error {

for _, torrent := range torrents {
content, tinfo, _, siteName, filename, id, err :=
helper.GetTorrentContent(torrent, defaultSite, false, true, nil)
helper.GetTorrentContent(torrent, defaultSite, false, true, nil, true)
if err != nil {
fmt.Printf("✕download %s (site=%s): %v\n", torrent, siteName, err)
errorCnt++
Expand Down
2 changes: 1 addition & 1 deletion cmd/parsetorrent/parsetorrent.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func parsetorrent(cmd *cobra.Command, args []string) error {
errorCnt := int64(0)

for i, torrent := range torrents {
_, tinfo, _, _, _, _, err := helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, nil)
_, tinfo, _, _, _, _, err := helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, nil, false)
if err != nil {
log.Errorf("Failed to get %s: %v", torrent, err)
errorCnt++
Expand Down
9 changes: 6 additions & 3 deletions cmd/tidyup/tidyup.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/sagan/ptool/client"
"github.com/sagan/ptool/cmd"
"github.com/sagan/ptool/site/public"
"github.com/sagan/ptool/site/tpl"
"github.com/sagan/ptool/util"
)
Expand Down Expand Up @@ -64,11 +65,13 @@ func tidyup(cmd *cobra.Command, args []string) error {
sitename := ""
ok := false
if sitename, ok = domainSiteMap[domain]; !ok {
domainSiteMap[domain], err = tpl.GuessSiteByDomain(domain, "")
if err != nil {
if domainSiteMap[domain], err = tpl.GuessSiteByDomain(domain, ""); err == nil {
sitename = domainSiteMap[domain]
} else if site := public.GetSiteByDomain(domain); site != nil {
sitename = site.Name
} else {
log.Warnf("Failed to find match site for %s: %v", domain, err)
}
sitename = domainSiteMap[domain]
}
if sitename != "" {
existingSitename := torrent.GetSiteFromTag()
Expand Down
2 changes: 1 addition & 1 deletion cmd/verifytorrent/verifytorrent.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func verifytorrent(cmd *cobra.Command, args []string) error {
if showAll && i > 0 {
fmt.Printf("\n")
}
_, tinfo, _, _, _, _, err := helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, nil)
_, tinfo, _, _, _, _, err := helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, nil, false)
if err != nil {
fmt.Printf("X torrent %s: failed to get: %v\n", torrent, err)
errorCnt++
Expand Down
3 changes: 2 additions & 1 deletion cmd/xseedadd/xseedadd.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ func xseedadd(cmd *cobra.Command, args []string) error {
})
errorCnt := int64(0)
for _, torrent := range torrents {
content, tinfo, _, sitename, _, _, err := helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, nil)
content, tinfo, _, sitename, _, _, err :=
helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, nil, false)
if err != nil {
fmt.Printf("X%s: failed to get: %v\n", torrent, err)
errorCnt++
Expand Down
2 changes: 1 addition & 1 deletion cmd/xseedcheck/xseedcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func xseedcheck(cmd *cobra.Command, args []string) error {
if err != nil {
return fmt.Errorf("failed to create client: %v", err)
}
_, tinfo, _, _, _, _, err := helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, nil)
_, tinfo, _, _, _, _, err := helper.GetTorrentContent(torrent, defaultSite, forceLocal, false, nil, false)
if err != nil {
return fmt.Errorf("failed to get %s: %v", torrent, err)
}
Expand Down
5 changes: 5 additions & 0 deletions constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package constants

// .torrent file magic number.
// See: https://en.wikipedia.org/wiki/Torrent_file , https://en.wikipedia.org/wiki/Bencode .
const TORRENT_FILE_MAGIC_NUMBER = "d8:announce"
1 change: 1 addition & 0 deletions site/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
_ "github.com/sagan/ptool/site/gazellepw"
_ "github.com/sagan/ptool/site/nexusphp"
_ "github.com/sagan/ptool/site/tnode"
_ "github.com/sagan/ptool/site/torrenttrader"
_ "github.com/sagan/ptool/site/tpl"
_ "github.com/sagan/ptool/site/unit3d"
)
66 changes: 66 additions & 0 deletions site/public/public.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package public

import (
"net/url"
"regexp"

"github.com/sagan/ptool/util"
)

// public Bittorrent sites. add cmd supports adding torrent url of these sites

type PublicBittorrentSite struct {
Name string
Domains []string // first one of Domains is considered as primary domain
TrackerDomains []string
TorrentDownloadUrl string // placeholders: {{origin}}, {{domain}}, {{id}}
TorrentUrlIdRegexp *regexp.Regexp // extract torrent id (in "id" subgroup) from url
}

var (
Sites = []*PublicBittorrentSite{
{
Name: "nyaa",
Domains: []string{"sukebei.nyaa.si", "nyaa.si"},
// https://sukebei.nyaa.si/upload
// https://nyaa.si/upload
TrackerDomains: []string{"sukebei.tracker.wf", "nyaa.tracker.wf"},
TorrentUrlIdRegexp: regexp.MustCompile(`\bview/(?P<id>\d+)\b`),
TorrentDownloadUrl: `{{origin}}/download/{{id}}.torrent`,
},
}
// site name & domain (main or tracker) => site
SitesMap = map[string]*PublicBittorrentSite{}
)

func init() {
for _, btsite := range Sites {
SitesMap[btsite.Name] = btsite
for _, domain := range btsite.Domains {
SitesMap[domain] = btsite
}
for _, domain := range btsite.TrackerDomains {
SitesMap[domain] = btsite
}
}
}

// Get a site by a website or tracker url or domain.
func GetSiteByDomain(defaultSite string, domainOrUrls ...string) *PublicBittorrentSite {
if SitesMap[defaultSite] != nil {
return SitesMap[defaultSite]
}
for _, domain := range domainOrUrls {
if util.IsUrl(domain) {
if urlObj, err := url.Parse(domain); err != nil {
continue
} else {
domain = urlObj.Hostname()
}
}
if SitesMap[domain] != nil {
return SitesMap[domain]
}
}
return nil
}
145 changes: 145 additions & 0 deletions site/torrenttrader/torrenttrader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package torrenttrader

// torrenttrader is a legacy torrents software in php
// v3: https://github.com/MicrosoulV3/TorrentTrader-v3
// used by: aidoru-online.me
// torrent download url: https://aidoru-online.me/download.php?id=12345

import (
"fmt"
"net/url"
"strings"
"time"

"github.com/Noooste/azuretls-client"
log "github.com/sirupsen/logrus"

"github.com/sagan/ptool/config"
"github.com/sagan/ptool/site"
"github.com/sagan/ptool/util"
)

type Site struct {
Name string
Location *time.Location
SiteConfig *config.SiteConfigStruct
Config *config.ConfigStruct
HttpClient *azuretls.Session
HttpHeaders [][]string
}

const (
SELECTOR_USERNAME = `.myBlock:has(a[href$="account.php"]) .myBlock-caption`
SELECTOR_USER_UPLOADED = `.myBlock:has(a[href$="account.php"]) tr:has(td:contains("Uploaded")) td:last-child`
SELECTOR_USER_DOWNLOADED = `.myBlock:has(a[href$="account.php"]) tr:has(td:contains("Downloaded")) td:last-child`
)

func (usite *Site) GetDefaultHttpHeaders() [][]string {
return usite.HttpHeaders
}

func (usite *Site) PurgeCache() {
}

func (usite *Site) GetName() string {
return usite.Name
}

func (usite *Site) GetSiteConfig() *config.SiteConfigStruct {
return usite.SiteConfig
}

func (usite *Site) GetStatus() (*site.Status, error) {
doc, _, err := util.GetUrlDocWithAzuretls(usite.SiteConfig.Url, usite.HttpClient,
usite.GetSiteConfig().Cookie, site.GetUa(usite), usite.GetDefaultHttpHeaders())
if err != nil {
return nil, err
}
userNameSelector := SELECTOR_USERNAME
userUploadedSelector := SELECTOR_USER_UPLOADED
userDownloadedSelector := SELECTOR_USER_DOWNLOADED
if usite.SiteConfig.SelectorUserInfoUserName != "" {
userNameSelector = usite.SiteConfig.SelectorUserInfoUserName
}
if usite.SiteConfig.SelectorUserInfoUploaded != "" {
userUploadedSelector = usite.SiteConfig.SelectorUserInfoUploaded
}
if usite.SiteConfig.SelectorUserInfoDownloaded != "" {
userDownloadedSelector = usite.SiteConfig.SelectorUserInfoDownloaded
}
usernameEl := doc.Find(userNameSelector)
uploadedEl := doc.Find(userUploadedSelector)
downloadedEl := doc.Find(userDownloadedSelector)
userUploaded, _ := util.ExtractSizeStr(util.DomSanitizedText(uploadedEl))
userDownloaded, _ := util.ExtractSizeStr(util.DomSanitizedText(downloadedEl))
return &site.Status{
UserName: util.DomSanitizedText(usernameEl),
UserUploaded: userUploaded,
UserDownloaded: userDownloaded,
}, nil
}

func (usite *Site) GetAllTorrents(sort string, desc bool, pageMarker string, baseUrl string) (
torrents []site.Torrent, nextPageMarker string, err error) {
return nil, "", fmt.Errorf("not implemented yet")
}

func (usite *Site) GetLatestTorrents(full bool) ([]site.Torrent, error) {
return nil, fmt.Errorf("not implemented yet")
}

func (usite *Site) SearchTorrents(keyword string, baseUrl string) ([]site.Torrent, error) {
return nil, fmt.Errorf("not implemented yet")
}

func (usite *Site) DownloadTorrent(torrentUrl string) (content []byte, filename string, id string, err error) {
if !util.IsUrl(torrentUrl) {
id = strings.TrimPrefix(torrentUrl, usite.GetName()+".")
content, filename, err = usite.DownloadTorrentById(id)
return
}
if urlObj, err := url.Parse(torrentUrl); err == nil {
id = urlObj.Query().Get("id")
}
if !strings.Contains(torrentUrl, "/download.php") && id != "" {
content, filename, err = usite.DownloadTorrentById(id)
return
}
content, filename, err = site.DownloadTorrentByUrl(usite, usite.HttpClient, torrentUrl, id)
return
}

func (usite *Site) DownloadTorrentById(id string) ([]byte, string, error) {
torrentUrl := usite.SiteConfig.ParseSiteUrl("download.php?id="+id, false)
return site.DownloadTorrentByUrl(usite, usite.HttpClient, torrentUrl, id)
}

func NewSite(name string, siteConfig *config.SiteConfigStruct, config *config.ConfigStruct) (site.Site, error) {
if siteConfig.Cookie == "" {
log.Warnf("Site %s has no cookie provided", name)
}
location, err := time.LoadLocation(siteConfig.GetTimezone())
if err != nil {
return nil, fmt.Errorf("invalid site timezone %s: %v", siteConfig.GetTimezone(), err)
}
httpClient, httpHeaders, err := site.CreateSiteHttpClient(siteConfig, config)
if err != nil {
return nil, fmt.Errorf("failed to create site http client: %v", err)
}
site := &Site{
Name: name,
Location: location,
SiteConfig: siteConfig,
Config: config,
HttpClient: httpClient,
HttpHeaders: httpHeaders,
}
return site, nil
}

func init() {
site.Register(&site.RegInfo{
Name: "torrenttrader",
Creator: NewSite,
})
}

0 comments on commit 7994795

Please sign in to comment.