Merge branch 'dev'
Cette révision appartient à :
révision
c0468528ec
|
@ -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
|
||||
|
||||
|
|
|
@ -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
16
config/filesizeFetcher.go
Fichier normal
|
@ -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
11
config/i18n.go
Fichier normal
|
@ -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
|
||||
}
|
|
@ -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
41
main.go
|
@ -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
16
model/file.go
Fichier normal
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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();
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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_)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
|
|
228
service/torrent/filesizeFetcher/filesizeFetcher.go
Fichier normal
228
service/torrent/filesizeFetcher/filesizeFetcher.go
Fichier normal
|
@ -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()
|
||||
}
|
||||
|
60
service/torrent/filesizeFetcher/operation.go
Fichier normal
60
service/torrent/filesizeFetcher/operation.go
Fichier normal
|
@ -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
|
||||
}
|
||||
}
|
||||
|
45
service/torrent/filesizeFetcher/operation_test.go
Fichier normal
45
service/torrent/filesizeFetcher/operation_test.go
Fichier normal
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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}}">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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
32
templates/admin/reassign.html
Fichier normal
|
@ -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}}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}}
|
||||
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">×</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 />
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "削除"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "ลบ"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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": "删除"
|
||||
}
|
||||
]
|
||||
]
|
|
@ -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": "刪除"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
|
18
util/languages/translation_test.go
Fichier normal
18
util/languages/translation_test.go
Fichier normal
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
Référencer dans un nouveau ticket