Merge pull request #136 from sfan5/upl
Fixes & Features for torrent uploads
Cette révision appartient à :
révision
c637181ff9
10 fichiers modifiés avec 152 ajouts et 38 suppressions
|
@ -1,5 +1,6 @@
|
|||
package config
|
||||
|
||||
// remember to update the FAQ when updating these
|
||||
var Trackers = []string{
|
||||
"udp://tracker.coppersurfer.tk:6969",
|
||||
"udp://zer0day.to:1337/announce",
|
||||
|
|
|
@ -82,7 +82,7 @@ func (t *Torrents) ToJson() TorrentsJson {
|
|||
Status: t.Status,
|
||||
Hash: t.Hash,
|
||||
Date: time.Unix(t.Date, 0).Format(time.RFC3339),
|
||||
Filesize: util.FormatFilesize(t.Filesize),
|
||||
Filesize: util.FormatFilesize2(t.Filesize),
|
||||
Description: template.HTML(t.Description),
|
||||
Comments: b,
|
||||
Sub_Category: strconv.Itoa(t.Sub_Category),
|
||||
|
|
31
public/js/uploadPage.js
Fichier normal
31
public/js/uploadPage.js
Fichier normal
|
@ -0,0 +1,31 @@
|
|||
(function() {
|
||||
var torrent = $("input[name=torrent]"),
|
||||
magnet = $("input[name=magnet]"),
|
||||
name = $("input[name=name]");
|
||||
|
||||
torrent.on("change", function() {
|
||||
if (torrent.val() == "") {
|
||||
enableField(magnet);
|
||||
name.attr("required", "");
|
||||
} else {
|
||||
disableField(magnet);
|
||||
// .torrent file will allow autofilling name
|
||||
name.removeAttr("required", "");
|
||||
}
|
||||
});
|
||||
magnet.on("change", function() {
|
||||
if (magnet.val() == "")
|
||||
enableField(torrent);
|
||||
else
|
||||
disableField(torrent);
|
||||
});
|
||||
|
||||
function enableField(e) {
|
||||
e.attr("required", "")
|
||||
.removeAttr("disabled");
|
||||
}
|
||||
function disableField(e) {
|
||||
e.attr("disabled", "")
|
||||
.removeAttr("required");
|
||||
}
|
||||
})();
|
103
router/upload.go
103
router/upload.go
|
@ -7,6 +7,7 @@ import (
|
|||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"regexp"
|
||||
|
||||
"github.com/ewhal/nyaa/service/captcha"
|
||||
"github.com/ewhal/nyaa/util"
|
||||
|
@ -21,10 +22,12 @@ type UploadForm struct {
|
|||
Magnet string
|
||||
Infohash string
|
||||
Category string
|
||||
CategoryId int
|
||||
SubCategoryId int
|
||||
Description string
|
||||
captcha.Captcha
|
||||
|
||||
CategoryId int
|
||||
SubCategoryId int
|
||||
Filesize int64
|
||||
}
|
||||
|
||||
// TODO: these should be in another package (?)
|
||||
|
@ -44,9 +47,17 @@ const UploadFormCategory = "c"
|
|||
// form value for description
|
||||
const UploadFormDescription = "desc"
|
||||
|
||||
|
||||
// error indicating that you can't send both a magnet link and torrent
|
||||
var ErrTorrentPlusMagnet = errors.New("upload either a torrent file or magnet link, not both")
|
||||
|
||||
// error indicating a torrent is private
|
||||
var ErrPrivateTorrent = errors.New("torrent is private")
|
||||
|
||||
// error indicating a problem with its trackers
|
||||
// FIXME: hardcoded link
|
||||
var ErrTrackerProblem = errors.New("torrent does not have any (working) trackers: https://nyaa.pantsu.cat/faq#trackers")
|
||||
|
||||
// error indicating a torrent's name is invalid
|
||||
var ErrInvalidTorrentName = errors.New("torrent name is invalid")
|
||||
|
||||
|
@ -74,15 +85,6 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
|
|||
f.Description = p.Sanitize(util.TrimWhitespaces(f.Description))
|
||||
f.Magnet = util.TrimWhitespaces(f.Magnet)
|
||||
|
||||
if len(f.Name) == 0 {
|
||||
return ErrInvalidTorrentName
|
||||
}
|
||||
|
||||
if len(f.Description) == 0 {
|
||||
return ErrInvalidTorrentDescription
|
||||
|
||||
}
|
||||
|
||||
catsSplit := strings.Split(f.Category, "_")
|
||||
// need this to prevent out of index panics
|
||||
if len(catsSplit) == 2 {
|
||||
|
@ -101,13 +103,10 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
|
|||
return ErrInvalidTorrentCategory
|
||||
}
|
||||
|
||||
if len(f.Magnet) == 0 {
|
||||
// try parsing torrent file if provided if no magnet is specified
|
||||
tfile, _, err := r.FormFile(UploadFormTorrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// first: parse torrent file (if any) to fill missing information
|
||||
tfile, _, err := r.FormFile(UploadFormTorrent)
|
||||
if err == nil {
|
||||
var torrent metainfo.TorrentFile
|
||||
// decode torrent
|
||||
err = bencode.NewDecoder(tfile).Decode(&torrent)
|
||||
|
@ -115,17 +114,32 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
|
|||
return metainfo.ErrInvalidTorrentFile
|
||||
}
|
||||
|
||||
// check if torrent is private
|
||||
// check a few things
|
||||
if torrent.IsPrivate() {
|
||||
return ErrPrivateTorrent
|
||||
}
|
||||
trackers := torrent.GetAllAnnounceURLS()
|
||||
if !CheckTrackers(trackers) {
|
||||
return ErrTrackerProblem
|
||||
}
|
||||
|
||||
// generate magnet
|
||||
// Name
|
||||
if len(f.Name) == 0 {
|
||||
f.Name = torrent.TorrentName()
|
||||
}
|
||||
|
||||
// Magnet link: if a file is provided it should be empty
|
||||
if len(f.Magnet) != 0 {
|
||||
return ErrTorrentPlusMagnet
|
||||
}
|
||||
binInfohash := torrent.Infohash()
|
||||
f.Infohash = hex.EncodeToString(binInfohash[:])
|
||||
f.Infohash = strings.ToUpper(hex.EncodeToString(binInfohash[:]))
|
||||
f.Magnet = util.InfoHashToMagnet(f.Infohash, f.Name)
|
||||
f.Infohash = strings.ToUpper(f.Infohash)
|
||||
|
||||
// extract filesize
|
||||
f.Filesize = int64(torrent.TotalSize())
|
||||
} else {
|
||||
// No torrent file provided
|
||||
magnetUrl, parseErr := url.Parse(f.Magnet)
|
||||
if parseErr != nil {
|
||||
return metainfo.ErrInvalidTorrentFile
|
||||
|
@ -133,14 +147,57 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
|
|||
exactTopic := magnetUrl.Query().Get("xt")
|
||||
if !strings.HasPrefix(exactTopic, "urn:btih:") {
|
||||
return metainfo.ErrInvalidTorrentFile
|
||||
} else {
|
||||
f.Infohash = strings.ToUpper(strings.TrimPrefix(exactTopic, "urn:btih:"))
|
||||
}
|
||||
f.Infohash = strings.ToUpper(strings.TrimPrefix(exactTopic, "urn:btih:"))
|
||||
matched, err := regexp.MatchString("^[0-9A-F]{40}$", f.Infohash)
|
||||
if err != nil || !matched {
|
||||
return metainfo.ErrInvalidTorrentFile
|
||||
}
|
||||
|
||||
f.Filesize = 0
|
||||
}
|
||||
|
||||
|
||||
// then actually check that we have everything we need
|
||||
if len(f.Name) == 0 {
|
||||
return ErrInvalidTorrentName
|
||||
}
|
||||
|
||||
//if len(f.Description) == 0 {
|
||||
// return ErrInvalidTorrentDescription
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var dead_trackers = []string{ // substring matches!
|
||||
"://open.nyaatorrents.info:6544",
|
||||
"://tracker.openbittorrent.com:80",
|
||||
"://tracker.publicbt.com:80",
|
||||
"://stats.anisource.net:2710",
|
||||
"://exodus.desync.com",
|
||||
"://open.demonii.com:1337",
|
||||
"://tracker.istole.it:80",
|
||||
"://tracker.ccc.de:80",
|
||||
"://bt2.careland.com.cn:6969",
|
||||
"://announce.torrentsmd.com:8080"}
|
||||
|
||||
func CheckTrackers(trackers []string) bool {
|
||||
var numGood int
|
||||
for _, t := range trackers {
|
||||
var good bool = true
|
||||
for _, check := range dead_trackers {
|
||||
if strings.Contains(t, check) {
|
||||
good = false
|
||||
}
|
||||
}
|
||||
if good {
|
||||
numGood += 1
|
||||
}
|
||||
}
|
||||
return numGood > 0
|
||||
}
|
||||
|
||||
// NewUploadForm creates a new upload form given parameters as list
|
||||
func NewUploadForm(params ...string) (uploadForm UploadForm) {
|
||||
if len(params) > 1 {
|
||||
|
|
|
@ -17,6 +17,7 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
var uploadForm UploadForm
|
||||
if r.Method == "POST" {
|
||||
defer r.Body.Close()
|
||||
// validation is done in ExtractInfo()
|
||||
err = uploadForm.ExtractInfo(r)
|
||||
if err == nil {
|
||||
if !captcha.Authenticate(uploadForm.Captcha) {
|
||||
|
@ -25,7 +26,6 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
//validate name + hash
|
||||
//add to db and redirect depending on result
|
||||
torrent := model.Torrents{
|
||||
Name: uploadForm.Name,
|
||||
|
@ -34,9 +34,10 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Status: 1,
|
||||
Hash: uploadForm.Infohash,
|
||||
Date: time.Now().Unix(),
|
||||
Filesize: uploadForm.Filesize, // FIXME: should set to NULL instead of 0
|
||||
Description: uploadForm.Description,
|
||||
Comments: []byte{}}
|
||||
fmt.Printf("%+v\n", torrent)
|
||||
//fmt.Printf("%+v\n", torrent)
|
||||
db.ORM.Create(&torrent)
|
||||
fmt.Printf("%+v\n", torrent)
|
||||
url, err := Router.Get("view_torrent").URL("id", strconv.Itoa(torrent.Id))
|
||||
|
@ -44,7 +45,6 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, url.String(), 302)
|
||||
}
|
||||
}
|
||||
fmt.Printf("%+v\n", uploadForm)
|
||||
} else if r.Method == "GET" {
|
||||
uploadForm.CaptchaID = captcha.GetID(r.RemoteAddr)
|
||||
htv := UploadTemplateVariables{uploadForm, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}
|
||||
|
|
|
@ -43,6 +43,18 @@
|
|||
<p>The magnet link should look like this: <span style="font-family:monospace">
|
||||
magnet:?xt=urn:btih:[hash]&dn=[name]&tr=[tracker]&tr=[...]</span></p>
|
||||
|
||||
<h2 id="trackers">Which trackers do you recommend using?</h2>
|
||||
<p>If your torrent upload is denied because of trackers you'll need to add some of these:</p>
|
||||
<pre>udp://tracker.coppersurfer.tk:6969
|
||||
udp://zer0day.to:1337/announce
|
||||
udp://tracker.leechers-paradise.org:6969
|
||||
udp://explodie.org:6969
|
||||
udp://tracker.opentrackr.org:1337
|
||||
udp://tracker.internetwarriors.net:1337/announce
|
||||
udp://eddie4.nl:6969/announce
|
||||
http://mgtracker.org:6969/announce
|
||||
http://tracker.baka-sub.cf/announce</pre>
|
||||
|
||||
<h2>How can I help?</h2>
|
||||
<p>If you have website development expertise, you can join the #nyaapantsu IRC channel on irc.rizon.net.
|
||||
If you have any current databases, especially for sukebei, <b>UPLOAD THEM</b>.</p>
|
||||
|
@ -54,7 +66,7 @@
|
|||
<p>It's the author's favorite programming language.</p>
|
||||
|
||||
<br />
|
||||
<img src="https://my.mixtape.moe/omrskw.png">
|
||||
<img src="https://my.mixtape.moe/omrskw.png" alt="funny meme">
|
||||
|
||||
<br />
|
||||
<h2>nyaa.pantsu.cat and sukebei.pantsu.cat do not host any files.</h2>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{{define "captcha"}}
|
||||
<div class="form-group captcha-container">
|
||||
<input type="text" name="capthcaID" value="{{.CaptchaID}}" hidden>
|
||||
<label for="solution">Captcha</label>
|
||||
<input type="text" name="captchaID" value="{{.CaptchaID}}" hidden>
|
||||
<img src="/captcha/{{.CaptchaID}}.png">
|
||||
<input type="number" name="solution" class="form-control" placeholder="Captcha" required>
|
||||
</div>
|
||||
|
|
|
@ -6,16 +6,16 @@
|
|||
<form enctype="multipart/form-data" role="upload" method="POST">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="torrent">Torrent file upload</label>
|
||||
<label for="name">Name</label>
|
||||
<input type="text" name="name" class="form-control" placeholder="File Name" value="{{.Name}}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="torrent">Torrent file</label>
|
||||
<input type="file" name="torrent" id="torrent" accept=".torrent">
|
||||
<p class="help-block">Upload a torrent file to pre-fill information</p>
|
||||
<p class="help-block">Uploading a torrent file allows pre-filling some fields, this is recommended.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">FileName</label>
|
||||
<input type="text" name="name" class="form-control"placeholder="File Name" value="{{.Name}}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Magnet">Magnet Link</label>
|
||||
<label for="magnet">Magnet Link</label>
|
||||
<input type="text" name="magnet" class="form-control"
|
||||
style="width:60rem" placeholder="Magnet Link" value="{{.Magnet}}">
|
||||
</div>
|
||||
|
@ -45,6 +45,7 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label for="desc">Torrent Description</label>
|
||||
<p class="help-block">A limited set of HTML is allowed in the description, make sure to use <span style="font-family:monospace"><br/></span>.</p>
|
||||
<textarea name="desc" class="form-control" rows="10">{{.Description}}</textarea>
|
||||
</div>
|
||||
|
||||
|
@ -56,4 +57,7 @@
|
|||
</form>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{define "js_footer"}}
|
||||
<script type="text/javascript" charset="utf-8" src="{{.URL.Parse "/js/uploadPage.js"}}"></script>
|
||||
{{end}}
|
||||
|
|
|
@ -25,3 +25,11 @@ func FormatFilesize(bytes int64) string {
|
|||
}
|
||||
return fmt.Sprintf("%.1f %s", value, unit)
|
||||
}
|
||||
|
||||
func FormatFilesize2(bytes int64) string {
|
||||
if bytes == 0 { // this is what gorm returns for NULL
|
||||
return "Unknown"
|
||||
} else {
|
||||
return FormatFilesize(bytes)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ func (tf *TorrentFile) TorrentName() string {
|
|||
|
||||
// return true if this torrent is private otherwise return false
|
||||
func (tf *TorrentFile) IsPrivate() bool {
|
||||
return tf.Info.Private == nil || *tf.Info.Private == 0
|
||||
return tf.Info.Private != nil && *tf.Info.Private == 1
|
||||
}
|
||||
|
||||
// calculate infohash
|
||||
|
|
Référencer dans un nouveau ticket