Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Merge branch 'dev'

Cette révision appartient à :
Eliot Whalan 2017-05-14 21:49:52 +10:00
révision c0468528ec
60 fichiers modifiés avec 1365 ajouts et 352 suppressions

Voir le fichier

@ -6,6 +6,12 @@
The aim of this project is to write a fully featured nyaa replacement in golang
that anyone will be able to deploy locally or remotely.
## [Roadmap](https://trello.com/b/gMJBwoRq/nyaa-pantsu-cat-roadmap)
The Roadmap will give you an overview of the features and tasks that the project are currently discussing, working on and have completed.
If you are looking for a feature that is not listed just make a GitHub Issue and it will get added to the trello board.
You can view the public trello board [here](https://trello.com/b/gMJBwoRq/nyaa-pantsu-cat-roadmap) or click on the "Roadmap".
# Requirements
* Golang

Voir le fichier

@ -21,7 +21,7 @@ type Config struct {
DBType string `json:"db_type"`
// DBParams will be directly passed to Gorm, and its internal
// structure depends on the dialect for each db type
DBParams string `json:"db_params"`
DBParams string `json:"db_params"`
DBLogMode string `json:"db_logmode"`
// tracker scraper config (required)
Scrape ScraperConfig `json:"scraper"`
@ -31,9 +31,14 @@ type Config struct {
Search SearchConfig `json:"search"`
// optional i2p configuration
I2P *I2PConfig `json:"i2p"`
// filesize fetcher config
FilesizeFetcher FilesizeFetcherConfig `json:"filesize_fetcher"`
// internationalization config
I18n I18nConfig `json:"i18n"`
}
var Defaults = Config{"localhost", 9999, "sqlite3", "./nyaa.db?cache_size=50", "default", DefaultScraperConfig, DefaultCacheConfig, DefaultSearchConfig, nil}
var Defaults = Config{"localhost", 9999, "sqlite3", "./nyaa.db?cache_size=50", "default", DefaultScraperConfig, DefaultCacheConfig, DefaultSearchConfig, nil, DefaultFilesizeFetcherConfig, DefaultI18nConfig}
var allowedDatabaseTypes = map[string]bool{
"sqlite3": true,
@ -57,6 +62,8 @@ func New() *Config {
config.DBLogMode = Defaults.DBLogMode
config.Scrape = Defaults.Scrape
config.Cache = Defaults.Cache
config.FilesizeFetcher = Defaults.FilesizeFetcher
config.I18n = Defaults.I18n
return &config
}

16
config/filesizeFetcher.go Fichier normal
Voir le fichier

@ -0,0 +1,16 @@
package config
type FilesizeFetcherConfig struct {
QueueSize int `json:"queue_size"`
Timeout int `json:"timeout"`
MaxDays int `json:"max_days"`
WakeUpInterval int `json:"wake_up_interval"`
}
var DefaultFilesizeFetcherConfig = FilesizeFetcherConfig{
QueueSize: 10,
Timeout: 120, // 2 min
MaxDays: 90,
WakeUpInterval: 300, // 5 min
}

11
config/i18n.go Fichier normal
Voir le fichier

@ -0,0 +1,11 @@
package config
type I18nConfig struct {
TranslationsDirectory string `json:"translations_directory"`
DefaultLanguage string `json:"default_language"`
}
var DefaultI18nConfig = I18nConfig{
TranslationsDirectory: "translations",
DefaultLanguage: "en-us", // TODO: Remove refs to "en-us" from the code and templates
}

Voir le fichier

@ -58,7 +58,7 @@ func GormInit(conf *config.Config, logger Logger) (*gorm.DB, error) {
if db.Error != nil {
return db, db.Error
}
db.AutoMigrate(&model.Torrent{}, &model.TorrentReport{})
db.AutoMigrate(&model.Torrent{}, &model.TorrentReport{}, &model.File{})
if db.Error != nil {
return db, db.Error
}

41
main.go
Voir le fichier

@ -6,7 +6,6 @@ import (
"net/http"
"os"
"path/filepath"
"time"
"github.com/ewhal/nyaa/cache"
@ -15,31 +14,21 @@ import (
"github.com/ewhal/nyaa/network"
"github.com/ewhal/nyaa/router"
"github.com/ewhal/nyaa/service/scraper"
"github.com/ewhal/nyaa/service/torrent/filesizeFetcher"
"github.com/ewhal/nyaa/util/languages"
"github.com/ewhal/nyaa/util/log"
"github.com/ewhal/nyaa/util/search"
"github.com/ewhal/nyaa/util/signals"
"github.com/nicksnyder/go-i18n/i18n"
)
func initI18N() {
/* Initialize the languages translation */
i18n.MustLoadTranslationFile("translations/en-us.all.json")
paths, err := filepath.Glob("translations/*.json")
if err == nil {
for _, path := range paths {
i18n.LoadTranslationFile(path)
}
}
}
// RunServer runs webapp mainloop
func RunServer(conf *config.Config) {
http.Handle("/", router.Router)
// Set up server,
srv := &http.Server{
WriteTimeout: 24 * time.Second,
ReadTimeout: 8 * time.Second,
WriteTimeout: 5 * time.Second,
ReadTimeout: 5 * time.Second,
}
l, err := network.CreateHTTPListener(conf)
log.CheckError(err)
@ -94,11 +83,24 @@ func RunScraper(conf *config.Config) {
scraper.Wait()
}
// RunFilesizeFetcher runs the database filesize fetcher main loop
func RunFilesizeFetcher(conf *config.Config) {
fetcher, err := filesizeFetcher.New(&conf.FilesizeFetcher)
if err != nil {
log.Fatalf("failed to start fetcher, %s", err)
return
}
signals.RegisterCloser(fetcher)
fetcher.RunAsync()
fetcher.Wait()
}
func main() {
conf := config.New()
processFlags := conf.BindFlags()
defaults := flag.Bool("print-defaults", false, "print the default configuration file on stdout")
mode := flag.String("mode", "webapp", "which mode to run daemon in, either webapp or scraper")
mode := flag.String("mode", "webapp", "which mode to run daemon in, either webapp, scraper or filesize_fetcher")
flag.Float64Var(&conf.Cache.Size, "c", config.DefaultCacheSize, "size of the search cache in MB")
flag.Parse()
@ -122,7 +124,10 @@ func main() {
if err != nil {
log.Fatal(err.Error())
}
initI18N()
err = languages.InitI18n(conf.I18n)
if err != nil {
log.Fatal(err.Error())
}
err = cache.Configure(&conf.Cache)
if err != nil {
log.Fatal(err.Error())
@ -142,6 +147,8 @@ func main() {
RunScraper(conf)
} else if *mode == "webapp" {
RunServer(conf)
} else if *mode == "metadata_fetcher" {
RunFilesizeFetcher(conf)
} else {
log.Fatalf("invalid runtime mode: %s", *mode)
}

16
model/file.go Fichier normal
Voir le fichier

@ -0,0 +1,16 @@
package model
type File struct {
ID uint `gorm:"column:file_id;primary_key"`
TorrentID uint `gorm:"column:torrent_id"`
Path string `gorm:"column:path"`
Filesize int64 `gorm:"column:filesize"`
Torrent *Torrent `gorm:"AssociationForeignKey:TorrentID;ForeignKey:torrent_id"`
}
// Returns the total size of memory allocated for this struct
func (f File) Size() int {
return (1 + len(f.Path) + 2) * 8;
}

Voir le fichier

@ -44,6 +44,7 @@ type Torrent struct {
Leechers uint32 `gorm:"column:leechers"`
Completed uint32 `gorm:"column:completed"`
LastScrape time.Time `gorm:"column:last_scrape"`
FileList []File `gorm:"ForeignKey:torrent_id"`
}
// Returns the total size of memory recursively allocated for this struct
@ -88,6 +89,11 @@ type CommentJSON struct {
Date time.Time `json:"date"`
}
type FileJSON struct {
Path string `json:"path"`
Length int64 `json:"length"`
}
type TorrentJSON struct {
ID string `json:"id"`
Name string `json:"name"`
@ -110,6 +116,7 @@ type TorrentJSON struct {
Leechers uint32 `json:"leechers"`
Completed uint32 `json:"completed"`
LastScrape time.Time `json:"last_scrape"`
FileList []File `json:"file_list"`
}
// ToJSON converts a model.Torrent to its equivalent JSON structure
@ -155,6 +162,7 @@ func (t *Torrent) ToJSON() TorrentJSON {
Seeders: t.Seeders,
Completed: t.Completed,
LastScrape: t.LastScrape,
FileList: t.FileList,
}
return res

Voir le fichier

@ -202,6 +202,10 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ
{
width: 12rem;
}
#mainmenu .navbar-form select.form-control#max
{
width: 8rem;
}
.special-img
{
position: relative;
@ -220,6 +224,7 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ
#mainmenu .badgemenu {
padding-top: 0;
margin-right: -50px; /* don't ask */
}
/* PROFILE PAGE */

Voir le fichier

@ -2,9 +2,9 @@ var night = localStorage.getItem("night");
function toggleNightMode() {
var night = localStorage.getItem("night");
if(night == "true") {
document.getElementById("style-dark").remove()
document.getElementsByTagName("head")[0].removeChild(darkStyleLink);
} else {
document.getElementsByTagName("head")[0].append(darkStyleLink);
document.getElementsByTagName("head")[0].appendChild(darkStyleLink);
}
localStorage.setItem("night", (night == "true") ? "false" : "true");
}
@ -78,5 +78,4 @@ function loadLanguages() {
xhr.send()
}
loadLanguages();
loadLanguages();

Voir le fichier

@ -8,11 +8,8 @@ import (
)
func FaqHandler(w http.ResponseWriter, r *http.Request) {
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
languages.SetTranslationFromRequest(faqTemplate, r, "en-us")
err := faqTemplate.ExecuteTemplate(w, "index.html", FaqTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
err := faqTemplate.ExecuteTemplate(w, "index.html", FaqTemplateVariables{Navigation{}, NewSearchForm(), GetUser(r), r.URL, mux.CurrentRoute(r)})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}

Voir le fichier

@ -5,6 +5,7 @@ import (
"html"
"net/http"
"strconv"
"strings"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
@ -21,6 +22,83 @@ import (
"github.com/gorilla/mux"
)
type ReassignForm struct {
AssignTo uint
By string
Data string
Torrents []uint
}
func (f *ReassignForm) ExtractInfo(r *http.Request) error {
f.By = r.FormValue("by")
if f.By != "olduser" && f.By != "torrentid" {
return fmt.Errorf("what?")
}
f.Data = strings.Trim(r.FormValue("data"), " \r\n")
if f.By == "olduser" {
if f.Data == "" {
return fmt.Errorf("No username given")
} else if strings.Contains(f.Data, "\n") {
return fmt.Errorf("More than one username given")
}
} else if f.By == "torrentid" {
if f.Data == "" {
return fmt.Errorf("No IDs given")
}
splitData := strings.Split(f.Data, "\n")
for i, tmp := range splitData {
tmp = strings.Trim(tmp, " \r")
torrent_id, err := strconv.ParseUint(tmp, 10, 0)
if err != nil {
return fmt.Errorf("Couldn't parse number on line %d", i+1)
}
f.Torrents = append(f.Torrents, uint(torrent_id))
}
}
tmp := r.FormValue("to")
parsed, err := strconv.ParseUint(tmp, 10, 0)
if err != nil {
return err
}
f.AssignTo = uint(parsed)
_, _, _, _, err = userService.RetrieveUser(r, tmp)
if err != nil {
return fmt.Errorf("User to assign to doesn't exist")
}
return nil
}
func (f *ReassignForm) ExecuteAction() (int, error) {
var toBeChanged []uint
var err error
if f.By == "olduser" {
toBeChanged, err = userService.RetrieveOldUploadsByUsername(f.Data)
if err != nil {
return 0, err
}
} else if f.By == "torrentid" {
toBeChanged = f.Torrents
}
num := 0
for _, torrent_id := range toBeChanged {
torrent, err2 := torrentService.GetRawTorrentById(torrent_id)
if err2 == nil {
torrent.UploaderID = f.AssignTo
db.ORM.Save(&torrent)
num += 1
}
}
// TODO: clean shit from user_uploads_old if needed
return num, nil
}
func IndexModPanel(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if userPermission.HasAdmin(currentUser) {
@ -32,7 +110,9 @@ func IndexModPanel(w http.ResponseWriter, r *http.Request) {
torrentReports, _, _ := reportService.GetAllTorrentReports(offset, 0)
languages.SetTranslationFromRequest(panelIndex, r, "en-us")
htv := PanelIndexVbs{torrents, model.TorrentReportsToJSON(torrentReports), users, comments, NewSearchForm(), currentUser, r.URL}
search := NewSearchForm()
search.ShowItemsPerPage = false
htv := PanelIndexVbs{torrents, model.TorrentReportsToJSON(torrentReports), users, comments, search, currentUser, r.URL}
err := panelIndex.ExecuteTemplate(w, "admin_index.html", htv)
log.CheckError(err)
} else {
@ -59,9 +139,9 @@ func TorrentsListPanel(w http.ResponseWriter, r *http.Request) {
searchParam, torrents, _, err := search.SearchByQuery(r, pagenum)
searchForm := SearchForm{
SearchParam: searchParam,
Category: searchParam.Category.String(),
HideAdvancedSearch: false,
SearchParam: searchParam,
Category: searchParam.Category.String(),
ShowItemsPerPage: true,
}
languages.SetTranslationFromRequest(panelTorrentList, r, "en-us")
@ -271,3 +351,43 @@ func TorrentReportDeleteModPanel(w http.ResponseWriter, r *http.Request) {
http.Error(w, "admins only", http.StatusForbidden)
}
}
func TorrentReassignModPanel(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if !userPermission.HasAdmin(currentUser) {
http.Error(w, "admins only", http.StatusForbidden)
return
}
languages.SetTranslationFromRequest(panelTorrentReassign, r, "en-us")
htv := PanelTorrentReassignVbs{ReassignForm{}, NewSearchForm(), currentUser, form.NewErrors(), form.NewInfos(), r.URL}
err := panelTorrentReassign.ExecuteTemplate(w, "admin_index.html", htv)
log.CheckError(err)
}
func TorrentPostReassignModPanel(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if !userPermission.HasAdmin(currentUser) {
http.Error(w, "admins only", http.StatusForbidden)
return
}
var rForm ReassignForm
err := form.NewErrors()
infos := form.NewInfos()
err2 := rForm.ExtractInfo(r)
if err2 != nil {
err["errors"] = append(err["errors"], err2.Error())
} else {
count, err2 := rForm.ExecuteAction()
if err2 != nil {
err["errors"] = append(err["errors"], "Something went wrong")
} else {
infos["infos"] = append(infos["infos"], fmt.Sprintf("%d torrents updated.", count))
}
}
htv := PanelTorrentReassignVbs{rForm, NewSearchForm(), currentUser, err, infos, r.URL}
err_ := panelTorrentReassign.ExecuteTemplate(w, "admin_index.html", htv)
log.CheckError(err_)
}

Voir le fichier

@ -10,11 +10,8 @@ import (
func NotFoundHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, NewSearchForm(), GetUser(r), r.URL, mux.CurrentRoute(r)})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}

Voir le fichier

@ -101,17 +101,14 @@ func init() {
Router.HandleFunc("/mod/torrent/delete", TorrentDeleteModPanel).Name("mod_tdelete")
Router.HandleFunc("/mod/report/delete", TorrentReportDeleteModPanel).Name("mod_trdelete")
Router.HandleFunc("/mod/comment/delete", CommentDeleteModPanel).Name("mod_cdelete")
Router.HandleFunc("/mod/reassign", TorrentReassignModPanel).Name("mod_treassign").Methods("GET")
Router.HandleFunc("/mod/reassign", TorrentPostReassignModPanel).Name("mod_treassign").Methods("POST")
//reporting a torrent
Router.HandleFunc("/report/{id}", ReportTorrentHandler).Methods("POST").Name("post_comment")
Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles)
//Router.HandleFunc("/report/create", gzipTorrentReportCreateHandler).Name("torrent_report_create").Methods("POST")
// TODO Allow only moderators to access /moderation/*
//Router.HandleFunc("/moderation/report/delete", gzipTorrentReportDeleteHandler).Name("torrent_report_delete").Methods("POST")
//Router.HandleFunc("/moderation/torrent/delete", gzipTorrentDeleteHandler).Name("torrent_delete").Methods("POST")
Router.HandleFunc("/language", SeeLanguagesHandler).Methods("GET").Name("see_languages")
Router.HandleFunc("/language", ChangeLanguageHandler).Methods("POST").Name("change_language")

Voir le fichier

@ -38,9 +38,9 @@ func SearchHandler(w http.ResponseWriter, r *http.Request) {
navigationTorrents := Navigation{nbTorrents, int(searchParam.Max), pagenum, "search_page"}
// Convert back to strings for now.
searchForm := SearchForm{
SearchParam: searchParam,
Category: searchParam.Category.String(),
HideAdvancedSearch: false,
SearchParam: searchParam,
Category: searchParam.Category.String(),
ShowItemsPerPage: true,
}
htv := HomeTemplateVariables{b, searchForm, navigationTorrents, GetUser(r), r.URL, mux.CurrentRoute(r)}

Voir le fichier

@ -9,7 +9,7 @@ var TemplateDir = "templates"
var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate, changeLanguageTemplate *template.Template
var panelIndex, panelTorrentList, panelUserList, panelCommentList, panelTorrentEd, panelTorrentReportList *template.Template
var panelIndex, panelTorrentList, panelUserList, panelCommentList, panelTorrentEd, panelTorrentReportList, panelTorrentReassign *template.Template
type templateLoader struct {
templ **template.Template
@ -127,6 +127,11 @@ func ReloadTemplates() {
name: "torrent_report",
file: filepath.Join("admin", "torrent_report.html"),
},
templateLoader{
templ: &panelTorrentReassign,
name: "torrent_reassign",
file: filepath.Join("admin", "reassign.html"),
},
}
for idx := range modTempls {

Voir le fichier

@ -36,6 +36,24 @@ var FuncMap = template.FuncMap{
}
return "error"
},
"genSearchWithOrdering": func(currentUrl url.URL, sortBy string) template.URL {
values := currentUrl.Query()
order := false
if _, ok := values["order"]; ok {
order, _ = strconv.ParseBool(values["order"][0])
if values["sort"][0]==sortBy {
order=!order //Flip order by repeat-clicking
} else {
order=false //Default to descending when sorting by something new
}
}
values.Set("sort", sortBy)
values.Set("order", strconv.FormatBool(order))
currentUrl.RawQuery=values.Encode()
return template.URL(currentUrl.String())
},
"genNav": func(nav Navigation, currentUrl *url.URL, pagesSelectable int) template.HTML {
var ret = ""
if (nav.TotalItem > 0) {

Voir le fichier

@ -157,6 +157,7 @@ type PanelCommentListVbs struct {
User *model.User
URL *url.URL // For parsing Url in templates
}
type PanelTorrentEdVbs struct {
Upload UploadForm
Search SearchForm
@ -174,6 +175,15 @@ type PanelTorrentReportListVbs struct {
URL *url.URL // For parsing Url in templates
}
type PanelTorrentReassignVbs struct {
Reassign ReassignForm
Search SearchForm // unused?
User *model.User // unused?
FormErrors map[string][]string
FormInfos map[string][]string
URL *url.URL // For parsing Url in templates
}
/*
* Variables used by the upper ones
*/
@ -186,14 +196,15 @@ type Navigation struct {
type SearchForm struct {
common.SearchParam
Category string
HideAdvancedSearch bool
Category string
ShowItemsPerPage bool
}
// Some Default Values to ease things out
func NewSearchForm() SearchForm {
return SearchForm{
Category: "_",
ShowItemsPerPage: true,
}
}

Voir le fichier

@ -66,9 +66,7 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
err["errors"] = append(err["errors"], errUser.Error())
}
languages.SetTranslationFromRequest(viewUserDeleteTemplate, r, "en-us")
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
htv := UserVerifyTemplateVariables{err, searchForm, Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
htv := UserVerifyTemplateVariables{err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
errorTmpl := viewUserDeleteTemplate.ExecuteTemplate(w, "index.html", htv)
if errorTmpl != nil {
http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
@ -81,9 +79,7 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
if unfollow != nil {
infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_unfollowed_msg"), userProfile.Username))
}
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
htv := UserProfileVariables{&userProfile, infosForm, searchForm, Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
htv := UserProfileVariables{&userProfile, infosForm, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv)
if err != nil {
@ -91,11 +87,8 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
}
}
} else {
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, NewSearchForm(), GetUser(r), r.URL, mux.CurrentRoute(r)})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
@ -113,21 +106,16 @@ func UserDetailsHandler(w http.ResponseWriter, r *http.Request) {
b := form.UserForm{}
modelHelper.BindValueForm(&b, r)
languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us")
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
availableLanguages := languages.GetAvailableLanguages()
htv := UserProfileEditVariables{&userProfile, b, form.NewErrors(), form.NewInfos(), availableLanguages, searchForm, Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
htv := UserProfileEditVariables{&userProfile, b, form.NewErrors(), form.NewInfos(), availableLanguages, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
err := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
} else {
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, NewSearchForm(), GetUser(r), r.URL, mux.CurrentRoute(r)})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
@ -185,21 +173,15 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
}
} else {
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, NewSearchForm(), GetUser(r), r.URL, mux.CurrentRoute(r)})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
} else {
searchForm := NewSearchForm()
searchForm.HideAdvancedSearch = true
languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us")
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)})
err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, NewSearchForm(), GetUser(r), r.URL, mux.CurrentRoute(r)})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}

Voir le fichier

@ -5,7 +5,7 @@ type WhereParams struct {
Params []interface{}
}
func CreateWhereParams(conditions string, params ...string) WhereParams {
func CreateWhereParams(conditions string, params ...interface{}) WhereParams {
whereParams := WhereParams{
Conditions: conditions,
Params: make([]interface{}, len(params)),

Voir le fichier

@ -0,0 +1,228 @@
package filesizeFetcher;
import (
"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/util/log"
serviceBase "github.com/ewhal/nyaa/service"
torrentService "github.com/ewhal/nyaa/service/torrent"
"sync"
"time"
)
type FilesizeFetcher struct {
torrentClient *torrent.Client
results chan Result
queueSize int
timeout int
maxDays int
done chan int
queue []*FetchOperation
queueMutex sync.Mutex
failedOperations map[uint]struct{}
wakeUp *time.Ticker
wg sync.WaitGroup
}
func New(fetcherConfig *config.FilesizeFetcherConfig) (fetcher *FilesizeFetcher, err error) {
client, err := torrent.NewClient(nil)
fetcher = &FilesizeFetcher{
torrentClient: client,
results: make(chan Result, fetcherConfig.QueueSize),
queueSize: fetcherConfig.QueueSize,
timeout: fetcherConfig.Timeout,
maxDays: fetcherConfig.MaxDays,
done: make(chan int, 1),
failedOperations: make(map[uint]struct{}),
wakeUp: time.NewTicker(time.Second * time.Duration(fetcherConfig.WakeUpInterval)),
}
return
}
func (fetcher *FilesizeFetcher) isFetchingOrFailed(t model.Torrent) bool {
for _, op := range fetcher.queue {
if op.torrent.ID == t.ID {
return true
}
}
_, ok := fetcher.failedOperations[t.ID]
return ok
}
func (fetcher *FilesizeFetcher) addToQueue(op *FetchOperation) bool {
fetcher.queueMutex.Lock()
defer fetcher.queueMutex.Unlock()
if len(fetcher.queue) + 1 > fetcher.queueSize {
return false
}
fetcher.queue = append(fetcher.queue, op)
return true
}
func (fetcher *FilesizeFetcher) removeFromQueue(op *FetchOperation) bool {
fetcher.queueMutex.Lock()
defer fetcher.queueMutex.Unlock()
for i, queueOP := range fetcher.queue {
if queueOP == op {
fetcher.queue = append(fetcher.queue[:i], fetcher.queue[i+1:]...)
return true
}
}
return false
}
func updateFileList(dbEntry model.Torrent, info *metainfo.Info) error {
log.Infof("TID %d has %d files.", dbEntry.ID, len(info.Files))
for _, file := range info.Files {
path := file.DisplayPath(info)
fileExists := false
for _, existingFile := range dbEntry.FileList {
if existingFile.Path == path {
fileExists = true
break
}
}
if !fileExists {
log.Infof("Adding file %s to filelist of TID %d", path, dbEntry.ID)
dbFile := model.File{
TorrentID: dbEntry.ID,
Path: path,
Filesize: file.Length,
}
err := db.ORM.Create(&dbFile).Error
if err != nil {
return err
}
}
}
return nil
}
func (fetcher *FilesizeFetcher) gotResult(r Result) {
updatedSuccessfully := false
if r.err != nil {
log.Infof("Failed to get torrent filesize (TID: %d), err %v", r.operation.torrent.ID, r.err)
} else if r.info.TotalLength() == 0 {
log.Infof("Got length 0 for torrent TID: %d. Possible bug?", r.operation.torrent.ID)
} else {
log.Infof("Got length %d for torrent TID: %d. Updating.", r.info.TotalLength(), r.operation.torrent.ID)
r.operation.torrent.Filesize = r.info.TotalLength()
_, err := torrentService.UpdateTorrent(r.operation.torrent)
if err != nil {
log.Infof("Failed to update torrent TID: %d with new filesize", r.operation.torrent.ID)
} else {
updatedSuccessfully = true
}
// Also update the File list with FileInfo, I guess.
err = updateFileList(r.operation.torrent, r.info)
if err != nil {
log.Infof("Failed to update file list of TID %d", r.operation.torrent.ID)
}
}
if !updatedSuccessfully {
fetcher.failedOperations[r.operation.torrent.ID] = struct{}{}
}
fetcher.removeFromQueue(r.operation)
}
func (fetcher *FilesizeFetcher) fillQueue() {
toFill := fetcher.queueSize - len(fetcher.queue)
if toFill <= 0 {
return
}
oldest := time.Now().Add(0 - (time.Hour * time.Duration(24 * fetcher.maxDays)))
params := serviceBase.CreateWhereParams("(filesize IS NULL OR filesize = 0) AND date > ?", oldest)
// Get up to queueSize + len(failed) torrents, so we get at least some fresh new ones.
dbTorrents, count, err := torrentService.GetTorrents(params, fetcher.queueSize + len(fetcher.failedOperations), 0)
if err != nil {
log.Infof("Failed to get torrents for filesize updating")
return
}
if count == 0 {
log.Infof("No torrents for filesize update")
return
}
for _, T := range dbTorrents {
if fetcher.isFetchingOrFailed(T) {
continue
}
log.Infof("Added TID %d for filesize fetching", T.ID)
operation := NewFetchOperation(fetcher, T)
if fetcher.addToQueue(operation) {
fetcher.wg.Add(1)
go operation.Start(fetcher.results)
} else {
break
}
}
}
func (fetcher *FilesizeFetcher) run() {
var result Result
defer fetcher.wg.Done()
done := 0
fetcher.fillQueue()
for done == 0 {
select {
case done = <-fetcher.done:
break
case result = <-fetcher.results:
fetcher.gotResult(result)
fetcher.fillQueue()
break
case <-fetcher.wakeUp.C:
fetcher.fillQueue()
break
}
}
}
func (fetcher *FilesizeFetcher) RunAsync() {
fetcher.wg.Add(1)
go fetcher.run()
}
func (fetcher *FilesizeFetcher) Close() error {
fetcher.queueMutex.Lock()
defer fetcher.queueMutex.Unlock()
// Send the done event to every Operation
for _, op := range fetcher.queue {
op.done <- 1
}
fetcher.done <- 1
fetcher.wg.Wait()
return nil
}
func (fetcher *FilesizeFetcher) Wait() {
fetcher.wg.Wait()
}

Voir le fichier

@ -0,0 +1,60 @@
package filesizeFetcher;
import (
"github.com/anacrolix/torrent/metainfo"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/model"
"github.com/ewhal/nyaa/util"
"errors"
"time"
"strings"
)
type FetchOperation struct {
fetcher *FilesizeFetcher
torrent model.Torrent
done chan int
}
type Result struct {
operation *FetchOperation
err error
info *metainfo.Info
}
func NewFetchOperation(fetcher *FilesizeFetcher, dbEntry model.Torrent) (op *FetchOperation) {
op = &FetchOperation{
fetcher: fetcher,
torrent: dbEntry,
done: make(chan int, 1),
}
return
}
// Should be started from a goroutine somewhere
func (op *FetchOperation) Start(out chan Result) {
defer op.fetcher.wg.Done()
magnet := util.InfoHashToMagnet(strings.TrimSpace(op.torrent.Hash), op.torrent.Name, config.Trackers...)
downloadingTorrent, err := op.fetcher.torrentClient.AddMagnet(magnet)
if err != nil {
out <- Result{op, err, nil}
return
}
timeoutTicker := time.NewTicker(time.Second * time.Duration(op.fetcher.timeout))
select {
case <-downloadingTorrent.GotInfo():
downloadingTorrent.Drop()
out <- Result{op, nil, downloadingTorrent.Info()}
break
case <-timeoutTicker.C:
downloadingTorrent.Drop()
out <- Result{op, errors.New("Timeout"), nil}
break
case <-op.done:
downloadingTorrent.Drop()
break
}
}

Voir le fichier

@ -0,0 +1,45 @@
package filesizeFetcher;
import (
"testing"
"github.com/anacrolix/torrent"
"github.com/ewhal/nyaa/model"
)
func TestInvalidHash(t *testing.T) {
client, err := torrent.NewClient(nil)
if err != nil {
t.Skipf("Failed to create client, with err %v. Skipping.", err)
}
fetcher := &FilesizeFetcher{
timeout: 5,
torrentClient: client,
results: make(chan Result, 1),
}
dbEntry := model.Torrent{
Hash: "INVALID",
Name: "Invalid",
}
op := NewFetchOperation(fetcher, dbEntry)
fetcher.wg.Add(1)
op.Start(fetcher.results)
var res Result
select {
case res = <-fetcher.results:
break
default:
t.Fatal("No result in channel, should have one")
}
if res.err == nil {
t.Fatal("Got no error, should have got invalid magnet")
}
t.Logf("Got error %s, shouldn't be timeout", res.err)
}

Voir le fichier

@ -71,7 +71,7 @@ func GetTorrentById(id string) (torrent model.Torrent, err error) {
torrent.Uploader = new(model.User)
db.ORM.Where("user_id = ?", torrent.UploaderID).Find(torrent.Uploader)
torrent.OldUploader = ""
if torrent.ID <= config.LastOldTorrentID {
if torrent.ID <= config.LastOldTorrentID && torrent.UploaderID == 0 {
var tmp model.UserUploadsOld
if !db.ORM.Where("torrent_id = ?", torrent.ID).Find(&tmp).RecordNotFound() {
torrent.OldUploader = tmp.Username
@ -88,6 +88,15 @@ func GetTorrentById(id string) (torrent model.Torrent, err error) {
return
}
// won't fetch user or comments
func GetRawTorrentById(id uint) (torrent model.Torrent, err error) {
err = nil
if db.ORM.Where("torrent_id = ?", id).Find(&torrent).RecordNotFound() {
err = errors.New("Article is not found.")
}
return
}
func GetTorrentsOrderByNoCount(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, err error) {
torrents, _, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, false)
return

Voir le fichier

@ -16,7 +16,11 @@ func CheckTrackers(trackers []string) bool {
"://tracker.istole.it:80",
"://tracker.ccc.de:80",
"://bt2.careland.com.cn:6969",
"://announce.torrentsmd.com:8080"}
"://announce.torrentsmd.com:8080",
"://open.demonii.com:1337",
"://tracker.btcake.com",
"://tracker.prq.to",
"://bt.rghost.net"}
var numGood int
for _, t := range trackers {

Voir le fichier

@ -50,7 +50,7 @@ func IsAgreed(termsAndConditions string) bool { // TODO: Inline function
type RegistrationForm struct {
Username string `form:"username" needed:"true" len_min:"3" len_max:"20"`
Email string `form:"email"`
Password string `form:"password" needed:"true" len_min:"6" len_max:"25" equalInput:"ConfirmPassword"`
Password string `form:"password" needed:"true" len_min:"6" len_max:"72" equalInput:"ConfirmPassword"`
ConfirmPassword string `form:"password_confirmation" omit:"true" needed:"true"`
CaptchaID string `form:"captchaID" omit:"true" needed:"true"`
TermsAndConditions bool `form:"t_and_c" omit:"true" needed:"true" equal:"true" hum_name:"Terms and Conditions"`
@ -67,8 +67,8 @@ type UserForm struct {
Username string `form:"username" needed:"true" len_min:"3" len_max:"20"`
Email string `form:"email"`
Language string `form:"language" default:"en-us"`
CurrentPassword string `form:"current_password" len_min:"6" len_max:"25" omit:"true"`
Password string `form:"password" len_min:"6" len_max:"25" equalInput:"Confirm_Password"`
CurrentPassword string `form:"current_password" len_min:"6" len_max:"72" omit:"true"`
Password string `form:"password" len_min:"6" len_max:"72" equalInput:"Confirm_Password"`
Confirm_Password string `form:"password_confirmation" omit:"true"`
Status int `form:"status" default:"0"`
}

Voir le fichier

@ -253,6 +253,19 @@ func RetrieveUserByUsername(username string) (*model.PublicUser, string, int, er
return &model.PublicUser{User: &user}, username, http.StatusOK, nil
}
func RetrieveOldUploadsByUsername(username string) ([]uint, error) {
var ret []uint
var tmp []*model.UserUploadsOld
err := db.ORM.Where("username = ?", username).Find(&tmp).Error
if err != nil {
return ret, err
}
for _, tmp2 := range tmp {
ret = append(ret, tmp2.TorrentId)
}
return ret, nil
}
// RetrieveUserForAdmin retrieves a user for an administrator.
func RetrieveUserForAdmin(id string) (model.User, int, error) {
var user model.User

Voir le fichier

@ -39,14 +39,15 @@
<h2 id="trackers">{{T "which_trackers_do_you_recommend"}}</h2>
<p>{{T "answer_which_trackers_do_you_recommend"}}</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
http://mgtracker.org:6969/announce
http://tracker.baka-sub.cf/announce</pre>
<pre>udp://tracker.doko.moe:6969</pre>
<p>{{T "other_trackers"}}</p>
<pre>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
http://mgtracker.org:6969/announce
http://tracker.baka-sub.cf/announce</pre>
<h2>{{T "how_can_i_help"}}</h2>
<p>{{T "answer_how_can_i_help"}}</p>

Voir le fichier

@ -7,6 +7,7 @@
<img src="https://www.gravatar.com/avatar/{{ .MD5 }}?s=50" class="img-circle special-img"> {{ .Username }} <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}"><i class="fa fa-cog"></i> {{T "profile"}}</a></li>
<li><a href="{{ genRoute "user_profile_edit" "id" (print .ID) "username" .Username }}"><i class="fa fa-cog"></i> {{T "settings"}}</a></li>
{{if HasAdmin . }}<li><a href="{{ genRoute "mod_index" }}"><i class="fa fa-cog"></i> {{T "moderation"}}</a></li>{{end}}
<li class="divider"></li>
<li><a href="{{ genRoute "user_logout" }}"><i class="fa fa-sign-out"></i> {{ T "sign_out"}}</a></li>

Voir le fichier

@ -5,7 +5,7 @@
<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>
<input type="text" name="solution" id="solution" class="form-control" placeholder="Captcha" autocomplete="off" required>
</div>
{{end}}
{{end}}

Voir le fichier

@ -8,7 +8,7 @@
darkStyleLink.type = "text/css";
darkStyleLink.href = "/css/style-night.css"
if (night == "true") {
document.getElementsByTagName("head")[0].append(darkStyleLink);
document.getElementsByTagName("head")[0].appendChild(darkStyleLink);
}
</script>
{{end}}

Voir le fichier

@ -9,6 +9,10 @@
<h3>{{ T "personal_info"}}</h3>
<form class="form-horizontal" role="form" method="POST">
<div class="form-group">
<label class="col-lg-3 control-label">{{T "api_token" }}:</label>
<div class="col-lg-8">{{.ApiToken}}</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">{{ T "email_address" }}:</label>
<div class="col-lg-8">

Voir le fichier

@ -31,24 +31,8 @@
<option value="2" {{if eq .Search.Status 2}}selected{{end}}>{{T "trusted"}}</option>
<option value="3" {{if eq .Search.Status 3}}selected{{end}}>A+</option>
</select>
<input type="hidden" name="userID" value="{{ .Search.UserID }}">
{{end}}
{{define "search_advanced"}}
<select name="sort" class="form-control input-sm">
<option value="0" {{if eq .Search.Sort 0}}selected{{end}}>{{T "id"}}</option>
<option value="1" {{if eq .Search.Sort 1}}selected{{end}}>{{T "name"}}</option>
<option value="2" {{if eq .Search.Sort 2}}selected{{end}}>{{T "date"}}</option>
<option value="3" {{if eq .Search.Sort 3}}selected{{end}}>{{T "downloads"}}</option>
<option value="4" {{if eq .Search.Sort 4}}selected{{end}}>{{T "size"}}</option>
<option value="5" {{if eq .Search.Sort 4}}selected{{end}}>{{T "seeders"}}</option>
<option value="6" {{if eq .Search.Sort 4}}selected{{end}}>{{T "leechers"}}</option>
<option value="7" {{if eq .Search.Sort 4}}selected{{end}}>{{T "completed"}}</option>
</select>
<select name="order" class="form-control input-sm">
<option value="false" {{if eq .Search.Order false}}selected{{end}}>{{T "descending"}}</option>
<option value="true" {{if eq .Search.Order true}}selected{{end}}>{{T "ascending"}}</option>
</select>
<select name="max" class="form-control input-sm">
{{ if .Search.ShowItemsPerPage }}
<select id="max" name="max" class="form-control input-sm">
<option value="5" {{if eq .Navigation.MaxItemPerPage 5}}selected{{end}}>5</option>
<option value="10" {{if eq .Navigation.MaxItemPerPage 10}}selected{{end}}>10</option>
<option value="15" {{if eq .Navigation.MaxItemPerPage 15}}selected{{end}}>15</option>
@ -65,8 +49,26 @@
<option value="200" {{if eq .Navigation.MaxItemPerPage 200}}selected{{end}}>200</option>
<option value="300" {{if eq .Navigation.MaxItemPerPage 300}}selected{{end}}>300</option>
</select>
{{ end }}
<input type="hidden" name="userID" value="{{ .Search.UserID }}">
{{end}}
{{/* this is used in the modpanel */}}
{{define "search_advanced"}}
<select name="sort" class="form-control input-sm">
<option value="0" {{if eq .Search.Sort 0}}selected{{end}}>{{T "id"}}</option>
<option value="1" {{if eq .Search.Sort 1}}selected{{end}}>{{T "name"}}</option>
<option value="2" {{if eq .Search.Sort 2}}selected{{end}}>{{T "date"}}</option>
<option value="3" {{if eq .Search.Sort 3}}selected{{end}}>{{T "downloads"}}</option>
<option value="4" {{if eq .Search.Sort 4}}selected{{end}}>{{T "size"}}</option>
<option value="5" {{if eq .Search.Sort 4}}selected{{end}}>{{T "seeders"}}</option>
<option value="6" {{if eq .Search.Sort 4}}selected{{end}}>{{T "leechers"}}</option>
<option value="7" {{if eq .Search.Sort 4}}selected{{end}}>{{T "completed"}}</option>
</select>
<select name="order" class="form-control input-sm">
<option value="false" {{if eq .Search.Order false}}selected{{end}}>{{T "descending"}}</option>
<option value="true" {{if eq .Search.Order true}}selected{{end}}>{{T "ascending"}}</option>
</select>
{{end}}
{{define "search_button"}}
<div class="input-group">
<input name="q" class="form-control input-sm" placeholder="{{T "search"}}" type="text" value="{{.Search.Query}}">

Voir le fichier

@ -10,8 +10,9 @@
<th>{{T "size"}}</th>
<th>{{T "links"}}</th>
</tr>
{{ range .Torrents }}
{{ with .ToJSON }}
{{ range $i, $t := .Torrents }}
{{ if lt $i 16 }}
{{ with $t.ToJSON }}
<tr class="torrent-info
{{if eq .Status 2}}remake{{end}}
{{if eq .Status 3}}trusted{{end}}
@ -42,6 +43,7 @@
</tr>
{{end}}
{{end}}
{{end}}
</table>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">

Voir le fichier

@ -9,8 +9,10 @@
{{ 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>
<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>
<tr>
<td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td>
<td><a>{{ .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>
<nav class="torrentNav" aria-label="Page navigation">

Voir le fichier

@ -11,7 +11,7 @@
<tr>
<td><a href="{{ genViewTorrentRoute .ID }}">{{ .Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">Edit</a>)</td>
<td><a href="{{ genRoute "mod_tlist" }}?userID={{.UploaderID}}">{{ .UploaderID }}</a></td>
<td><a href="{{ genRoute "mod_tdelete" }}?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>
<td><a href="{{ genRoute "mod_tdelete" }}?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>
@ -22,7 +22,7 @@
</nav>
<hr />
<h3 id="torrents">Last Torrents Report</h3>
<h3 id="torrents">Last Torrents Reports</h3>
<table class="table">
<tr>
<th class="col-xs-9">Torrent Name</th>
@ -36,13 +36,13 @@
<td><a href="{{ genRoute "view_torrent" "id" .Torrent.ID }}">{{ .Torrent.Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">Edit</a>)</td>
<td>{{.User.Username}}</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>
<td><a href="{{ genRoute "mod_trdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg"><i class="glyphicon glyphicon-trash"></i> {{ T "delete" }}</a></td>
</tr>
{{end}}
</table>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
<li><a href="{{ genRoute "mod_trlist" }}">More</a></li>
<li><a href="{{ genRoute "mod_trlist" }}">More</a></li>
</ul>
</nav>
<hr />
@ -53,11 +53,11 @@
<th class="col-xs-1">Action</th>
</tr>
{{ range .Users}}
{{range .Users}}
<tr>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?edit">{{ .Username }}</a></td>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?delete" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?edit">{{ .Username }}</a></td>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?delete" 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>
@ -75,15 +75,18 @@
<th class="col-xs-1">Action</th>
</tr>
{{ range .Comments}}
{{range .Comments}}
<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>
<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>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
<li><a href="{{ genRoute "mod_clist" }}">More</a></li>
<li><a href="{{ genRoute "mod_clist" }}">More</a></li>
</ul>
</nav>
{{end}}

Voir le fichier

@ -12,17 +12,6 @@
<label for="name">{{T "name"}}</label>
<input type="text" name="name" class="form-control" placeholder="File Name" value="{{.Name}}" required>
</div>
<!--
<div class="form-group">
<label for="torrent">{{T "torrent_file"}}</label>
<input type="file" name="torrent" id="torrent" accept=".torrent">
<p class="help-block">{{T "uploading_file_prefills_fields"}}</p>
</div>
<div class="form-group">
<label for="magnet">{{T "magnet_link"}}</label>
<input type="text" name="magnet" class="form-control"
style="width:60rem" placeholder="{{T "magnet_link"}}" value="{{.Magnet}}">
</div>-->
<div class="form-group">
<label for="c">{{T "category"}}</label>
<select name="c" class="form-control input-sm">

32
templates/admin/reassign.html Fichier normal
Voir le fichier

@ -0,0 +1,32 @@
{{define "title"}}Torrent Reassign{{end}}
{{define "content"}}
<form enctype="multipart/form-data" method="POST">
{{ range (index $.FormInfos "infos")}}
<div class="alert alert-info"><a class="panel-close close" data-dismiss="alert">×</a><i class="glyphicon glyphicon-info-sign"></i> {{ . }}</div>
{{end}}
{{ range (index $.FormErrors "errors")}}
<div class="alert alert-danger"><a class="panel-close close" data-dismiss="alert">×</a><i class="glyphicon glyphicon-exclamation-sign"></i> {{ . }}</div>
{{end}}
<p>Reassigning torrents to a new user is not easily reverted and should be done with care.</p>
{{with .Reassign}}
<div class="form-group">
<label for="name">Reassign to:</label>
<input type="text" name="to" class="form-control" placeholder="User ID" {{if ne .AssignTo 0}}value="{{.AssignTo}}"{{end}} required>
</div>
<div class="form-group">
<label for="by">Reassign based on:</label><br />
<input type="radio" name="by" value="olduser" {{if eq .By "olduser"}}selected{{end}} required> Old Username<br />
<input type="radio" name="by" value="torrentid" {{if eq .By "torrentid"}}selected{{end}} required> Torrent ID
</div>
<div class="form-group">
<p>One ID per line <b>or</b> a single username</p>
<textarea rows="20" cols="40" name="data">{{.Data}}</textarea>
</div>
{{end}}
<p>Might take a long time, do <b>NOT</b> abort the request.</p>
<button type="submit" class="btn btn-success">{{T "save_changes"}}</button>
</form>
{{end}}

Voir le fichier

@ -13,7 +13,7 @@
<tr>
<td><a href="{{ genViewTorrentRoute .ID }}">{{ .Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">Edit</a>)</td>
<td><a href="{{ genRoute "mod_tlist" }}?userID={{.UploaderID}}">{{ .UploaderID }}</a></td>
<td><a href="{{ genRoute "mod_tdelete" }}?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>
<td><a href="{{ genRoute "mod_tdelete" }}?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

@ -9,8 +9,8 @@
{{ range .Users}}
<tr>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?edit">{{ .Username }}</a></td>
<td>{{if gt .ID 0}}<a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?delete" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a>{{end}}</td>
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?edit">{{ .Username }}</a></td>
<td>{{if gt .ID 0}}<a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?delete" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i> {{ T "delete" }}</a>{{end}}</td>
</tr>
{{end}}
</table>

Voir le fichier

@ -47,7 +47,13 @@
<li><a href="{{ genRoute "mod_tlist"}}">{{T "Torrents"}}</a></li>
<li><a href="{{ genRoute "mod_ulist"}}">{{T "Users"}}</a></li>
<li><a href="{{ genRoute "mod_clist"}}">{{T "Comments"}}</a></li>
<li><a href="{{ genRoute "mod_trlist"}}">{{T "Torrent Reports"}}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Other<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{{ genRoute "mod_trlist"}}">{{T "Torrent Reports"}}</a></li>
<li><a href="{{ genRoute "mod_treassign" }}">Torrent Reassign</a></li>
</ul>
</li>
</ul>
{{block "badge_user" .}}{{end}}

Voir le fichier

@ -1,7 +1,7 @@
{{define "title"}}{{T "home"}}{{end}}
{{define "contclass"}}cont-home{{end}}
{{define "content"}}
<div class="blockBody">
<div class="blockBody">
<ul class="list-inline" aria-label="Page navigation">
<li><img id="mascot" src="/img/renchon.png" /></li>
<li style="padding-top: 7%;" class="pull-right"><ul class="pagination">
@ -13,10 +13,16 @@
<table class="table custom-table-hover">
<tr>
<th class="col-xs-1 hidden-xs">{{T "category"}}</th>
<th class="col-xs-8">{{T "name"}}</th>
<th class="col-xs-1 hidden-xs">{{T "S"}} / {{T "L"}} / {{T "D"}}</th>
<th class="col-xs-1 hidden-xs">{{T "date"}}</th>
<th class="col-xs-1 hidden-xs">{{T "size"}}</th>
<th class="col-xs-8">
<a href="{{ genSearchWithOrdering .URL "1" }}">{{T "name"}}</a>
</th>
<th class="col-xs-1 hidden-xs">
<a href="{{ genSearchWithOrdering .URL "4" }}">{{T "S"}}</a> /
<a href="{{ genSearchWithOrdering .URL "4" }}">{{T "L"}}</a> /
<a href="{{ genSearchWithOrdering .URL "3" }}">{{T "D"}}</a>
</th>
<th class="col-xs-1 hidden-xs"><a href="{{ genSearchWithOrdering .URL "2" }}">{{T "date"}}</th></a>
<th class="col-xs-1 hidden-xs"><a href="{{ genSearchWithOrdering .URL "4" }}">{{T "size"}}</a></th>
<th class="col-xs-1 hidden-xs">{{T "links"}}</th>
</tr>
{{ range .ListTorrents}}

Voir le fichier

@ -72,21 +72,6 @@
<div style="padding-top: 10rem"></div>
<div class="container {{block "contclass" .}}generic{{end}}" id="container">
{{if not .Search.HideAdvancedSearch}}
<div class="blockBody" style="text-align:center">
<a href="#advanced-search" data-toggle="collapse">{{T "advanced_search"}}</a><br />
<form id="advanced-search" class="navbar-form collapse" role="search" action="/search" method="get">
<div class="form-group">
{{block "search_common" .}}{{end}}
{{block "search_advanced" .}}{{end}}
{{block "search_button" .}}{{end}}
</div>
</form>
<div style="clear:both"></div>
</div>
<div style="margin:0.5em"></div>
{{end}}
{{block "content" .}}{{T "nothing_here"}}{{end}}
</div>
@ -96,12 +81,15 @@
Powered by NyaaPantsu
</footer>
{{if eq .User.ID 0}}
<form method="POST" action="{{ .URL.Parse "/language" }}" id="bottom_language_selector_form">
<select id="bottom_language_selector" name="language" onchange="javascript:document.getElementById('bottom_language_selector_form').submit()" hidden class="form-control"></select>
</form>
<noscript>
<center><a href="{{ .URL.Parse "/language" }}">{{ T "change_language" }}</a></center>
</noscript>
{{end}}
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

Voir le fichier

@ -7,7 +7,7 @@
<div class="form-group">
<label for="name">{{T "name"}}</label>
<input type="text" name="name" class="form-control" placeholder="File Name" value="{{.Name}}" required>
<input type="text" name="name" id="name" class="form-control" placeholder="File Name" value="{{.Name}}" autofocus required>
</div>
<div class="form-group">
<label for="torrent">{{T "torrent_file"}}</label>
@ -16,12 +16,12 @@
</div>
<div class="form-group">
<label for="magnet">{{T "magnet_link"}}</label>
<input type="text" name="magnet" class="form-control"
<input type="text" name="magnet" id="magnet" class="form-control"
style="width:60rem" placeholder="{{T "magnet_link"}}" value="{{.Magnet}}">
</div>
<div class="form-group">
<label for="c">{{T "category"}}</label>
<select name="c" class="form-control input-sm" required>
<select name="c" id="c" class="form-control input-sm" required>
<option value="">{{T "select_a_torrent_category"}}</option>
<option value="3_12" {{if eq .Category "3_12"}}selected{{end}}>{{T "anime_amv"}}</option>
<option value="3_5" {{if eq .Category "3_5"}}selected{{end}}>{{T "anime_english_translated"}}</option>
@ -43,14 +43,14 @@
</select>
</div>
<div class="form-group">
<input type="checkbox" name="remake">
<input type="checkbox" name="remake" id="remake" >
<label for="remake">{{T "mark_as_remake"}}</label>
</div>
<div class="form-group">
<label for="desc">{{T "torrent_description"}}</label>
<p class="help-block">{{T "description_markdown_notice"}}</p>
<textarea name="desc" class="form-control" rows="10">{{.Description}}</textarea>
<textarea name="desc" id="desc" class="form-control" rows="10">{{.Description}}</textarea>
</div>
{{block "captcha" .}}{{end}}

Voir le fichier

@ -12,7 +12,7 @@
<div class="alert alert-danger">{{ . }}</div>
{{end}}
<div class="form-group">
<input type="text" name="username" id="username" class="form-control input-lg" autofocus="" placeholder="{{ T "email_address_or_username"}}">
<input type="text" name="username" id="username" class="form-control input-lg" autofocus placeholder="{{ T "email_address_or_username"}}">
{{ range (index $.FormErrors "username")}}
<p class="text-error">{{ . }}</p>
{{end}}

Voir le fichier

@ -12,13 +12,13 @@
<div class="alert alert-danger">{{ . }}</div>
{{end}}
<div class="form-group">
<input type="text" name="username" id="display_name" class="form-control input-lg" placeholder="{{T "username" }}" tabindex="1" value="{{ .Username }}">
<input type="text" name="username" id="display_name" class="form-control input-lg" placeholder="{{T "username" }}" value="{{ .Username }}" autofocus>
{{ range (index $.FormErrors "username")}}
<p class="text-error">{{ . }}</p>
{{end}}
</div>
<div class="form-group">
<input type="email" name="email" id="email" class="form-control input-lg" placeholder="{{T "email_address" }}" tabindex="2" value="{{ .Email }}">
<input type="email" name="email" id="email" class="form-control input-lg" placeholder="{{T "email_address" }}" value="{{ .Email }}">
{{ range (index $.FormErrors "email")}}
<p class="text-error">{{ . }}</p>
{{end}}
@ -26,7 +26,7 @@
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6">
<div class="form-group">
<input type="password" name="password" id="password" class="form-control input-lg" placeholder="{{T "password" }}" tabindex="3" value="{{ .Password }}">
<input type="password" name="password" id="password" class="form-control input-lg" placeholder="{{T "password" }}" value="{{ .Password }}">
{{ range (index $.FormErrors "password")}}
<p class="text-error">{{ . }}</p>
{{end}}
@ -34,7 +34,7 @@
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<div class="form-group">
<input type="password" name="password_confirmation" id="password_confirmation" class="form-control input-lg" placeholder="{{T "confirm_password" }}" tabindex="4">
<input type="password" name="password_confirmation" id="password_confirmation" class="form-control input-lg" placeholder="{{T "confirm_password" }}">
{{ range (index $.FormErrors "password_confirmation")}}
<p class="text-error">{{ . }}</p>
{{end}}
@ -44,7 +44,7 @@
<div class="row">
<div class="col-xs-4 col-sm-3 col-md-3">
<span class="button-checkbox">
<button type="button" class="btn hidden" data-color="info" tabindex="5">{{T "i_agree" }}</button>
<button type="button" class="btn hidden" data-color="info">{{T "i_agree" }}</button>
<input type="checkbox" name="t_and_c" id="t_and_c" value="1">
{{ range (index $.FormErrors "t_and_c")}}
<p class="text-error">{{ . }}</p>
@ -60,7 +60,7 @@
<hr class="colorgraph">
<div class="row">
<div class="col-xs-12 col-md-6"><input type="submit" value="{{T "register" }}" class="btn btn-primary btn-block btn-lg" tabindex="7"></div>
<div class="col-xs-12 col-md-6"><input type="submit" value="{{T "register" }}" class="btn btn-primary btn-block btn-lg"></div>
<div class="col-xs-12 col-md-6">or <a href="{{ genRoute "user_login" }}" class="">{{T "signin" }}</a></div>
</div>
</form>

Voir le fichier

@ -123,23 +123,22 @@
</div>
</div>
</div>
{{with .Torrent}}
<div id="reportModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Report Torrent #{{.ID}}</h4>
<h4 class="modal-title">Report Torrent #{{.Torrent.ID}}</h4>
</div>
<div class="modal-body">
<b>Report type:</b>
<form method="post" action="/report/{{.ID}}">
<input type="radio" name="report_type" value="illegal"> Illegal content<br />
<input type="radio" name="report_type" value="spam"> Spam / Garbage<br />
<input type="radio" name="report_type" value="wrongcat"> Wrong category<br />
<input type="radio" name="report_type" value="dup"> Duplicate / Deprecated<br />
{{end}}
<form method="post" action="/report/{{.Torrent.ID}}">
<input type="radio" name="report_type" value="illegal" required> Illegal content<br />
<input type="radio" name="report_type" value="spam" required> Spam / Garbage<br />
<input type="radio" name="report_type" value="wrongcat" required> Wrong category<br />
<input type="radio" name="report_type" value="dup" required> Duplicate / Deprecated<br />
{{block "captcha" .}}{{end}}
<button type="submit" class="btn btn-default">Report!</button>
</form> <br />

Voir le fichier

@ -273,7 +273,7 @@
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "Um Sukebei steht es hingegen schlechter. Zurzeit haben wir nur eine Sukebei-Datenbank bis 2016, aber eine neuere Datenbank steht möglicherweise zu Verfügung."
"translation": "Das Gleiche gilt für Sukebei, es fehlt auch fast nichts."
},
{
"id": "how_are_we_recovering",
@ -281,7 +281,7 @@
},
{
"id": "answer_how_are_we_recovering",
"translation": "Die obengenannten Datenbanken werden im Moment auf nyaa.pantsu.cat und sukebei.pantsu.cat bereitgestellt. Es gibt eine Suchfunktion und (fast) vollständige Funktionalität von nyaa sollte bald wiederhergestellt sein. Seeder/Leecher Statistiken sind via Scraping möglich und werden in Zukunft vielleicht wiederhergestellt werden, da andere Funktionen Vorrang haben."
"translation": "Die obengenannten Datenbanken werden im Moment auf nyaa.pantsu.cat und sukebei.pantsu.cat bereitgestellt. Es gibt eine Suchfunktion und (fast) vollständige Funktionalität von nyaa sollte bald wiederhergestellt sein."
},
{
"id": "are_the_trackers_working",
@ -305,11 +305,15 @@
},
{
"id": "which_trackers_do_you_recommend",
"translation": "Welche Tracker sind empfohlen?"
"translation": "Welche Tracker empfehlt ihr?"
},
{
"id": "answer_which_trackers_do_you_recommend",
"translation": "Wenn deine Torrents wegen Trackern Probleme machen, solltest du einige dieser hinzufügen:"
"translation": "Wir haben jetzt unseren eigenen Tracker! Füge ihn vorm Hochladen deinen Torrents hinzu:"
},
{
"id": "other_trackers",
"translation": "Allerdings solltest du diese auch hinzufügen, nur für den Fall, dass etwas schief läuft"
},
{
"id": "how_can_i_help",
@ -317,7 +321,7 @@
},
{
"id": "answer_how_can_i_help",
"translation": "Wenn du Erfahrungen mit Webseitenprogrammierung hast, kannst du dem IRC-Kanal #nyaapantsu auf irc.rizon.net (Englisch) beitreten. Wenn du irgendwelche Datenbanken hast, insbesondere für Sukebei, <b>LAD SIE HOCH</b>."
"translation": "Wenn du Erfahrungen mit Webseitenprogrammierung hast, kannst du dem IRC-Kanal #nyaapantsu auf irc.rizon.net (Englisch) beitreten. Wenn du irgendwelche Datenbanken hast, insbesondere für Sukebei, lad sie hoch."
},
{
"id": "your_design_sucks_found_a_bug",
@ -351,6 +355,10 @@
"id": "all_categories",
"translation": "Alle Kategorien"
},
{
"id": "select_a_torrent_category",
"translation": "Wähle eine Kategorie aus"
},
{
"id": "anime",
"translation": "Anime"
@ -587,6 +595,10 @@
"id": "torrent_status_remake",
"translation": "Remake"
},
{
"id": "profile_edit_page",
"translation": "Profil von %s bearbeiten"
},
{
"id": "seeders",
"translation": "Seeder"
@ -599,8 +611,16 @@
"id": "completed",
"translation": "Komplett"
},
{
"id": "change_language",
"translation": "Sprache ändern"
},
{
"id": "language_name",
"translation": "Deutsch"
},
{
"id": "delete",
"translation": "Löschen"
}
]

Voir le fichier

@ -249,7 +249,7 @@
},
{
"id": "future_not_looking_good",
"translation": "Future prospects for nyaa are not looking good. (It's dead)"
"translation": "Future prospects for nyaa are not looking good. (It's dead, Jim)"
},
{
"id": "recovery_effort",
@ -281,7 +281,7 @@
},
{
"id": "answer_how_are_we_recovering",
"translation": "The aforementioned databases are being hosted at nyaa.pantsu.cat and sukebei.pantsu.cat. There is a search function, and (almost) full nyaa functionality should be coming soon. Seeder/leecher statistics are possible via scraping and might be restored sometime in the future, since other feature take priority right now."
"translation": "The aforementioned databases are being hosted at nyaa.pantsu.cat and sukebei.pantsu.cat. There is a search function, and (almost) full nyaa functionality should be coming soon."
},
{
"id": "are_the_trackers_working",
@ -309,7 +309,11 @@
},
{
"id": "answer_which_trackers_do_you_recommend",
"translation": "If your torrent upload is denied because of trackers you'll need to add some of these:"
"translation": "We now have our own Tracker!, add it to the top of the list before uploading:"
},
{
"id": "other_trackers",
"translation": "But you should also add these, just in case something goes wrong"
},
{
"id": "how_can_i_help",
@ -341,7 +345,7 @@
},
{
"id": "uploading_file_prefills_fields",
"translation": "Uploading a torrent file allows pre-filling some fields, this is recommended."
"translation": "Uploading a torrent file allows pre-filling some fields. This is recommended."
},
{
"id": "magnet_link",
@ -543,6 +547,10 @@
"id": "moderator",
"translation": "Moderator"
},
{
"id":" api_token",
"translation": "API Token"
},
{
"id": "save_changes",
"translation": "Save Changes"
@ -573,7 +581,7 @@
},
{
"id": "renchon_anon_explanation",
"translation": "れんちょん is the username assigned to uploads and comments made anonymously. It is also used for torrent imported from the original nyaa, though the original uploader may be displayed alongside."
"translation": "れんちょん (Ren-chon) is the username assigned to uploads and comments made anonymously. It is also used for torrents imported from the original nyaa, though sometimes the original uploader can be displayed alongside."
},
{
"id": "mark_as_remake",
@ -626,5 +634,9 @@
{
"id": "language_name",
"translation": "English"
},
{
"id": "delete",
"translation": "Delete"
}
]

Voir le fichier

@ -1,7 +1,7 @@
[
{
"id": "link",
"translation": "link"
"translation": "enlace"
},
{
"id": "verify_email_title",
@ -9,7 +9,7 @@
},
{
"id": "verify_email_content",
"translation": "Por favor, da click en el siguiente link para verificar tu correo electrónico."
"translation": "Por favor, da click en el siguiente enlace para verificar tu correo electrónico."
},
{
"id": "reset_password_title",
@ -17,7 +17,7 @@
},
{
"id": "reset_password_content",
"translation": "Por favor, da click en el siguiente link para reestablecer tu contraseña."
"translation": "Por favor, da click en el siguiente enlace para reestablecer tu contraseña."
},
{
"id":"register_title",
@ -25,7 +25,7 @@
},
{
"id":"signup_box_title",
"translation": "Por favor registrate. <small>Es gratis y siempre lo será.</small>"
"translation": "Por favor, regístrate. <small>Es gratis y siempre lo será.</small>"
},
{
"id":"username",
@ -53,7 +53,7 @@
},
{
"id":"terms_conditions_confirm",
"translation": "Al dar click en <strong class=\"label label-primary\">Registrate</strong>, aceptas los <a href=\"#\" data-toggle=\"modal\" data-target=\"#t_and_c_m\">Terminos and Condiciones</a> establecidos por este sitio, incluyendo nuestro uso de Cookies."
"translation": "Al dar click en <strong class=\"label label-primary\">Registrarse</strong>, aceptas los <a href=\"#\" data-toggle=\"modal\" data-target=\"#t_and_c_m\">Términos y Condiciones</a> del sitio, incluyendo nuestro Uso de Cookies."
},
{
"id":"signin",
@ -61,7 +61,7 @@
},
{
"id":"register",
"translation": "Registrate"
"translation": "Registrarse"
},
{
"id":"terms_conditions",
@ -89,23 +89,27 @@
},
{
"id":"register_success_title",
"translation": "Registro exitoso"
"translation": "Registro Exitoso"
},
{
"id":"sign_up_success",
"translation": "Gracias por registrate!"
"translation": "¡Gracias por registrarte!"
},
{
"id":"verify_success",
"translation": "<i style=\"color:limegreen\" class=\"glyphicon glyphicon-ok-circle\"></i>Tu cuenta ha sido activada!"
"translation": "<i style=\"color:limegreen\" class=\"glyphicon glyphicon-ok-circle\"></i>¡Tu cuenta ha sido activada!"
},
{
"id":"signup_verification_email",
"translation": "Ahora, como paso final para registrarte, por favor revisa la bandeja de entrada de tu correo electrónico (o spam) y da click en el link provisto para activar tu cuenta!"
"translation": "Por último, revisa en tu bandeja de entrada (¡y en la carpeta de spam!) el correo de verificación."
},
{
"id":"signup_verification_noemail",
"translation": "Te has registrado exitosamente, ahora puedes usar tu cuenta."
},
{
"id":"settings",
"translation": "Ajustes"
"translation": "Ajustes de la Cuenta"
},
{
"id":"torrents",
@ -115,6 +119,18 @@
"id":"follow",
"translation": "Seguir"
},
{
"id":"unfollow",
"translation": "Dejar de Seguir"
},
{
"id":"user_followed_msg",
"translation": "¡Ahora sigues a %s!"
},
{
"id":"user_unfollowed_msg",
"translation": "¡Has dejado de seguir a %s!"
},
{
"id":"profile_page",
"translation": "Perfil de %s"
@ -141,7 +157,7 @@
},
{
"id": "links",
"translation": "Links"
"translation": "Enlaces"
},
{
"id": "home",
@ -181,7 +197,7 @@
},
{
"id": "no_torrents_uploaded",
"translation": "No hay torrents subidos aún!"
"translation": "¡No hay torrents subidos aún!"
},
{
"id": "profile",
@ -201,7 +217,7 @@
},
{
"id": "sign_up",
"translation": "Registrate"
"translation": "Registrarse"
},
{
"id": "no_results_found",
@ -209,7 +225,7 @@
},
{
"id": "notice_keep_seeding",
"translation": "AVISO: Sigue compartiendo (seeding) y habilita DHT."
"translation": "AVISO: Sigue compartiendo y habilita la red DHT"
},
{
"id": "official_nyaapocalipse_faq",
@ -217,7 +233,7 @@
},
{
"id": "links_replacement_mirror",
"translation": "Links al reemplazo/espejo"
"translation": "Enlaces al reemplazo/espejo"
},
{
"id": "what_happened",
@ -225,15 +241,15 @@
},
{
"id": "nyaa_se_went_offline",
"translation": "nyaa.se y los dominios asociados (como nyaatorrents.info) quedaron fuera de linea el 1ro de Mayo del 2017."
"translation": "nyaa.se y los dominios relacionados (como nyaatorrents.info) quedaron fuera de línea el 1ro de Mayo de 2017."
},
{
"id": "its_not_a_ddos",
"translation": "Fueron desactivados, asi que no fue un ataque DDos como lo es usualmente."
"translation": "Fueron desactivados, así que no fue un ataque DDoS como lo es usualmente."
},
{
"id": "future_not_looking_good",
"translation": "Las perspectivas de futuro para nyaa no se ven bien. (Está muerto)"
"translation": "Las posibilidades de que nyaa vuelva son desalentadoras. (Está muerto)"
},
{
"id": "recovery_effort",
@ -241,7 +257,7 @@
},
{
"id": "is_everything_lost",
"translation": "¿Está todo perdido?"
"translation": "¿Se ha perdido todo?"
},
{
"id": "in_short_no",
@ -249,7 +265,7 @@
},
{
"id": "are_some_things_lost",
"translation": "¿Hay cosas perdidas?"
"translation": "¿Hay algo que se haya perdido?"
},
{
"id": "answer_is_nyaa_db_lost",
@ -257,7 +273,7 @@
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "Sukebei, sin embargo, está en peor estado. Actualmente, solo tenemos bases de datos hasta el 2016, pero una nueva base de datos podría estar disponible para usar."
"translation": "Sukebei también está a salvo, casi nada se perdió."
},
{
"id": "how_are_we_recovering",
@ -265,7 +281,7 @@
},
{
"id": "answer_how_are_we_recovering",
"translation": "Las bases de datos mencionadas están hospedadas en nyaa.pantsu.cat y sukebei.pantsu.cat. Hay una función de busqueda, y (casi) la funcionalidad total de nyaa deberia estar pronto. Las estadisticas de Seeder/leecher son posibles mediante 'scraping' y podrían ser restauradas en un futuro, debido a que otras funcionalidades son prioridad por el momento."
"translation": "Las bases de datos mencionadas están hospedadas en nyaa.pantsu.cat y sukebei.pantsu.cat. Hay una función de busqueda, y la (casi) total funcionalidad de nyaa deberia estar lista pronto. Las estadisticas de Seeder/leecher son posibles mediante 'scraping' y podrían ser restauradas en un futuro, debido a que otras funcionalidades son prioridad por el momento."
},
{
"id": "are_the_trackers_working",
@ -273,7 +289,7 @@
},
{
"id": "answer_are_the_trackers_working",
"translation": "Aún si los trackers están caídos, los seeders aún están conectadosa la red DHT. Mientras el archivo esté listado en la red DHT, se compatirá como usualmente pasa."
"translation": "Aún si los trackers están caídos, los seeders aún están conectados a la red descentralizada DHT. Mientras el archivo esté listado en la red DHT, se compatirá como usualmente pasa."
},
{
"id": "how_do_i_download_the_torrents",
@ -281,11 +297,11 @@
},
{
"id": "answer_how_do_i_download_the_torrents",
"translation": " Sólo usa el <b>link magnet</b>. El link magnet será utilizado por tu cliente de BitTorrent para buscar el archivo en la red DHT y descargarlo."
"translation": "Solo usa el <b>enlace magnet</b>. El enlace magnet será usado por tu cliente de BitTorrent para buscar el archivo en la red DHT y debería descargarlo correctamente."
},
{
"id": "magnet_link_should_look_like",
"translation": "El link magnetico debería verse así:"
"translation": "El enlace magnet debería verse así:"
},
{
"id": "which_trackers_do_you_recommend",
@ -293,7 +309,7 @@
},
{
"id": "answer_which_trackers_do_you_recommend",
"translation": "Si tu carga de un torrent es denegada por culpa de los trackers, necesitarás agregar algunos de estos:"
"translation": "Si el torrent que subes es rechazado debido a los trackers, necesitarás añadir alguno de estos:"
},
{
"id": "how_can_i_help",
@ -301,15 +317,15 @@
},
{
"id": "answer_how_can_i_help",
"translation": "Si tienes expericia en desarrollo web, puedes unirte al canal IRC #nyaapantsu en irc.rizon.net. Si tienes bases de datos actuales, especialemente de sukebei, <b>SUBELAS</b>."
"translation": "Si tienes experiencia en desarrollo web, puedes unirte al canal IRC #nyaapantsu en irc.rizon.net. Si tienes bases de datos actuales, especialmente de sukebei, súbelas por favor."
},
{
"id": "your_design_sucks_found_a_bug",
"translation": "El diseño apesta / Encontré un bug"
"translation": "El diseño apesta / Encontré un error"
},
{
"id": "why_written_in_go",
"translation": "¿Porqué está escrito en Go?"
"translation": "¿Por qué está escrito en Go?"
},
{
"id": "authors_favorite_language",
@ -329,11 +345,15 @@
},
{
"id": "magnet_link",
"translation": "Link magnet"
"translation": "Enlace Magnet"
},
{
"id": "all_categories",
"translation": "Todas las categorías"
"translation": "Todas las Categorías"
},
{
"id": "select_a_torrent_category",
"translation": "Selecciona una Categoría para el Torrent"
},
{
"id": "anime",
@ -345,11 +365,11 @@
},
{
"id": "anime_english_translated",
"translation": "Anime - Traducción a Inglés"
"translation": "Anime - Traducido al Inglés"
},
{
"id": "anime_non_english_translated",
"translation": "Anime - Traducción diferente a Inglés"
"translation": "Anime - Traducido a un idioma distinto del Inglés"
},
{
"id": "anime_raw",
@ -361,11 +381,11 @@
},
{
"id": "audio_lossless",
"translation": "Audio - Lossless"
"translation": "Audio - Sin Pérdida"
},
{
"id": "audio_lossy",
"translation": "Audio - Lossy"
"translation": "Audio - Con Pérdida"
},
{
"id": "literature",
@ -373,7 +393,7 @@
},
{
"id": "literature_english_translated",
"translation": "Literatura - Traducción a Inglés"
"translation": "Literatura - Traducida al Inglés"
},
{
"id": "literature_raw",
@ -381,7 +401,7 @@
},
{
"id": "literature_non_english_translated",
"translation": "Literatura - Traducción diferente a Inglés"
"translation": "Literatura - Traducida a un idioma distinto del Inglés"
},
{
"id": "live_action",
@ -389,7 +409,7 @@
},
{
"id": "live_action_english_translated",
"translation": "Live Action - Traducción a Inglés"
"translation": "Live Action - Traducido al Inglés"
},
{
"id": "live_action_idol_pv",
@ -397,7 +417,7 @@
},
{
"id": "live_action_non_english_translated",
"translation": "Live Action - Traducción diferente a Inglés"
"translation": "Live Action - Traducido a un idioma distinto del Inglés"
},
{
"id": "live_action_raw",
@ -429,19 +449,19 @@
},
{
"id": "torrent_description",
"translation": "Description del Torrent"
"translation": "Descripción del Torrent"
},
{
"id": "limited_html_set_is_allowed_use",
"translation": "Un conjunto limitado de HTML se permite en la descripción, asegúrate de utilizarlo"
"id": "description_markdown_notice",
"translation": "Puedes usar Markdown en las descripciones."
},
{
"id": "show_all",
"translation": "Mostrar todos"
"translation": "Mostrar todo"
},
{
"id": "filter_remakes",
"translation": "Filtrar Remaked"
"translation": "Filtrar Remakes"
},
{
"id": "trusted",
@ -529,19 +549,79 @@
},
{
"id": "profile_updated",
"translation": "Tu perfil ha sido actualizado correctamente!"
"translation": "¡Tu perfil ha sido actualizado correctamente!"
},
{
"id": "delete_account",
"translation": "Eliminar cuenta"
"translation": "Eliminar Cuenta"
},
{
"id": "delete_account_confirm",
"translation": "¿Estás seguro de que desear eliminar esta cuenta?"
"translation": "¿Estás seguro de que deseas eliminar esta cuenta?"
},
{
"id": "delete_success",
"translation": "Se ha eliminado exitosamente la cuenta!"
"translation": "¡Esta cuenta ha sido eliminada exitosamente!"
},
{
"id": "moderation",
"translation": "Moderación"
},
{
"id": "who_is_renchon",
"translation": "¿Quién es れんちょん?"
},
{
"id": "renchon_anon_explanation",
"translation": "れんちょん es el nombre de usuario asignado a las subidas y comentarios hechos anónimamente. También es usado en los torrents importados desde el nyaa original, aunque es posible que se muestre junto al nombre de usuario original."
},
{
"id": "mark_as_remake",
"translation": "Marcar como remake"
},
{
"id": "email_changed",
"translation": "¡Se ha cambiado exitosamente la dirección de correo electrónico! Sin embargo, tendrás que confirmar el cambio dando click al enlace enviado a: %s"
},
{
"id": "torrent_status",
"translation": "Estado del Torrent"
},
{
"id": "torrent_status_hidden",
"translation": "Oculto"
},
{
"id": "torrent_status_normal",
"translation": "Normal"
},
{
"id": "torrent_status_remake",
"translation": "Remake"
},
{
"id": "profile_edit_page",
"translation": "Editar perfil de %s"
},
{
"id":"date_format",
"translation": "2006-01-02 15:04"
},
{
"id": "seeders",
"translation": "Seeders"
},
{
"id": "leechers",
"translation": "Leechers"
},
{
"id": "completed",
"translation": "Completado"
},
{
"id": "change_language",
"translation": "Cambiar Idioma"
},
{
"id": "language_name",

Voir le fichier

@ -229,7 +229,7 @@
},
{
"id": "nyaa_se_went_offline",
"translation": "nyaa.se és a vele kapcsolatos domain-ek (pl. nyaatorrents.info) elérhetetlenné vált 2017. Május 1-én."
"translation": "nyaa.se és a vele kapcsolatos domain-ek (pl. nyaatorrents.info) elérhetetlenné váltak 2017. május 1-én."
},
{
"id": "its_not_a_ddos",
@ -547,6 +547,66 @@
"id": "delete_success",
"translation": "A fiók törlésre került."
},
{
"id": "moderation",
"translation": "Moderáció"
},
{
"id": "who_is_renchon",
"translation": "Ki a fasz az a れんちょん?"
},
{
"id": "renchon_anon_explanation",
"translation": "れんちょん nevet az kapja, aki névtelenül ír kommentet vagy tölt fel valamit. Ezt a nevet kapják az eredeti nyaa-ról átimportált torrentek is, viszont az eredeti feltöltő neve megjelenhet mellette."
},
{
"id": "mark_as_remake",
"translation": "Remake-ként jelölés"
},
{
"id": "email_changed",
"translation": "Az email megváltoztatása sikeres volt, viszont még meg kell erősítened a(z) %s címre küldött linkkel."
},
{
"id": "torrent_status",
"translation": "Torrent állapota"
},
{
"id": "torrent_status_hidden",
"translation": "Eltüntetett"
},
{
"id": "torrent_status_normal",
"translation": "Normál"
},
{
"id": "torrent_status_remake",
"translation": "Remake"
},
{
"id": "profile_edit_page",
"translation": "%s profiljának szerkesztése."
},
{
"id":"date_format",
"translation": "2006-01-02 15:04"
},
{
"id": "seeders",
"translation": "Seederek"
},
{
"id": "leechers",
"translation": "Leecherek"
},
{
"id": "completed",
"translation": "Befejezett"
},
{
"id": "change_language",
"translation": "Nyelv megváltoztatása"
},
{
"id": "language_name",
"translation": "Magyar"

Voir le fichier

@ -101,15 +101,15 @@
},
{
"id":"signup_verification_email",
"translation": "Per concludere, controlla la tua casella email (e la cartella spam!) per l'email di verifica."
"translation": "Per concludere controlla la tua casella email (e la cartella spam!) per l'email di verifica."
},
{
"id":"signup_verification_noemail",
"translation": "La registrazione è avvenuta con successo, adesso puoi usare il tuo account."
"translation": "La registrazione è avvenuta con successo. Ora puoi usare il tuo account."
},
{
"id":"settings",
"translation": "Settaggi Account"
"translation": "Impostazioni Account"
},
{
"id":"torrents",
@ -173,7 +173,7 @@
},
{
"id": "upload",
"translation": "Carica"
"translation": "Upload"
},
{
"id": "faq",
@ -209,7 +209,7 @@
},
{
"id": "member",
"translation": "Membro"
"translation": "Utente"
},
{
"id": "sign_in",
@ -233,7 +233,7 @@
},
{
"id": "links_replacement_mirror",
"translation": "Collegamenti di sostituzione/mirror"
"translation": "Mirror"
},
{
"id": "what_happened",
@ -273,7 +273,7 @@
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "Anche Sukebei è salvo, quasi niente è andato perso."
"translation": "Anche sukebei è al sicuro, e quasi nulla è andato perso."
},
{
"id": "how_are_we_recovering",
@ -281,7 +281,7 @@
},
{
"id": "answer_how_are_we_recovering",
"translation": "I database citati sono attualmente hostati su nyaa.pantsu.cat e sukebei.pantsu.cat. C'è una funzione di ricerca, e (almost) full nyaa functionality dovrebbe arrivare presto. Le statistiche dei Seeder/leecher sono possibili attraverso scraping e potrebbero essere ripristinate in futuro, visto che altre funzionalità hanno la precedenza adesso."
"translation": "I database citati sono attualmente hostati su nyaa.pantsu.cat e sukebei.pantsu.cat. C'è una funzione di ricerca, e (quasi) tutte le altre dovrebbero arrivare presto. Le statistiche dei Seeder/leecher sono possibili attraverso scraping e potrebbero essere ripristinate in futuro, visto che altre funzionalità hanno la precedenza adesso."
},
{
"id": "are_the_trackers_working",
@ -309,7 +309,7 @@
},
{
"id": "answer_which_trackers_do_you_recommend",
"translation": "Se il caricamente del tuo torrent è negato a causa dei trackers, dovrai aggiungere alcuni di questi:"
"translation": "Se il caricamento del tuo torrent è negato a causa dei trackers, dovrai aggiungere alcuni di questi:"
},
{
"id": "how_can_i_help",
@ -341,7 +341,7 @@
},
{
"id": "uploading_file_prefills_fields",
"translation": "Caricare un file torrent permette di auto-riempire alcuni campi, è raccomandato farlo."
"translation": "Caricare un file torrent permette di auto-riempire alcuni campi. È raccomandato farlo."
},
{
"id": "magnet_link",
@ -393,7 +393,7 @@
},
{
"id": "literature_english_translated",
"translation": "Letteratura - Tradotti in Inglese"
"translation": "Letteratura - Tradotta in Inglese"
},
{
"id": "literature_raw",
@ -401,7 +401,7 @@
},
{
"id": "literature_non_english_translated",
"translation": "Letteratura - Tradotti in altre lingue"
"translation": "Letteratura - Tradotta in altre lingue"
},
{
"id": "live_action",
@ -413,7 +413,7 @@
},
{
"id": "live_action_idol_pv",
"translation": "Live Action - Idol/Video Promozionali"
"translation": "Live Action - Idol/Videoclip"
},
{
"id": "live_action_non_english_translated",
@ -485,7 +485,7 @@
},
{
"id": "search",
"translation": "Ricerca"
"translation": "Cerca"
},
{
"id": "hash",

Voir le fichier

@ -9,7 +9,7 @@
},
{
"id": "verify_email_content",
"translation": "あなたのメールアドレスを認証するには、以下のリンクをクリックします。"
"translation": "お使いのメールアドレスを認証するには、以下のリンクをクリックします。"
},
{
"id": "reset_password_title",
@ -25,7 +25,7 @@
},
{
"id":"signup_box_title",
"translation": "登録 <small>ずっと無料です</small>"
"translation": "登録 <small>永久無料です</small>"
},
{
"id":"username",
@ -53,7 +53,7 @@
},
{
"id":"terms_conditions_confirm",
"translation": "<strong class=\"label label-primary\">登録</strong> をクリックすることにより、Cookie の使用を含む、本サイトの <a href=\"#\" data-toggle=\"modal\" data-target=\"#t_and_c_m\">利用規約</a> に同意したものとみなします。"
"translation": "<strong class=\"label label-primary\">登録</strong> をクリックすることにより、クッキーの使用を含む、本サイトの <a href=\"#\" data-toggle=\"modal\" data-target=\"#t_and_c_m\">利用規約</a> に同意したものとみなします。"
},
{
"id":"signin",
@ -97,19 +97,19 @@
},
{
"id":"verify_success",
"translation": "<i style=\"color:limegreen\" class=\"glyphicon glyphicon-ok-circle\"></i>アカウントは有効になっています。"
"translation": "<i style=\"color:limegreen\" class=\"glyphicon glyphicon-ok-circle\"></i>お使いのアカウントは有効になりました。"
},
{
"id":"signup_verification_email",
"translation": "認証メールを送信しました。メールボックスを確認してください。迷惑メールフォルダーの確認もお忘れずに。"
"translation": "認証メールを送信しました。メールボックスを確認してください。迷惑メールフォルダーの確認もお忘れなく。"
},
{
"id":"signup_verification_noemail",
"translation": "認証が完了しました。今からこのアカウントを使うことができます。"
"translation": "認証が完了しました。今からこのアカウントを使えるようになります。"
},
{
"id":"settings",
"translation": "アカウント設定"
"translation": "アカウント設定"
},
{
"id":"torrents",
@ -119,13 +119,25 @@
"id":"follow",
"translation": "フォロー"
},
{
"id":"unfollow",
"translation": "フォロー解除"
},
{
"id":"user_followed_msg",
"translation": "%s さんをフォローしました。"
},
{
"id":"user_unfollowed_msg",
"translation": "%s さんのフォローを解除しました。"
},
{
"id":"profile_page",
"translation": "%s プロフィールページ"
"translation": "%s さんのプロフィールページ"
},
{
"id":"see_more_torrents_from",
"translation": "%s の Torrent をもっと表示"
"translation": "%s さんの Torrent をもっと表示"
},
{
"id":"category",
@ -177,7 +189,7 @@
},
{
"id": "nothing_here",
"translation": "何もありません"
"translation": "ここには何もありません"
},
{
"id": "404_not_found",
@ -213,11 +225,11 @@
},
{
"id": "notice_keep_seeding",
"translation": "お願い: DHT 機能を有効にし、なるべくシードを継続してください"
"translation": "お知らせ: DHT 機能を有効にし、なるべくシードを継続してください"
},
{
"id": "official_nyaapocalipse_faq",
"translation": "公式 nyaa <ruby>黙示録<rp>(</rp><rt>アポカリプス</rt><rp>)</rp></ruby> のよくある質問"
"translation": "公式 nyaa 最期についてよくある質問"
},
{
"id": "links_replacement_mirror",
@ -229,11 +241,11 @@
},
{
"id": "nyaa_se_went_offline",
"translation": "nyaa.se とその関連ドメイン (nyaatorrents.infoなど) は 2017年5月1日 にオフラインになった。"
"translation": "nyaa.se 並びにその関連ドメイン (nyaatorrents.info など) は 2017年5月1日 にオフラインになった。"
},
{
"id": "its_not_a_ddos",
"translation": "いつものような DDoS 攻撃ではなく、利用停止になった。"
"translation": "いつものような DDoS 攻撃ではなく、サイト自体が利用停止になった。"
},
{
"id": "future_not_looking_good",
@ -245,11 +257,11 @@
},
{
"id": "is_everything_lost",
"translation": "nyaa.se のすべてが失われてしまったの?"
"translation": "すべてが失われてしまったの?"
},
{
"id": "in_short_no",
"translation": "いいえ。"
"translation": "一言で言えば、NO です。"
},
{
"id": "are_some_things_lost",
@ -257,19 +269,19 @@
},
{
"id": "answer_is_nyaa_db_lost",
"translation": "<s>4月5日</s> 5月1日までの nyaa.se のデータベースはあります。要するに、ほとんど失われていません。"
"translation": "<s>4月5日</s> 5月1日までのデータベースはあります。要するに、ほとんど失われていません。"
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "ただし sukebei は別です。sukebei に至っては 2016年までのデータベースしか手元にありません。ただ、今後新しいデータベースが使える可能性はあります。"
"translation": "sukebei の方も問題なく、ほとんど失われていません。"
},
{
"id": "how_are_we_recovering",
"translation": "どのように復旧させるの?"
"translation": "どのように復旧してるの?"
},
{
"id": "answer_how_are_we_recovering",
"translation": "上述のデータベースは nyaa.pantsu.cat と sukebei.pantsu.cat にホストされています。検索機能はすでにあり、近いうちに nyaa.se にあったほぼすべての機能が利用可能になるでしょう。また、Seeder / Leecher 統計はスクレイピングによって収集可能ですが、今は他に優先すべきことがあるため後回しにされます。"
"translation": "上述のデータベースは nyaa.pantsu.cat と sukebei.pantsu.cat にホストされています。検索機能はすでにあり、近いうちに nyaa.se にあったほぼすべての機能が利用可能になるでしょう。"
},
{
"id": "are_the_trackers_working",
@ -305,7 +317,7 @@
},
{
"id": "answer_how_can_i_help",
"translation": "ウェブ開発の経験があるのであれば、irc.rizon.net の #nyaapantsu IRC チャンネルに参加することができますよ。現在のデータベースがあるのなら、特に sukebei の方ですが、<b>ぜひともアップロードしていただきたいです。</b>"
"translation": "ウェブ開発の専門知識がおありでしたら、irc.rizon.net の #nyaapantsu IRC チャンネルに参加できますよ。また、現在のデータベースをお持ちの場合、特に sukebei の場合は、<b>ぜひともアップロードしていただきたいです。</b>"
},
{
"id": "your_design_sucks_found_a_bug",
@ -339,6 +351,10 @@
"id": "all_categories",
"translation": "すべてのカテゴリー"
},
{
"id": "select_a_torrent_category",
"translation": "Torrent カテゴリーを選択"
},
{
"id": "anime",
"translation": "アニメ"
@ -353,7 +369,7 @@
},
{
"id": "anime_non_english_translated",
"translation": "アニメ 英訳"
"translation": "アニメ 英訳"
},
{
"id": "anime_raw",
@ -365,27 +381,27 @@
},
{
"id": "audio_lossless",
"translation": "オーディオ 可逆圧縮方式 (flacなど)"
"translation": "オーディオ 可逆圧縮方式 (FLAC など)"
},
{
"id": "audio_lossy",
"translation": "オーディオ 非可逆圧縮方式 (mp3など)"
"translation": "オーディオ 非可逆圧縮方式 (MP3 など)"
},
{
"id": "literature",
"translation": ""
"translation": "書籍"
},
{
"id": "literature_english_translated",
"translation": " 英訳済み"
"translation": "書籍 英訳済み"
},
{
"id": "literature_raw",
"translation": " RAW"
"translation": "書籍 RAW"
},
{
"id": "literature_non_english_translated",
"translation": "英訳"
"translation": "書籍 英訳"
},
{
"id": "live_action",
@ -401,7 +417,7 @@
},
{
"id": "live_action_non_english_translated",
"translation": "実写 英訳"
"translation": "実写 英訳"
},
{
"id": "live_action_raw",
@ -437,7 +453,7 @@
},
{
"id": "description_markdown_notice",
"translation": "説明文には MarkDown 記法が使えます。"
"translation": "説明文には MarkDown 記法を使うことができます。"
},
{
"id": "show_all",
@ -445,7 +461,7 @@
},
{
"id": "filter_remakes",
"translation": "再構成されたフィルター"
"translation": "再構成フィルター"
},
{
"id": "trusted",
@ -457,7 +473,7 @@
},
{
"id": "downloads",
"translation": "ダウンロード"
"translation": "ダウンロード"
},
{
"id": "descending",
@ -527,6 +543,10 @@
"id": "moderator",
"translation": "モデレーター"
},
{
"id":" api_token",
"translation": "API トークン"
},
{
"id": "save_changes",
"translation": "変更を保存"
@ -549,7 +569,7 @@
},
{
"id": "moderation",
"translation": "緩和"
"translation": "節制"
},
{
"id": "who_is_renchon",
@ -592,9 +612,6 @@
"translation": "2006/01/02 15:04"
},
{
"id": "language_name",
"translation": "日本語"
},
"id": "seeders",
"translation": "Seeder 数"
},
@ -605,5 +622,17 @@
{
"id": "completed",
"translation": "完了数"
},
{
"id": "change_language",
"translation": "言語の変更"
},
{
"id": "language_name",
"translation": "日本語"
},
{
"id": "delete",
"translation": "削除"
}
]

Voir le fichier

@ -309,7 +309,11 @@
},
{
"id": "answer_which_trackers_do_you_recommend",
"translation": "Se o seu torrent for negado por conta de trackers, você vai precisar adicionar alguns desses:"
"translation": "Agora nós possuímos nosso próprio Tracker, adicione-o ao topo da lista antes de enviar um torrent:"
},
{
"id": "other_trackers",
"translation": "Mas você também deve adicionar estes, para caso algo dê errado."
},
{
"id": "how_can_i_help",
@ -569,7 +573,7 @@
},
{
"id": "renchon_anon_explanation",
"translation": "れんちょん é o nome atribuído a uploads e comentários feitos anonimamente. Também é usado para torrents importados do nyaa original, embora o uploader original seja mostrado ao lado."
"translation": "れんちょん (Ren-chon) é o nome atribuído a uploads e comentários feitos anonimamente. Também é usado para torrents importados do nyaa original, embora o uploader original seja mostrado ao lado."
},
{
"id": "mark_as_remake",

Voir le fichier

@ -229,11 +229,11 @@
},
{
"id": "official_nyaapocalipse_faq",
"translation": "ถาม-ตอบ Nyaapocalypse อย่างเป็นทางการ"
"translation": "ถาม-ตอบสถานการณ์ล่าสุดของ Nyaa อย่างเป็นทางการ"
},
{
"id": "links_replacement_mirror",
"translation": "ลิงค์สำหรับเว็บที่ใช้แทน/Mirror"
"translation": "ลิงค์สำหรับเว็บที่ใช้ทดแทน/Mirror"
},
{
"id": "what_happened",
@ -249,7 +249,7 @@
},
{
"id": "future_not_looking_good",
"translation": "และอนาคตข้างหน้าสำหรับ Nyaa ดูจะไม่ค่อยดีเท่าไหร่ (ตายสนิท)"
"translation": "และอนาคตสำหรับ Nyaa เองก็ไม่ค่อยดีเท่าไหร่ (ตายสนิท)"
},
{
"id": "recovery_effort",
@ -269,11 +269,11 @@
},
{
"id": "answer_is_nyaa_db_lost",
"translation": "เรามีข้อมูลฐานข้อมูลทอร์เรนท์ที่อยู่บน Nyaa ถึง <s>5 เมษายน</s> 1 พฤษภาคม นั่นหมายความว่าไม่มีอะไรหาย"
"translation": "เรามีข้อมูลฐานข้อมูลทอร์เรนท์ที่อยู่บน Nyaa ถึง <s>5 เมษายน</s> 1 พฤษภาคม นั่นหมายความว่าแทบจะไม่มีอะไรหาย"
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "สำหรับ Sukebei นั่นโชคร้ายหน่อย ที่ในตอนนี้เรามีฐานข้อมูลของ Sukebei ถึงแค่ช่วงปี 2016 แต่อาจจะมีฐานข้อมูลที่ใหม่กว่านี้ให้ใช้ก็ได้"
"translation": "ที่ Sukebei เองก็แทบจะไม่มีอะไรหายเหมือนกัน"
},
{
"id": "how_are_we_recovering",
@ -281,7 +281,7 @@
},
{
"id": "answer_how_are_we_recovering",
"translation": "ฐานข้อมูลดังกล่าวถูกใช้งานกับ nyaa.pantsu.cat และ sukebei.pantsu.cat ซึ่งมีระบบค้นหา และระบบของ Nyaa (เกือบ) ครบถ้วนควรจะมาในเร็วๆ นี้ สถิติผู้ปล่อย/ผู้โหลดที่อาจจะกู้กลับมาผ่านการ Scraping ในอนาคตอันใกล้ตั้งแต่ฟีเจอร์อื่นๆ ถูกให้ความสำคัญมากกว่าในตอนนี้"
"translation": "ฐานข้อมูลดังกล่าวถูกใช้งานกับ nyaa.pantsu.cat และ sukebei.pantsu.cat ซึ่งมีระบบค้นหา และระบบของ Nyaa (เกือบ) ครบถ้วนควรจะมาในเร็วๆ นี้"
},
{
"id": "are_the_trackers_working",
@ -309,7 +309,11 @@
},
{
"id": "answer_which_trackers_do_you_recommend",
"translation": "ถ้าทอร์เรนท์ของคุณอัพโหลดไม่ได้เพราะแทรคเกอร์คุณควรใส่ตามนี้:"
"translation": "เรามีแทรคเกอร์เป็นของตัวเองแล้ว! กรุณาใส่ไว้บนสุดของลิสต์ก่อนอัพโหลด:"
},
{
"id": "other_trackers",
"translation": "แต่คุณควรใส่ตามนี้ลงไปด้วยเผื่อมันมีอันเป็นไป"
},
{
"id": "how_can_i_help",
@ -317,11 +321,11 @@
},
{
"id": "answer_how_can_i_help",
"translation": "หากมีประสบการณ์ในการเขียนเว็บ คุณสามารถเข้ามาคุยในช่อง IRC #nyaapantsu บน irc.rizon.net หากคุณมีฐานข้อมูลใดๆ โดยเฉพาะของ Sukebei <b>กรุณาอัพโหลด</b>"
"translation": "หากมีประสบการณ์ในการเขียนเว็บ คุณสามารถเข้ามาคุยในช่อง IRC #nyaapantsu บน irc.rizon.net หากคุณมีฐานข้อมูลใดๆ โดยเฉพาะของ Sukebei กรุณาอัพโหลด"
},
{
"id": "your_design_sucks_found_a_bug",
"translation": "ดีไซน์กากว่ะ / เจอบัค"
"translation": "ดีไซน์กากว่ะคุณ / เจอบัค"
},
{
"id": "why_written_in_go",
@ -329,7 +333,7 @@
},
{
"id": "authors_favorite_language",
"translation": "เพราะเป็นภาษาถนัดของผู้พัฒนา"
"translation": "เพราะเป็นภาษาที่ผู้พัฒนาชอบ"
},
{
"id": "upload_magnet",
@ -350,6 +354,10 @@
{
"id": "all_categories",
"translation": "ทุกหมวดหมู่"
},
{
"id": "select_a_torrent_category",
"translation": "เลือกหมวดหมู่ทอร์เรนท์"
},
{
"id": "anime",
@ -457,7 +465,7 @@
},
{
"id": "filter_remakes",
"translation": "Filter Remakes"
"translation": "กรองงานทำซ้ำออกไป"
},
{
"id": "trusted",
@ -473,11 +481,11 @@
},
{
"id": "descending",
"translation": "Descending"
"translation": "เรียงจากบนลงล่าง"
},
{
"id": "ascending",
"translation": "Ascending"
"translation": "เรียงจากล่างขึ้นบน"
},
{
"id": "search",
@ -569,7 +577,7 @@
},
{
"id": "renchon_anon_explanation",
"translation": "れんちょん คือชื่อผู้ใช้ที่ตั้งไว้สำหรับอัพโหลดและคอมเมนท์แบบไม่ระบุตัวตน ซึ่งถูกใช้กับทอร์เรนท์ที่กู้มาจาก Nyaa ดั้งเดิม และชื่อผู้อัพโหลดดั้งเดิมควรจะแสดงผลคู่กัน"
"translation": "れんちょん คือชื่อผู้ใช้ที่ตั้งไว้สำหรับอัพโหลดและคอมเมนท์แบบไม่ระบุตัวตน ซึ่งถูกใช้กับทอร์เรนท์ที่กู้มาจาก Nyaa ดั้งเดิม และในบางครั้งชื่อผู้อัพโหลดดั้งเดิมจะแสดงผลคู่กัน"
},
{
"id": "mark_as_remake",
@ -615,8 +623,16 @@
"id": "completed",
"translation": "โหลดเสร็จแล้ว"
},
{
"id": "change_language",
"translation": "เปลี่ยนภาษา"
},
{
"id": "language_name",
"translation": "ไทย"
"translation": "ภาษาไทย"
},
{
"id": "delete",
"translation": "ลบ"
}
]

Voir le fichier

@ -113,7 +113,7 @@
},
{
"id":"torrents",
"translation": "种子"
"translation": "种子列表"
},
{
"id":"follow",
@ -181,7 +181,7 @@
},
{
"id": "fap",
"translation": "秘花园"
"translation": "花园"
},
{
"id": "advanced_search",
@ -237,7 +237,7 @@
},
{
"id": "what_happened",
"translation": "什么鬼"
"translation": "Nyaa站怎么了"
},
{
"id": "nyaa_se_went_offline",
@ -245,15 +245,15 @@
},
{
"id": "its_not_a_ddos",
"translation": "站点完全被停用,并不是以往的 DDoS 攻击"
"translation": "这次并不是因为以往的 DDoS 攻击,而是站点完全被停用了"
},
{
"id": "future_not_looking_good",
"translation": "nyaa 的未来非常的严峻 她真的走了"
"translation": "Nyaa 的未来非常的严峻,她永远的走了"
},
{
"id": "recovery_effort",
"translation": "而资料的抢救也正在进行中"
"translation": "我们正在进行对资料的抢救"
},
{
"id": "is_everything_lost",
@ -273,7 +273,7 @@
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "然而 sukebei 就没那么幸运了。种子库只备份到 2016年但也许能找到更新的版本"
"translation": "而 sukebei 也一样,几乎没有什么损失"
},
{
"id": "how_are_we_recovering",
@ -281,11 +281,11 @@
},
{
"id": "answer_how_are_we_recovering",
"translation": "前面所说的资料都存放在 nyaa.pantsu.cat 和 sukebei.pantsu.cat 的服务器上。 我们也会尽力恢复旧 nyaa 站的全部功能。 而在未来的某一天,种子和链接数的统计资料也可能通过抓取其他 tracker 來恢复!不过先要把眼前的工作完成再说。"
"translation": "前面所说的资料都存放在 nyaa.pantsu.cat 和 sukebei.pantsu.cat 的服务器上。 我们也会尽快恢复旧 nyaa 站的全部功能。"
},
{
"id": "are_the_trackers_working",
"translation": "这些种子都还活着吗?"
"translation": "那..这些种子都还活着吗?"
},
{
"id": "answer_are_the_trackers_working",
@ -293,7 +293,7 @@
},
{
"id": "how_do_i_download_the_torrents",
"translation": "所以要怎么抓种?"
"translation": "所以要怎么抓种?"
},
{
"id": "answer_how_do_i_download_the_torrents",
@ -309,7 +309,11 @@
},
{
"id": "answer_which_trackers_do_you_recommend",
"translation": "如果您上传种子被拒绝,原因是 tracker 不符合要求,那有可能需要加上这几个 tracker"
"translation": "现在我们有自己的 tracker 啦!在上传之前把他们放在 tracker 列表的顶部吧!"
},
{
"id": "other_trackers",
"translation": "当然,你也可以使用以下的 tracker ,不过某些情况下可能会出现错误。"
},
{
"id": "how_can_i_help",
@ -321,20 +325,16 @@
},
{
"id": "your_design_sucks_found_a_bug",
"translation": "你们设计的破网站 / 我找到 bug 了!"
"translation": "你们设计的什么破网站 / 我找到 bug 了!"
},
{
"id": "why_written_in_go",
"translation": "为啥要用 GO 语言?没有别的选择了吗"
"translation": "为什么非要用辣鸡 GO 语言"
},
{
"id": "authors_favorite_language",
"translation": "这是作者的信仰!"
},
{
"id": "nyaa_pantsu_dont_host_files",
"translation": "nyaa.pantsu.cat 以及 sukebei.pantsu.cat 沒有保存任何资料喵~"
},
{
"id": "upload_magnet",
"translation": "上传磁力链接"
@ -355,25 +355,29 @@
"id": "all_categories",
"translation": "所有分类"
},
{
"id": "select_a_torrent_category",
"translation": "请选择种子分类"
},
{
"id": "anime",
"translation": "动画"
"translation": "动"
},
{
"id": "anime_amv",
"translation": "动 - 动画MV"
"translation": "动 - 动画MV"
},
{
"id": "anime_english_translated",
"translation": "动 - 已翻译为英文"
"translation": "动 - 已翻译为英文"
},
{
"id": "anime_non_english_translated",
"translation": "动 - 未翻译为英文"
"translation": "动 - 未翻译为英文"
},
{
"id": "anime_raw",
"translation": "动 - 生肉"
"translation": "动 - 生肉"
},
{
"id": "audio",
@ -461,11 +465,11 @@
},
{
"id": "filter_remakes",
"translation": "过滤 Remakes"
"translation": "过滤再发行版"
},
{
"id": "trusted",
"translation": "信任的"
"translation": "信任的资源"
},
{
"id": "id",
@ -562,5 +566,73 @@
{
"id": "delete_success",
"translation": "您的账号已被删除!"
},
{
"id": "moderation",
"translation": "节制"
},
{
"id": "who_is_renchon",
"translation": "れんちょん是谁?"
},
{
"id": "renchon_anon_explanation",
"translation": "れんちょん是匿名评论与上传者的默认用户名同时也显示为Nyaa站原始资源的上传者."
},
{
"id": "mark_as_remake",
"translation": "标为再发行版(重制版)"
},
{
"id": "email_changed",
"translation": "邮箱更变成功喵~一份确认邮件已发送到 %s ,请点击里面的确认链接来完成修改!"
},
{
"id": "torrent_status",
"translation": "种子状态"
},
{
"id": "torrent_status_hidden",
"translation": "隐藏"
},
{
"id": "torrent_status_normal",
"translation": "普通"
},
{
"id": "torrent_status_remake",
"translation": "再发行"
},
{
"id": "profile_edit_page",
"translation": "编辑 %s 的个人资料"
},
{
"id":"date_format",
"translation": "2006-01-02 15:04"
},
{
"id": "seeders",
"translation": "上传数"
},
{
"id": "leechers",
"translation": "下载数"
},
{
"id": "completed",
"translation": "完成数"
},
{
"id": "change_language",
"translation": "切换语言"
},
{
"id": "language_name",
"translation": "简体中文"
},
{
"id": "delete",
"translation": "删除"
}
]
]

Voir le fichier

@ -13,15 +13,15 @@
},
{
"id": "reset_password_title",
"translation": "喵 重密碼"
"translation": "喵 重密碼"
},
{
"id": "reset_password_content",
"translation": "請按下面連結重密碼"
"translation": "請按下面連結重密碼"
},
{
"id":"register_title",
"translation": "建新帳號"
"translation": "新帳號"
},
{
"id":"signup_box_title",
@ -161,7 +161,7 @@
},
{
"id": "home",
"translation": "頁"
"translation": "頁"
},
{
"id": "error_404",
@ -169,7 +169,7 @@
},
{
"id": "toggle_navigation",
"translation": "切換導列"
"translation": "切換導列"
},
{
"id": "upload",
@ -177,11 +177,11 @@
},
{
"id": "faq",
"translation": "有問有答"
"translation": "常見問題"
},
{
"id": "fap",
"translation": "不尻嗎"
"translation": "尻尻"
},
{
"id": "advanced_search",
@ -221,7 +221,7 @@
},
{
"id": "no_results_found",
"translation": "沒有結果喔"
"translation": "找不到結果"
},
{
"id": "notice_keep_seeding",
@ -249,7 +249,7 @@
},
{
"id": "future_not_looking_good",
"translation": "nyaa 的未來並不明朗(她真的離開了)"
"translation": "nyaa 的未來並不樂觀(她真的離開了)"
},
{
"id": "recovery_effort",
@ -265,15 +265,15 @@
},
{
"id": "are_some_things_lost",
"translation": "那有一部分的東西消失了嗎?"
"translation": "那有東西消失了嗎?"
},
{
"id": "answer_is_nyaa_db_lost",
"translation": "我們有 nyaa 種子資料庫直到 <s>4月5日</s> 5月1日這表示幾乎沒失去任何東西"
"translation": "我們有直到 <s>4月5日</s> 5月1日的 nyaa 種子資料庫,這表示幾乎沒失去任何東西"
},
{
"id": "answer_is_sukebei_db_lost",
"translation": "然而 sukebei 的命運比較坎坷。目前資料庫只有備份到 2016年但也許有個新的日期版本可供使用"
"translation": "sukebei 還活著,也幾乎沒有雷到。然而 sukebei 的命運比較坎坷。目前資料庫只有備份到 2016年但也許有個新的日期版本可供使用"
},
{
"id": "how_are_we_recovering",
@ -309,7 +309,11 @@
},
{
"id": "answer_which_trackers_do_you_recommend",
"translation": "如果您上傳種子遭到拒絕,原因是 tracker 不符要求,那有可能需要加上這其中幾個 tracker"
"translation": "我們現在有自己的 Tracker 啦!如果您上傳種子遭到拒絕,原因是 tracker 不符要求,那有可能需要加上這其中幾個 tracker"
},
{
"id": "other_trackers",
"translation": "不過你最好再加上這些,免得雷到。"
},
{
"id": "how_can_i_help",
@ -325,11 +329,11 @@
},
{
"id": "why_written_in_go",
"translation": "爲啥要用 GO 寫啊?沒其他更好的程式語言"
"translation": "你的狗屎爛蛋為什麼用Go寫"
},
{
"id": "authors_favorite_language",
"translation": "這是作者的愛啊啊啊啊"
"translation": "作者尬意"
},
{
"id": "upload_magnet",
@ -351,6 +355,10 @@
"id": "all_categories",
"translation": "所有分類"
},
{
"id": "select_a_torrent_category",
"translation": "選擇種子分類"
},
{
"id": "anime",
"translation": "動畫"
@ -449,7 +457,7 @@
},
{
"id": "description_markdown_notice",
"translation": "描述可使用 Markdown"
"translation": "描述可使用 Markdown 語法"
},
{
"id": "show_all",
@ -539,6 +547,10 @@
"id": "moderator",
"translation": "管理員"
},
{
"id":" api_token",
"translation": "API Token"
},
{
"id": "save_changes",
"translation": "儲存變更"
@ -558,5 +570,73 @@
{
"id": "delete_success",
"translation": "您的帳號已經成功刪除!"
},
{
"id": "moderation",
"translation": "節制"
},
{
"id": "who_is_renchon",
"translation": "誰是れんちょん?"
},
{
"id": "renchon_anon_explanation",
"translation": "れんちょん是匿名評論與上傳者的默認用戶名同時也顯示為Nyaa站原始資源的上傳者。"
},
{
"id": "mark_as_remake",
"translation": "設為Remake"
},
{
"id": "email_changed",
"translation": "電子郵件變更成功,已發送確認郵件到 %s ,請按下裡面的確認連結來完成修改!"
},
{
"id": "torrent_status",
"translation": "種子狀態"
},
{
"id": "torrent_status_hidden",
"translation": "隱藏"
},
{
"id": "torrent_status_normal",
"translation": "普通"
},
{
"id": "torrent_status_remake",
"translation": "再發行"
},
{
"id": "profile_edit_page",
"translation": "編輯 %s 的個人檔案"
},
{
"id":"date_format",
"translation": "2006-01-02 15:04"
},
{
"id": "seeders",
"translation": "上傳數"
},
{
"id": "leechers",
"translation": "下載數"
},
{
"id": "completed",
"translation": "完成數"
},
{
"id": "change_language",
"translation": "變更語言"
},
{
"id": "language_name",
"translation": "繁體中文"
},
{
"id": "delete",
"translation": "刪除"
}
]

Voir le fichier

@ -2,13 +2,40 @@ package languages
import (
"fmt"
"html/template"
"net/http"
"path"
"path/filepath"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/service/user"
"github.com/nicksnyder/go-i18n/i18n"
"github.com/nicksnyder/go-i18n/i18n/language"
"html/template"
"net/http"
)
// Initialize the languages translation
func InitI18n(conf config.I18nConfig) error {
defaultFilepath := path.Join(conf.TranslationsDirectory, conf.DefaultLanguage+".all.json")
err := i18n.LoadTranslationFile(defaultFilepath)
if err != nil {
panic(fmt.Sprintf("failed to load default translation file '%s': %v", defaultFilepath, err))
}
paths, err := filepath.Glob(path.Join(conf.TranslationsDirectory, "*.json"))
if err != nil {
return fmt.Errorf("failed to get translation files: %v", err)
}
for _, path := range paths {
err := i18n.LoadTranslationFile(path)
if err != nil {
return fmt.Errorf("failed to load translation file '%s': %v", path, err)
}
}
return nil
}
// When go-i18n finds a language with >0 translations, it uses it as the Tfunc
// However, if said language has a missing translation, it won't fallback to the "main" language
func TfuncAndLanguageWithFallback(language string, languages ...string) (i18n.TranslateFunc, *language.Language, error) {
@ -81,7 +108,6 @@ func GetTfuncAndLanguageFromRequest(r *http.Request, defaultLanguage string) (T
return
}
func SetTranslationFromRequest(tmpl *template.Template, r *http.Request, defaultLanguage string) i18n.TranslateFunc {
r.Header.Add("Vary", "Accept-Encoding")
T, _ := GetTfuncAndLanguageFromRequest(r, defaultLanguage)

Voir le fichier

@ -0,0 +1,18 @@
package languages
import (
"path"
"testing"
"github.com/ewhal/nyaa/config"
)
func TestInitI18n(t *testing.T) {
conf := config.DefaultI18nConfig
conf.TranslationsDirectory = path.Join("..", "..", conf.TranslationsDirectory)
err := InitI18n(conf)
if err != nil {
t.Errorf("failed to initialize language translations: %v", err)
}
}

Voir le fichier

@ -87,8 +87,8 @@ func searchByQuery(r *http.Request, pagenum int, countAll bool) (
}
search.Category.Main = uint8(tmp)
if len(s) == 3 {
tmp, err = strconv.ParseUint(string(s[2]), 10, 8)
if len(s) > 2 && len(s) < 5 {
tmp, err = strconv.ParseUint(s[2:], 10, 8)
if err != nil {
return
}
@ -151,7 +151,7 @@ func searchByQuery(r *http.Request, pagenum int, countAll bool) (
if search.Category.Main != 0 {
conditions = append(conditions, "category = ?")
parameters.Params = append(parameters.Params, string(catString[0]))
parameters.Params = append(parameters.Params, search.Category.Main)
}
if search.UserID != 0 {
conditions = append(conditions, "uploader = ?")
@ -159,7 +159,7 @@ func searchByQuery(r *http.Request, pagenum int, countAll bool) (
}
if search.Category.Sub != 0 {
conditions = append(conditions, "sub_category = ?")
parameters.Params = append(parameters.Params, string(catString[2]))
parameters.Params = append(parameters.Params, search.Category.Sub)
}
if search.Status != 0 {
if search.Status == common.FilterRemakes {