Albirew/nyaa-pantsu
Albirew
/
nyaa-pantsu
Archivé
1
0
Bifurcation 0

Merge pull request #391 from sfan5/memes

Fixes & Features
Cette révision appartient à :
PantsuDev 2017-05-12 20:25:05 +10:00 révisé par GitHub
révision ac92ddfeba
33 fichiers modifiés avec 167 ajouts et 198 suppressions

Voir le fichier

@ -12,9 +12,8 @@ type User struct {
Status int `gorm:"column:status"`
CreatedAt time.Time `gorm:"column:created_at"`
UpdatedAt time.Time `gorm:"column:updated_at"`
// Currently unused (auth is stateless now)
/*Token string `gorm:"column:api_token"`
TokenExpiration time.Time `gorm:"column:api_token_expiry"`*/
Token string `gorm:"column:api_token"`
TokenExpiration time.Time `gorm:"column:api_token_expiry"`
Language string `gorm:"column:language"`
// TODO: move this to PublicUser
@ -43,7 +42,7 @@ func (u User) Size() (s int) {
4*3 + //time.Time
3*2 + // arrays
// string arrays
len(u.Username) + len(u.Password) + len(u.Email) + len(u.MD5) + len(u.Language)
len(u.Username) + len(u.Password) + len(u.Email) + len(u.Token) + len(u.MD5) + len(u.Language)
s *= 8
// Ignoring foreign key users. Fuck them.

Voir le fichier

@ -186,6 +186,10 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ
.navbar {
border-radius: 0px;
}
/* the logo needs some space */
.navbar-logo {
margin-right: 10px;
}
:target {
background-color: #e5eecc;

BIN
public/img/logo.png Fichier normal

Fichier binaire non affiché.

Après

Largeur:  |  Hauteur:  |  Taille: 8.0 KiB

Voir le fichier

@ -19,10 +19,10 @@ var FuncMap = template.FuncMap{
}
return "error"
},
"genRouteWithQuery": func(name string, currentUrl *url.URL, params ...string) template.HTML {
"genRouteWithQuery": func(name string, currentUrl *url.URL, params ...string) template.URL {
url, err := Router.Get(name).URL(params...)
if err == nil {
return template.HTML(template.HTMLEscapeString(url.String() + "?" + currentUrl.RawQuery)) // TODO: Review application of character escaping
return template.URL(url.String() + "?" + currentUrl.RawQuery)
}
return "error"
},
@ -69,6 +69,7 @@ var FuncMap = template.FuncMap{
"CurrentOrAdmin": userPermission.CurrentOrAdmin,
"CurrentUserIdentical": userPermission.CurrentUserIdentical,
"HasAdmin": userPermission.HasAdmin,
"NeedsCaptcha": userPermission.NeedsCaptcha,
"GetRole": userPermission.GetRole,
"IsFollower": userPermission.IsFollower,
"NoEncode": func(str string) template.HTML {

Voir le fichier

@ -6,7 +6,6 @@ import (
"github.com/ewhal/nyaa/common"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/service/user"
userForms "github.com/ewhal/nyaa/service/user/form"
"github.com/gorilla/mux"
@ -36,7 +35,7 @@ type NotFoundTemplateVariables struct {
type ViewTemplateVariables struct {
Torrent model.TorrentJSON
Captcha captcha.Captcha
CaptchaID string
Search SearchForm
Navigation Navigation
User *model.User

Voir le fichier

@ -17,7 +17,6 @@ import (
"github.com/ewhal/nyaa/cache"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/service/upload"
"github.com/ewhal/nyaa/util"
"github.com/ewhal/nyaa/util/metainfo"
@ -33,7 +32,7 @@ type UploadForm struct {
Remake bool
Description string
Status int
captcha.Captcha
CaptchaID string
Infohash string
CategoryID int
@ -84,12 +83,6 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
f.Status, _ = strconv.Atoi(r.FormValue(UploadFormStatus))
f.Magnet = r.FormValue(UploadFormMagnet)
f.Remake = r.FormValue(UploadFormRemake) == "on"
f.Captcha = captcha.Extract(r)
if !captcha.Authenticate(f.Captcha) {
// TODO: Prettier passing of mistyped Captcha errors
return errors.New(captcha.ErrInvalidCaptcha.Error())
}
// trim whitespace
f.Name = util.TrimWhitespaces(f.Name)

Voir le fichier

@ -10,7 +10,7 @@ import (
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/service/user"
"github.com/ewhal/nyaa/service/user/permission"
"github.com/ewhal/nyaa/util/languages"
"github.com/gorilla/mux"
)
@ -23,26 +23,32 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
var uploadForm UploadForm
if r.Method == "POST" {
defer r.Body.Close()
user := GetUser(r)
if userPermission.NeedsCaptcha(user) {
userCaptcha := captcha.Extract(r)
if !captcha.Authenticate(userCaptcha) {
http.Error(w, captcha.ErrInvalidCaptcha.Error(), http.StatusInternalServerError)
return
}
}
// validation is done in ExtractInfo()
err := uploadForm.ExtractInfo(r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
user, _, err := userService.RetrieveCurrentUser(r)
if err != nil {
fmt.Printf("error %+v\n", err)
}
status := 1 // normal
if uploadForm.Remake { // overrides trusted
status = 2
} else if user.Status == 1 {
status = 3 // mark as trusted if user is trusted
}
var sameTorrents int
db.ORM.Model(&model.Torrent{}).Where("torrent_hash = ?", uploadForm.Infohash).Count(&sameTorrents)
if (sameTorrents == 0) {
//add to db and redirect depending on result
var sameTorrents int
db.ORM.Model(&model.Torrent{}).Where("torrent_hash = ?", uploadForm.Infohash).Count(&sameTorrents)
if (sameTorrents == 0) {
// add to db and redirect
torrent := model.Torrent{
Name: uploadForm.Name,
Category: uploadForm.CategoryID,
@ -54,7 +60,6 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
Description: uploadForm.Description,
UploaderID: user.ID}
db.ORM.Create(&torrent)
fmt.Printf("%+v\n", torrent)
url, err := Router.Get("view_torrent").URL("id", strconv.FormatUint(uint64(torrent.ID), 10))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -66,7 +71,14 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
return
}
} else if r.Method == "GET" {
uploadForm.CaptchaID = captcha.GetID()
user := GetUser(r)
if userPermission.NeedsCaptcha(user) {
uploadForm.CaptchaID = captcha.GetID()
} else {
uploadForm.CaptchaID = ""
}
htv := UploadTemplateVariables{uploadForm, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
languages.SetTranslationFromRequest(uploadTemplate, r, "en-us")
err := uploadTemplate.ExecuteTemplate(w, "index.html", htv)

Voir le fichier

@ -10,6 +10,7 @@ import (
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/service/torrent"
"github.com/ewhal/nyaa/service/user/permission"
"github.com/ewhal/nyaa/util"
"github.com/ewhal/nyaa/util/languages"
"github.com/ewhal/nyaa/util/log"
@ -26,7 +27,12 @@ func ViewHandler(w http.ResponseWriter, r *http.Request) {
return
}
b := torrent.ToJSON()
htv := ViewTemplateVariables{b, captcha.Captcha{CaptchaID: captcha.GetID()}, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
captchaID := ""
user := GetUser(r)
if userPermission.NeedsCaptcha(user) {
captchaID = captcha.GetID()
}
htv := ViewTemplateVariables{b, captchaID, NewSearchForm(), Navigation{}, user, r.URL, mux.CurrentRoute(r)}
languages.SetTranslationFromRequest(viewTemplate, r, "en-us")
err = viewTemplate.ExecuteTemplate(w, "index.html", htv)
@ -39,12 +45,14 @@ func PostCommentHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
userCaptcha := captcha.Extract(r)
if !captcha.Authenticate(userCaptcha) {
http.Error(w, "bad captcha", 403)
return
}
currentUser := GetUser(r)
if userPermission.NeedsCaptcha(currentUser) {
userCaptcha := captcha.Extract(r)
if !captcha.Authenticate(userCaptcha) {
http.Error(w, "bad captcha", 403)
return
}
}
content := p.Sanitize(r.FormValue("comment"))
if strings.TrimSpace(content) == "" {
@ -75,12 +83,14 @@ func ReportTorrentHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
userCaptcha := captcha.Extract(r)
if !captcha.Authenticate(userCaptcha) {
http.Error(w, "bad captcha", 403)
return
}
currentUser := GetUser(r)
if userPermission.NeedsCaptcha(currentUser) {
userCaptcha := captcha.Extract(r)
if !captcha.Authenticate(userCaptcha) {
http.Error(w, "bad captcha", 403)
return
}
}
idNum, err := strconv.Atoi(id)
userID := currentUser.ID

Voir le fichier

@ -5,98 +5,96 @@ import (
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
formStruct "github.com/ewhal/nyaa/service/user/form"
"github.com/ewhal/nyaa/util/log"
"github.com/ewhal/nyaa/util/modelHelper"
"github.com/ewhal/nyaa/util/timeHelper"
"github.com/gorilla/securecookie"
"golang.org/x/crypto/bcrypt"
"net/http"
"strconv"
"time"
)
const CookieName = "session"
// If you want to keep login cookies between restarts you need to make these permanent
var cookieHandler = securecookie.New(
securecookie.GenerateRandomKey(64),
securecookie.GenerateRandomKey(32))
// Encoding & Decoding of the cookie value
func DecodeCookie(cookie_value string) (uint, error) {
value := make(map[string]string)
err := cookieHandler.Decode(CookieName, cookie_value, &value)
func Token(r *http.Request) (string, error) {
var token string
cookie, err := r.Cookie("session")
if err != nil {
return 0, err
return token, err
}
time_int, _ := strconv.ParseInt(value["t"], 10, 0)
if timeHelper.IsExpired(time.Unix(time_int, 0)) {
return 0, errors.New("Cookie is expired")
cookieValue := make(map[string]string)
err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
if err != nil {
return token, err
}
ret, err := strconv.ParseUint(value["u"], 10, 0)
return uint(ret), err
token = cookieValue["token"]
if len(token) == 0 {
return token, errors.New("token is empty")
}
return token, nil
}
func EncodeCookie(user_id uint) (string, error) {
validUntil := timeHelper.FewDaysLater(7) // 1 week
// SetCookie sets a cookie.
func SetCookie(w http.ResponseWriter, token string) (int, error) {
value := map[string]string{
"u": strconv.FormatUint(uint64(user_id), 10),
"t": strconv.FormatInt(validUntil.Unix(), 10),
"token": token,
}
return cookieHandler.Encode(CookieName, value)
encoded, err := cookieHandler.Encode("session", value)
if err != nil {
return http.StatusInternalServerError, err
}
cookie := &http.Cookie{
Name: "session",
Value: encoded,
Path: "/",
}
http.SetCookie(w, cookie)
return http.StatusOK, nil
}
// ClearCookie clears a cookie.
func ClearCookie(w http.ResponseWriter) (int, error) {
cookie := &http.Cookie{
Name: CookieName,
Name: "session",
Value: "",
Path: "/",
HttpOnly: true,
MaxAge: -1,
}
http.SetCookie(w, cookie)
return http.StatusOK, nil
}
// SetCookieHandler sets the authentication cookie
// SetCookieHandler sets a cookie with email and password.
func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, error) {
if email == "" || pass == "" {
return http.StatusNotFound, errors.New("No username/password entered")
}
var user model.User
// search by email or username
isValidEmail, _ := formStruct.EmailValidation(email, formStruct.NewErrors())
if isValidEmail {
if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() {
return http.StatusNotFound, errors.New("User not found")
if email != "" && pass != "" {
var user model.User
isValidEmail, _ := formStruct.EmailValidation(email, formStruct.NewErrors())
if isValidEmail {
log.Debug("User entered valid email.")
if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() {
return http.StatusNotFound, errors.New("User not found")
}
} else {
log.Debug("User entered username.")
if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() {
return http.StatusNotFound, errors.New("User not found")
}
}
} else {
if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() {
return http.StatusNotFound, errors.New("User not found")
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass))
if err != nil {
return http.StatusUnauthorized, errors.New("Password incorrect")
}
if user.Status == -1 {
return http.StatusUnauthorized, errors.New("Account banned")
}
status, err := SetCookie(w, user.Token)
if err != nil {
return status, err
}
w.Header().Set("X-Auth-Token", user.Token)
return http.StatusOK, nil
}
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass))
if err != nil {
return http.StatusUnauthorized, errors.New("Password incorrect")
}
if user.Status == -1 {
return http.StatusUnauthorized, errors.New("Account banned")
}
encoded, err := EncodeCookie(user.ID)
if err != nil {
return http.StatusInternalServerError, err
}
cookie := &http.Cookie{
Name: CookieName,
Value: encoded,
Path: "/",
HttpOnly: true,
}
http.SetCookie(w, cookie)
// also set response header for convenience
w.Header().Set("X-Auth-Token", encoded)
return http.StatusOK, nil
return http.StatusNotFound, errors.New("user not found")
}
// RegisterHanderFromForm sets cookie from a RegistrationForm.
@ -113,31 +111,24 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) (int, error) {
return RegisterHanderFromForm(w, registrationForm)
}
// CurrentUser determines the current user from the request
// CurrentUser get a current user.
func CurrentUser(r *http.Request) (model.User, error) {
var user model.User
var encoded string
encoded = r.Header.Get("X-Auth-Token")
if len(encoded) == 0 {
// check cookie instead
cookie, err := r.Cookie(CookieName)
var token string
var err error
token = r.Header.Get("X-Auth-Token")
if len(token) > 0 {
log.Debug("header token exists")
} else {
token, err = Token(r)
log.Debug("header token does not exist")
if err != nil {
return user, err
}
encoded = cookie.Value
}
user_id, err := DecodeCookie(encoded)
if err != nil {
return user, err
if db.ORM.Where("api_token = ?", token).First(&user).RecordNotFound() {
return user, errors.New("user not found")
}
if db.ORM.Where("user_id = ?", user_id).First(&user).RecordNotFound() {
return user, errors.New("User not found")
}
if user.Status == -1 {
// recheck as user might've been banned in the meantime
return user, errors.New("Account banned")
}
return user, nil
err = db.ORM.Model(&user).Error
return user, err
}

Voir le fichier

@ -6,6 +6,7 @@ import (
"github.com/ewhal/nyaa/util/log"
)
// HasAdmin checks that user has an admin permission.
func HasAdmin(user *model.User) bool {
return user.Status == 2
@ -18,11 +19,16 @@ func CurrentOrAdmin(user *model.User, userID uint) bool {
}
// CurrentUserIdentical check that userID is same as current user's ID.
// TODO: Inline this
// TODO: Inline this (won't go do this for us?)
func CurrentUserIdentical(user *model.User, userID uint) bool {
return user.ID == userID
}
func NeedsCaptcha(user *model.User) bool {
// Trusted members & Moderators don't
return !(user.Status == 1 || user.Status == 2)
}
func GetRole(user *model.User) string {
switch user.Status {
case -1:

Voir le fichier

@ -7,6 +7,7 @@ import (
"strconv"
"time"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
formStruct "github.com/ewhal/nyaa/service/user/form"
@ -14,6 +15,7 @@ import (
"github.com/ewhal/nyaa/util/crypto"
"github.com/ewhal/nyaa/util/log"
"github.com/ewhal/nyaa/util/modelHelper"
"github.com/ewhal/nyaa/util/timeHelper"
"golang.org/x/crypto/bcrypt"
)
@ -67,8 +69,15 @@ func CreateUserFromForm(registrationForm formStruct.RegistrationForm) (model.Use
return user, err
}
}
token, err := crypto.GenerateRandomToken32()
if err != nil {
return user, errors.New("token not generated")
}
user.Email = "" // unset email because it will be verified later
user.Token = token
user.TokenExpiration = timeHelper.FewDaysLater(config.AuthTokenExpirationDay)
log.Debugf("user %+v\n", user)
if db.ORM.Create(&user).Error != nil {
return user, errors.New("user not created")
}
@ -148,13 +157,17 @@ func UpdateUserCore(user *model.User) (int, error) {
}
}
user.UpdatedAt = time.Now()
err := db.ORM.Save(user).Error
token, err := crypto.GenerateRandomToken32()
if err != nil {
return http.StatusInternalServerError, err
}
user.Token = token
user.TokenExpiration = timeHelper.FewDaysLater(config.AuthTokenExpirationDay)
if db.ORM.Save(user).Error != nil {
return http.StatusInternalServerError, err
}
user.UpdatedAt = time.Now()
return http.StatusOK, nil
}
@ -184,13 +197,18 @@ func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *m
form.Username = user.Username
}
if (form.Email != user.Email) {
// send verification to new email and keep old
SendVerificationToUser(user, form.Email)
form.Email = user.Email
}
log.Debugf("form %+v\n", form)
modelHelper.AssignValue(&user, form)
status, err := UpdateUserCore(&user)
if err != nil {
return user, status, err
}
if userPermission.CurrentUserIdentical(currentUser, user.ID) {
status, err = SetCookie(w, user.Token)
}
return user, status, err
}

Voir le fichier

@ -63,8 +63,5 @@ http://tracker.baka-sub.cf/announce</pre>
<br />
<img style="max-width: 100%" src="https://my.mixtape.moe/omrskw.png" alt="funny meme">
<br />
<h2>{{T "nyaa_pantsu_dont_host_files"}}</h2>
</div>
{{end}}

Voir le fichier

@ -1,8 +1,11 @@
{{define "captcha"}}
{{/* unset if user doesn't need captcha */}}
{{if ne .CaptchaID ""}}
<div class="form-group captcha-container">
<label for="solution">Captcha</label>
<input type="text" name="captchaID" value="{{.CaptchaID}}" hidden>
<img src="/captcha/{{.CaptchaID}}.png">
<input type="text" name="solution" class="form-control" placeholder="Captcha" autocomplete="off" required>
</div>
{{end}}
{{end}}

Voir le fichier

@ -29,7 +29,7 @@
<th class="col-xs-1">Action</th>
</tr>
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">{{ .Torrent.Name }}</a></td><td>{{.User.Username}}</td><td>{{.Description}}</td>
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">{{ .Torrent.Name }}</a></td><td>{{.UserID}}</td><td>{{.Description}}</td>
<td><a href="{{ genRoute "mod_trdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
{{end}}
</table>
@ -70,7 +70,7 @@
{{ range .Comments}}
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .User.Username }}</a></td>
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{.UserID}}</a></td>
<td><a href="{{ genRoute "mod_cdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
{{end}}
</table>

Voir le fichier

@ -42,7 +42,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{.URL.Parse "/"}}">Nyaa Pantsu</a>
<a href="{{.URL.Parse "/"}}"><img src="/img/logo.png" class ="navbar-logo" alt="Nyaa Pantsu"></a>
<a class="navbar-brand hidden-md pull-right nightswitch" href="javascript:toggleNightMode();" ></a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">

Voir le fichier

@ -23,11 +23,11 @@
<p class="text-error">{{ . }}</p>
{{end}}
</div>
<span class="button-checkbox">
<!-- <button type="button" class="btn hidden" data-color="info">{{ T "remember_me"}}</button>
<input type="checkbox" name="remember_me" id="remember_me" checked="checked"> -->
<!-- <span class="button-checkbox">
<button type="button" class="btn hidden" data-color="info">{{ T "remember_me"}}</button>
<input type="checkbox" name="remember_me" id="remember_me" checked="checked">
<a href="" class="btn btn-link pull-right">{{ T "forgot_password"}}</a>
</span>
</span> -->
<hr class="colorgraph">
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">

Voir le fichier

@ -116,7 +116,7 @@
<label for="comment">{{ if gt .User.ID 0}} {{T "submit_a_comment_as_username" .User.Username}} {{else}} {{T "submit_a_comment_as_anonymous"}} {{end}}</label>
<textarea name="comment" class="form-control" rows="5"></textarea>
</div>
{{with .Captcha}} {{block "captcha" .}}{{end}} {{end}}
{{block "captcha" .}}{{end}}
<button type="submit" class="btn btn-success">{{T " submit "}}</button>
</form>
</div>
@ -137,7 +137,7 @@
<input type="radio" name="report_type" value="illegal"> Illegal content <br/>
<input type="radio" name="report_type" value="spam"> Spam / garbage
{{end}}
{{with .Captcha}} {{block "captcha" .}}{{end}} {{end}}
{{block "captcha" .}}{{end}}
<button type="submit" class="btn btn-default">Report!</button>
</form> <br />
</div>

Voir le fichier

@ -319,10 +319,6 @@
"id": "authors_favorite_language",
"translation": "És el llenguatge de programació preferit de l'autor."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat i sukebei.pantsu.cat no allotgen cap fitxer."
},
{
"id": "upload_magnet",
"translation": "Carrega un enllaç magnètic"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "Weil es die Lieblingsprogrammiersprache des Entwicklers ist."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat und sukebei.pantsu.cat stellen keine Dateien bereit."
},
{
"id": "upload_magnet",
"translation": "Magnet hochladen"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "It's the author's favorite programming language."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat and sukebei.pantsu.cat do not host any files."
},
{
"id": "upload_magnet",
"translation": "Upload magnet"

Voir le fichier

@ -315,10 +315,6 @@
"id": "authors_favorite_language",
"translation": "Es el lenguaje de programación favorito del autor."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat y sukebei.pantsu.cat no hospedan archivos."
},
{
"id": "upload_magnet",
"translation": "Subir Magnet"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "C'est le langage de programmation favori du développeur."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat and sukebei.pantsu.cat n'hébergent aucun fichier."
},
{
"id": "upload_magnet",
"translation": "Uploader un magnet"

Voir le fichier

@ -319,10 +319,6 @@
"id": "authors_favorite_language",
"translation": "Ez a dev kedvenc programozási nyelve."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat és sukebei.pantsu.cat nem hosztol fájlokat."
},
{
"id": "upload_magnet",
"translation": "Magnet Feltöltése"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "È il linguaggio di programmazione preferito dell'autore."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat e sukebei.pantsu.cat non contengono/hostano nessun file al loro interno."
},
{
"id": "upload_magnet",
"translation": "Carica magnet"

Voir le fichier

@ -319,10 +319,6 @@
"id": "authors_favorite_language",
"translation": "作者の一番好きなプログラミング言語だからです。"
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat と sukebei.pantsu.cat はいかなるファイルもホストしていません。"
},
{
"id": "upload_magnet",
"translation": "magnet リンクのアップロード"

Voir le fichier

@ -319,10 +319,6 @@
"id": "authors_favorite_language",
"translation": "제일 좋아하는 언어입니다. >__- 찡긋"
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat 와 sukebei.pantsu.cat 는 그 어떤 파일도 호스팅하고 있지 않습니다"
},
{
"id": "upload_magnet",
"translation": "마그넷 업로드"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "Favorittspråket til karen som dro sulamitten i gang."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat og sukebei.pantsu.cat har ingen av disse filene på sine servere, og deler ingen filer fra serverene ellers."
},
{
"id": "upload_magnet",
"translation": "Last opp magnet"

Voir le fichier

@ -319,10 +319,6 @@
"id": "authors_favorite_language",
"translation": "Het is de favoriete programmeertaal van de bedenker."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat en sukebei.pantsu.cat hosten geen bestanden."
},
{
"id": "upload_magnet",
"translation": "Magnet uploaden"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "É a linguagem de programação favorita do autor."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat e sukebei.pantsu.cat não hospedam nenhum arquivo."
},
{
"id": "upload_magnet",
"translation": "Enviar link magnético"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "Это любимый язык программирования автора."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat и sukebei.pantsu.cat не содержат файлы."
},
{
"id": "upload_magnet",
"translation": "Загрузить магнит"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "Det är skaparens favoritspråk."
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat och sukebei.pantsu.cat tillhandahåller inga filer på sina servrar."
},
{
"id": "upload_magnet",
"translation": "Ladda upp magnet"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "เพราะเป็นภาษาถนัดของผู้พัฒนา"
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat และ sukebei.pantsu.cat จะไม่เก็บไฟล์ใดๆ ไว้ในระบบทั้งสิ้น"
},
{
"id": "upload_magnet",
"translation": "อัพโหลด Magnet"

Voir le fichier

@ -331,10 +331,6 @@
"id": "authors_favorite_language",
"translation": "這是作者的愛啊啊啊啊"
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat 以及 sukebei.pantsu.cat 沒有存放任何檔案喔∼"
},
{
"id": "upload_magnet",
"translation": "上傳磁力連結"