Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Merge branch 'dev' into dev

Cette révision appartient à :
akuma06 2017-05-29 14:57:55 +02:00 révisé par GitHub
révision 33707e965c
26 fichiers modifiés avec 898 ajouts et 759 suppressions

Voir le fichier

@ -64,7 +64,19 @@ type Config struct {
}
// Defaults : Configuration by default
var Defaults = Config{"localhost", 9999, "sqlite3", "./nyaa.db?cache_size=50", "default", DefaultScraperConfig, DefaultCacheConfig, DefaultSearchConfig, nil, DefaultMetainfoFetcherConfig, DefaultI18nConfig}
var Defaults = Config{
Host: "localhost",
Port: 9999,
DBType: "sqlite3",
DBParams: "./nyaa.db?cache_size=50",
DBLogMode: "default",
Scrape: DefaultScraperConfig,
Cache: DefaultCacheConfig,
Search: DefaultSearchConfig,
I2P: nil,
MetainfoFetcher: DefaultMetainfoFetcherConfig,
I18n: DefaultI18nConfig,
}
var allowedDatabaseTypes = map[string]bool{
"sqlite3": true,
@ -81,17 +93,9 @@ var allowedDBLogModes = map[string]bool{
// New : Construct a new config variable
func New() *Config {
var config Config
config.Host = Defaults.Host
config.Port = Defaults.Port
config.DBType = Defaults.DBType
config.DBParams = Defaults.DBParams
config.DBLogMode = Defaults.DBLogMode
config.Scrape = Defaults.Scrape
config.Cache = Defaults.Cache
config.MetainfoFetcher = Defaults.MetainfoFetcher
config.I18n = Defaults.I18n
return &config
cfg := &Config{}
*cfg = Defaults
return cfg
}
// BindFlags returns a function which is to be used after

42
main.go
Voir le fichier

@ -4,6 +4,7 @@ import (
"bufio"
"flag"
"context"
"net/http"
"os"
"time"
@ -37,19 +38,24 @@ func RunServer(conf *config.Config) {
}
l, err := network.CreateHTTPListener(conf)
log.CheckError(err)
if err == nil {
// add http server to be closed gracefully
signals.RegisterCloser(&network.GracefulHttpCloser{
Server: srv,
Listener: l,
})
log.Infof("listening on %s", l.Addr())
err := srv.Serve(l)
if err != nil && err != network.ErrListenerStopped {
log.CheckError(err)
}
if err != nil {
return
}
log.Infof("listening on %s", l.Addr())
// http.Server.Shutdown closes associated listeners/clients.
// context.Background causes srv to indefinitely try to
// gracefully shutdown. add a timeout if this becomes a problem.
signals.OnInterrupt(func() {
srv.Shutdown(context.Background())
})
err = srv.Serve(l)
if err == nil {
log.Panic("http.Server.Serve never returns nil")
}
if err == http.ErrServerClosed {
return
}
log.CheckError(err)
}
// RunScraper runs tracker scraper mainloop
@ -73,10 +79,10 @@ func RunScraper(conf *config.Config) {
workers = 1
}
// register udp socket with signals
signals.RegisterCloser(pc)
// register scraper with signals
signals.RegisterCloser(scraper)
signals.OnInterrupt(func() {
pc.Close()
scraper.Close()
})
// run udp scraper worker
for workers > 0 {
log.Infof("starting up worker %d", workers)
@ -96,7 +102,9 @@ func RunMetainfoFetcher(conf *config.Config) {
return
}
signals.RegisterCloser(fetcher)
signals.OnInterrupt(func() {
fetcher.Close()
})
fetcher.RunAsync()
fetcher.Wait()
}

Voir le fichier

@ -1,18 +0,0 @@
package network
import (
"net"
"net/http"
)
// GracefulHttpCloser : implements io.Closer that gracefully closes an http server
type GracefulHttpCloser struct {
Server *http.Server
Listener net.Listener
}
// Close method
func (c *GracefulHttpCloser) Close() error {
c.Listener.Close()
return c.Server.Shutdown(nil)
}

Voir le fichier

@ -1,61 +0,0 @@
package network
import (
"errors"
"net"
)
var ErrListenerStopped = errors.New("listener was stopped")
// GracefulListener provides safe and graceful net.Listener wrapper that prevents error on graceful shutdown
type GracefulListener struct {
listener net.Listener
stop chan int
}
// Accept method
func (l *GracefulListener) Accept() (net.Conn, error) {
for {
c, err := l.listener.Accept()
select {
case <-l.stop:
if c != nil {
c.Close()
}
close(l.stop)
l.stop = nil
return nil, ErrListenerStopped
default:
}
if err != nil {
neterr, ok := err.(net.Error)
if ok && neterr.Timeout() && neterr.Temporary() {
continue
}
}
return c, err
}
}
// Close method
func (l *GracefulListener) Close() (err error) {
l.listener.Close()
if l.stop != nil {
l.stop <- 0
}
return
}
// Addr method
func (l *GracefulListener) Addr() net.Addr {
return l.listener.Addr()
}
// WrapListener wraps a net.Listener such that it can be closed gracefully
func WrapListener(l net.Listener) net.Listener {
return &GracefulListener{
listener: l,
stop: make(chan int),
}
}

Voir le fichier

@ -9,33 +9,27 @@ import (
)
// CreateHTTPListener creates a net.Listener for main http webapp given main config
func CreateHTTPListener(conf *config.Config) (l net.Listener, err error) {
if conf.I2P == nil {
l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", conf.Host, conf.Port))
} else {
func CreateHTTPListener(conf *config.Config) (net.Listener, error) {
if conf.I2P != nil {
s := i2p.NewSession(conf.I2P.Name, conf.I2P.Addr, conf.I2P.Keyfile)
err = s.Open()
err := s.Open()
if s != nil {
log.Infof("i2p address: %s", s.B32Addr())
l = s
}
return s, err
}
if l != nil {
l = WrapListener(l)
}
return
return net.Listen("tcp", fmt.Sprintf("%s:%d", conf.Host, conf.Port))
}
// CreateScraperSocket creates a UDP Scraper socket
func CreateScraperSocket(conf *config.Config) (pc net.PacketConn, err error) {
if conf.I2P == nil {
var laddr *net.UDPAddr
laddr, err = net.ResolveUDPAddr("udp", conf.Scrape.Addr)
if err == nil {
pc, err = net.ListenUDP("udp", laddr)
}
} else {
func CreateScraperSocket(conf *config.Config) (net.PacketConn, error) {
if conf.I2P != nil {
log.Fatal("i2p udp scraper not supported")
}
return
var laddr *net.UDPAddr
laddr, err := net.ResolveUDPAddr("udp", conf.Scrape.Addr)
if err != nil {
return nil, err
}
return net.ListenUDP("udp", laddr)
}

Voir le fichier

@ -493,11 +493,24 @@ td.tr-le { color: #E84C4C; }
border-radius: 4px;
}
#filelist > p {
padding-right: 14px;
padding-left: 14px;
}
.torrent-info-box > * > img {
max-width: 100%;
max-height: 100%;
}
.comment-box {
margin-top: 10px;
padding-left: 7px;
padding-right: 7px;
margin-right: 30px;
margin-left: 30px;
word-break: break-word;
text-align: justify;
}
.comment-form {
margin-left: 10px;

163
public/img/mafuyu.svg Fichier normal

Le diff du fichier est caché, car une ou plusieurs lignes sont trop longues

Après

Largeur:  |  Hauteur:  |  Taille: 509 Kio

BIN
public/img/sukebei_logo.png Fichier normal

Fichier binaire non affiché.

Après

Largeur:  |  Hauteur:  |  Taille: 47 Kio

Voir le fichier

@ -72,7 +72,7 @@ for(var i in list) {
e.innerText = new Date(e.innerText).toLocaleString(lang);
}
/*Fixed-Navbar offset fix*/
window.onload = function() {
document.addEventListener("DOMContentLoaded", function(event) {
var shiftWindow = function() { scrollBy(0, -70) };
if (location.hash) shiftWindow();
window.addEventListener("hashchange", shiftWindow);
@ -86,7 +86,16 @@ window.onload = function() {
document.getElementsByClassName("search-box")[0].style.width = "";
document.getElementsByClassName("h-user")[0].style.display = "inline-block";
});
};
// Keep mascot hiding choice
var hideMascot = (localStorage.getItem("hide_mascot") == "true")
if (hideMascot) {
var btn = document.getElementById("mascotKeepHide");
btn.innerHTML = "Mascot";
document.getElementById("mascot").className = "hide";
btn.value = "show";
}
});
function playVoice() {
if (explosion) {
@ -96,4 +105,19 @@ function playVoice() {
nyanpassu.volume = 0.5;
nyanpassu.play();
}
}
function toggleMascot(btn) {
var state= btn.value;
if (state == "hide") {
btn.innerHTML = "Mascot";
document.getElementById("mascot").className = "hide";
btn.value = "show";
localStorage.setItem("hide_mascot", "true")
} else {
btn.innerHTML = "Mascot";
document.getElementById("mascot").className = "";
btn.value = "hide";
localStorage.setItem("hide_mascot", "false")
}
}

Voir le fichier

@ -1,7 +1,6 @@
package router
import (
"encoding/hex"
"errors"
"fmt"
"io"
@ -172,12 +171,17 @@ func (f *uploadForm) ExtractInfo(r *http.Request) error {
if len(f.Magnet) != 0 {
return errTorrentPlusMagnet
}
binInfohash, err := torrent.Infohash()
if err != nil {
return err
_, seekErr = tfile.Seek(0, io.SeekStart)
if seekErr != nil {
return seekErr
}
f.Infohash = strings.ToUpper(hex.EncodeToString(binInfohash[:]))
f.Magnet = util.InfoHashToMagnet(f.Infohash, f.Name, trackers...)
infohash, err := metainfo.DecodeInfohash(tfile)
if err != nil {
return metainfo.ErrInvalidTorrentFile
}
f.Infohash = infohash
f.Magnet = util.InfoHashToMagnet(infohash, f.Name, trackers...)
// extract filesize
f.Filesize = int64(torrent.TotalSize())

Voir le fichier

@ -108,8 +108,12 @@ func PostCommentHandler(w http.ResponseWriter, r *http.Request) {
if strings.TrimSpace(content) == "" {
messages.AddErrorT("errors", "comment_empty")
}
if len(content) > 500 {
messages.AddErrorT("errors", "comment_toolong")
}
if !messages.HasErrors() {
userID := currentUser.ID
comment := model.Comment{TorrentID: torrent.ID, UserID: userID, Content: content, CreatedAt: time.Now()}
err := db.ORM.Create(&comment).Error
comment.Torrent = &torrent

Voir le fichier

@ -198,11 +198,15 @@ func (r *TorrentRequest) ValidateMultipartUpload(req *http.Request) (int64, erro
r.Name = torrent.TorrentName()
}
binInfohash, err := torrent.Infohash()
_, err = tfile.Seek(0, io.SeekStart)
if err != nil {
return 0, err, http.StatusInternalServerError
}
r.Hash = strings.ToUpper(hex.EncodeToString(binInfohash[:]))
infohash, err := metainfo.DecodeInfohash(tfile)
if err != nil {
return 0, err, http.StatusInternalServerError
}
r.Hash = infohash
// extract filesize
filesize := int64(torrent.TotalSize())

Voir le fichier

@ -7,7 +7,8 @@ import (
msg "github.com/NyaaPantsu/nyaa/util/messages"
)
const emailRegex = `(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})`
// Regex by: Philippe Verdy (in a comment somewhere on a website) - Valid every email RFC valid
const emailRegex = `^((?:[-!#$%&'*+/=?^` + "`" + `{|}~\w]|\\.)+(?:\.(?:[-!#$%&'*+/=?^` + "`" + `{|}~\w]|\\.)+)*|"(?:[^\\"]|\\.)+")@(?:\[(?:((?:(?:[01][\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:[01][\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?))|IPv6:((?:[0-9A-F]{1,4}:){7}[0-9A-F]{1,4}|(?:[0-9A-F]{1,4}:){6}:[0-9A-F]{1,4}|(?:[0-9A-F]{1,4}:){5}:(?:[0-9A-F]{1,4}:)?[0-9A-F]{1,4}|(?:[0-9A-F]{1,4}:){4}:(?:[0-9A-F]{1,4}:){0,2}[0-9A-F]{1,4}|(?:[0-9A-F]{1,4}:){3}:(?:[0-9A-F]{1,4}:){0,3}[0-9A-F]{1,4}|(?:[0-9A-F]{1,4}:){2}:(?:[0-9A-F]{1,4}:){0,4}[0-9A-F]{1,4}|[0-9A-F]{1,4}::(?:[0-9A-F]{1,4}:){0,5}[0-9A-F]{1,4}|::(?:[0-9A-F]{1,4}:){0,6}[0-9A-F]{1,4}|(?:[0-9A-F]{1,4}:){1,7}:|(?:[0-9A-F]{1,4}:){6}(?:(?:[01][\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:[01][\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)|(?:[0-9A-F]{1,4}:){0,5}:(?:(?:[01][\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:[01][\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)|::(?:[0-9A-F]{1,4}:){0,5}(?:(?:[01][\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:[01][\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?))|([-a-z\d]{0,62}[a-z\d]:[^\[\\\]]+))\]|([a-z\d](?:[-a-z\d]{0,62}[a-z\d])?(?:\.[a-z\d](?:[-a-z\d]{0,62}[a-z\d])?)+))$`
const usernameRegex = `(\W)`
// EmailValidation : Check if an email is valid

Voir le fichier

@ -1,19 +1,19 @@
{{define "search_common"}}
<select name="c" class="form-input hide-xs" value>
<select name="c" class="form-input hide-xs">
<option value="_">{{call $.T "all_categories"}}</option>
{{ range $name_cat, $id_cat := (GetCategories true) }}
<option value="{{ $id_cat }}" {{if eq $.Search.Category $id_cat }}selected{{end}}>{{call $.T $name_cat }}</option>
{{ end }}
</select>
<label><span class="select-icon caret-down-icon"></span></label>
<select name="s" class="form-input hide-xs" value>
<select name="s" class="form-input hide-xs">
<option value="0">{{call $.T "show_all"}}</option>
<option value="1" {{if eq .Search.Status 1}}selected{{end}}>{{call $.T "filter_remakes"}}</option>
<option value="2" {{if eq .Search.Status 2}}selected{{end}}>{{call $.T "trusted"}}</option>
<option value="3" {{if eq .Search.Status 3}}selected{{end}}>A+</option>
</select>
{{ if .Search.ShowItemsPerPage }}
<select name="max" class="form-input hide-xs" value>
<select name="max" class="form-input hide-xs">
<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>

Voir le fichier

@ -33,10 +33,10 @@
{{range .TorrentReports}}
<tr>
<td><a href="{{ genRoute "view_torrent" "id" .Torrent.ID }}">{{ .Torrent.Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">Edit</a>)</td>
<td><a href="{{ genRoute "view_torrent" "id" (print .Torrent.ID ) }}">{{ .Torrent.Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{ print .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"><i class="glyphicon glyphicon-trash"></i> {{ call $.T "delete" }}</a></td>
<td><a href="{{ genRoute "mod_trdelete" }}?id={{ print .ID }}" class="btn btn-danger btn-lg"><i class="glyphicon glyphicon-trash"></i> {{ call $.T "delete" }}</a></td>
</tr>
{{end}}
</table>

Voir le fichier

@ -10,11 +10,11 @@
{{range .Models}}
<tr>
<td><a href="{{ genRoute "view_torrent" "id" .Torrent.ID }}">{{ .Torrent.Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">Edit</a>)</td>
<td><a href="{{ genRoute "view_torrent" "id" (print .Torrent.ID) }}">{{ .Torrent.Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{ print .Torrent.ID}}">Edit</a>)</td>
<td>{{.User.Username}}</td>
<td>{{.Description}}</td>
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .Torrent.ID }}" onclick="if (!confirm('Are you sure?')) return false;">Delete</a><br />
<a href="{{ genRoute "mod_trdelete" }}?id={{ .ID }}">Delete Report</a></td>
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ print .Torrent.ID }}" onclick="if (!confirm('Are you sure?')) return false;">Delete</a><br />
<a href="{{ genRoute "mod_trdelete" }}?id={{ print .ID }}">Delete Report</a></td>
</tr>
{{end}}
</table>

Voir le fichier

@ -1,105 +1,87 @@
<!DOCTYPE html>
<html lang="{{ call $.T "language_code" }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="keywords" content="torrents, cartel, pantsu, anime, manga, sukebei, nyaapassu, horriblesubs, nyaa, hentai, dlsite">
<meta name="description" content='The leading open-source community-based nyaa.se successor, suitable for all anime and manga needs. にゃんぱす~!'>
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>{{if Sukebei}}Sukebei{{else}}Nyaa{{end}} Pantsu - {{block "title" .}}{{ call $.T "error_404" }}{{end}}</title>
<link rel="icon" type="image/png" href="/img/favicon.png?v=3" />
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="keywords" content="torrents, cartel, pantsu, anime, manga, sukebei, nyaapassu, horriblesubs, nyaa, hentai, dlsite">
<meta name="description" content='The leading open-source community-based nyaa.se successor, suitable for all anime and manga needs. にゃんぱす~!'>
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>{{if Sukebei}}Sukebei{{else}}Nyaa{{end}} Pantsu - {{block "title" .}}{{ call $.T "error_404" }}{{end}}</title>
<link rel="icon" type="image/png" href="/img/favicon.png?v=3" />
<!-- RSS Feed with Context -->
<link href="{{ block "rss_link" . }}{{ genRouteWithQuery "feed" .URL }}{{end}}" rel="alternate" type="application/rss+xml" title="Nyaa Pantsu - {{block "rsstitle" .}}Last torrents{{end}} RSS Feed" />
<!-- do NOT move -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
<!-- Base theme -->
<link rel="stylesheet" id="style" href="{{.URL.Parse "/css/main.css"}}?v=2">
<!-- User selected theme, if any -->
{{if $.Theme}}<link rel="stylesheet" id="theme" href="/css/{{$.Theme}}.css">{{end}}
<!-- RSS Feed with Context -->
<link href="{{ block "rss_link" . }}{{ genRouteWithQuery "feed" .URL }}{{end}}" rel="alternate" type="application/rss+xml" title="Nyaa Pantsu - {{block "rsstitle" .}}Last torrents{{end}} RSS Feed" />
<!-- do NOT move -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
<!-- Base theme -->
<link rel="stylesheet" id="style" href="{{.URL.Parse "/css/main.css"}}?v=2">
<!-- User selected theme, if any -->
{{if $.Theme}}<link rel="stylesheet" id="theme" href="/css/{{$.Theme}}.css">{{end}}
<!-- Search Box for Google -->
<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","url":"https://nyaa.pantsu.cat/","potentialAction":{"@type":"SearchAction","target":"https://nyaa.pantsu.cat/search?q={search_term_string}","query-input":"required name=search_term_string"}}</script>
<script>
// We need this inline javascript for preventing theme flickering
function toggleMascot(btn) {
var state= btn.value;
if (state == "hide") {
btn.innerHTML = "Mascot";
document.getElementById("mascot").className = "hide";
btn.value = "show";
} else {
btn.innerHTML = "Mascot";
document.getElementById("mascot").className = "";
btn.value = "hide";
}
}
</script>
<!-- Search Box for Google -->
<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","url":"https://nyaa.pantsu.cat/","potentialAction":{"@type":"SearchAction","target":"https://nyaa.pantsu.cat/search?q={search_term_string}","query-input":"required name=search_term_string"}}</script>
{{ block "additional_header" .}}{{end}}
</head>
<body>
<nav id="header" class="header">
<div class="container">
<div class="h-left">
<div class="h-logo">
<a href="{{.URL.Parse "/"}}"><img class="hide-md" src="/img/logo.png" alt="NyaaPantsu"><img class="visible-md" src="/img/logo_s.png"></img></a>
</head>
<body {{if Sukebei}}class="sukebei"{{end}}>
<nav id="header" class="header">
<div class="container">
<div class="h-left">
<div class="h-logo">
<a href="{{.URL.Parse "/"}}"><img class="hide-md" src="/img/{{if Sukebei}}sukebei_logo.png{{else}}logo.png{{end}}" alt="NyaaPantsu"><img class="visible-md" src="/img/logo_s.png"/></a>
</div>
<div class="h-nav">
<a href="{{.URL.Parse "/upload"}}" class="nav-btn"><div class="upload-icon visible-md"></div><span class="hide-md">{{ call $.T "upload" }}</span></a>
<a href="{{ genRouteWithQuery "feed" .URL }}" class="nav-btn"><div class="rss-icon visible-md"></div><span class="hide-md">RSS</span></a>
<a href="{{.URL.Parse "/faq"}}" class="nav-btn"><div class="faq-icon visible-md"></div><span class="hide-md">{{ call $.T "faq" }}</span></a>
{{if Sukebei}}
<a href="//nyaa.pantsu.cat" class="nav-btn"><div class="fun-icon visible-md"></div><span class="hide-md">{{ call $.T "fun" }}</span></a>
{{else}}
<a href="//sukebei.pantsu.cat" class="nav-btn"><div class="fap-icon visible-md"></div><span class="hide-md">{{ call $.T "fap" }}</span></a>
{{end}}
</div>
</div>
<div class="h-right">
{{block "badge_user" .}}{{end}}
<div class="h-search">
<form role="search" action="/search" method="get">
{{block "search_common" .}}{{end}}
{{block "search_button" .}}{{end}}
</form>
</div>
<div class="h-nav">
<a href="{{.URL.Parse "/upload"}}" class="nav-btn"><div class="upload-icon visible-md"></div><span class="hide-md">{{ call $.T "upload" }}</span></a>
<a href="{{ genRouteWithQuery "feed" .URL }}" class="nav-btn"><div class="rss-icon visible-md"></div><span class="hide-md">RSS</span></a>
<a href="{{.URL.Parse "/faq"}}" class="nav-btn"><div class="faq-icon visible-md"></div><span class="hide-md">{{ call $.T "faq" }}</span></a>
{{if Sukebei}}
<a href="//nyaa.pantsu.cat" class="nav-btn"><div class="fun-icon visible-md"></div><span class="hide-md">{{ call $.T "fun" }}</span></a>
{{else}}
<a href="//sukebei.pantsu.cat" class="nav-btn"><div class="fap-icon visible-md"></div><span class="hide-md">{{ call $.T "fap" }}</span></a>
{{end}}
</div>
</div>
<div class="h-right">
{{block "badge_user" .}}{{end}}
<div class="h-search">
<form role="search" action="/search" method="get">
{{block "search_common" .}}{{end}}
{{block "search_button" .}}{{end}}
</form>
</div>
</div>
</nav>
<div id="content">
<div class="content container center">
{{block "content" .}}{{call $.T "nothing_here"}}{{end}}
</div>
<div id="mascot" class="hide-xs" onclick="playVoice();"></div>
{{ if eq .Theme "tomorrow" }}
<audio id="explosion" hidden preload="none">
<source src="https://megumin.love/sounds/explosion.mp3" type="audio/mpeg">
</audio>
{{ else }}
<audio id="nyanpassu" hidden preload="none">
<source src="https://a.doko.moe/sewang.mp3" type="audio/mpeg">
</audio>
{{end}}
<div class="pagination">
{{ genNav .Navigation .URL 15 }}
</div>
<footer id="footer">
<div class="container footer center">
<div class="footer-opt">
<button class="form-input btn" id="mascotKeepHide" onclick="toggleMascot(this)" value="hide"><span class="oi" data-glyph="eye"></span> Mascot</button>
<p><a href="/settings">Change Settings</a></p>
</div>
<span><i>Powered by <a href="#">NyaaPantsu</a></i></span>
</div>
</footer>
</div>
</nav>
<div id="content">
<div class="content container center">
{{block "content" .}}{{call $.T "nothing_here"}}{{end}}
</div>
<div id="mascot" class="hide-xs" onclick="playVoice();"></div>
{{ if eq .Theme "tomorrow" }}
<audio id="explosion" hidden preload="none">
<source src="https://megumin.love/sounds/explosion.mp3" type="audio/mpeg">
</audio>
{{ else }}
<audio id="nyanpassu" hidden preload="none">
<source src="https://a.doko.moe/sewang.mp3" type="audio/mpeg">
</audio>
{{end}}
<div class="pagination">
{{ genNav .Navigation .URL 15 }}
</div>
<footer id="footer">
<div class="container footer center">
<div class="footer-opt">
<button class="form-input btn" id="mascotKeepHide" onclick="toggleMascot(this)" value="hide"><span class="oi" data-glyph="eye"></span> Mascot</button>
<p><a href="/settings">Change Settings</a></p>
</div>
<span><i>Powered by <a href="#">NyaaPantsu</a></i></span>
</div>
</footer>
</div>
<script type="text/javascript" charset="utf-8" src="{{.URL.Parse "/js/main.js"}}?v=3"></script>
{{ block "footer_js" .}}{{end}}
</body>
</html>
<script type="text/javascript" charset="utf-8" src="{{.URL.Parse "/js/main.js"}}?v=3"></script>
{{ block "footer_js" .}}{{end}}
</body>
</html>

Voir le fichier

@ -13,10 +13,10 @@
</select>
<h3>{{call $.T "theme"}}</h3>
<select id="theme-selector" name="theme" class="form-input" onchange="switchThemes()">
<option value="{{$.Theme}}" selected>{{call $.T "select"}}</option>
<option value="{{$.Theme}}" selected>{{call $.T "theme_select"}}</option>
<option value="g">/g/</option>
<option value="tomorrow">Tomorrow</option>
<option value="">None</option>
<option value="">{{call $.T "theme_none"}}</option>
</select>
</br>
</br>

Voir le fichier

@ -29,7 +29,7 @@
<input type="checkbox" name="remake" id="remake" >
<label for="remake">{{call $.T "mark_as_remake"}}</label>
</p>
{{ if $.User }}
{{ if gt $.User.ID 0 }}
<p>
<input type="checkbox" name="hidden" id="hidden" >
<label for="hidden">{{call $.T "mark_as_hidden"}}</label>

Voir le fichier

@ -970,5 +970,21 @@
{
"id": "torrent_nav_deleted",
"translation": "Torrents deleted"
},
{
"id": "theme",
"translation": "Theme"
},
{
"id": "theme_select",
"translation": "Select a Theme"
},
{
"id": "theme_none",
"translation": "None"
},
{
"id": "mark_as_hidden",
"translation": "Mark as Hidden"
}
]

Le diff du fichier est caché, car celui-ci est trop grand Voir la diff

Voir le fichier

@ -970,5 +970,21 @@
{
"id": "torrent_nav_deleted",
"translation": "Torrent は削除されました"
},
{
"id": "theme",
"translation": "テーマ"
},
{
"id": "theme_select",
"translation": "-- テーマを選択 --"
},
{
"id": "theme_none",
"translation": "なし"
},
{
"id": "mark_as_hidden",
"translation": "非表示としてマークする"
}
]

Voir le fichier

@ -547,6 +547,10 @@
"id": "comments",
"translation": "คอมเมนท์"
},
{
"id": "no_description",
"translation": "ไม่ได้ระบุรายละเอียดเอาไว้!"
},
{
"id": "submit_a_comment_as_username",
"translation": "ส่งคอมเมนท์ในชื่อ %s"
@ -691,6 +695,10 @@
"id": "files",
"translation": "ไฟล์"
},
{
"id": "no_files",
"translation": "ไม่เจอไฟล์? บ้าน่า!"
},
{
"id": "filename",
"translation": "ชื่อไฟล์"
@ -841,7 +849,7 @@
},
{
"id": "no_action_selected",
"translation": "คุณต้องบอกว่าคุณเลือกจะทำอะไร!"
"translation": "คุณต้องเลือกว่าคุณจะทำอะไร!"
},
{
"id": "no_move_location_selected",
@ -929,7 +937,7 @@
},
{
"id": "delete_definitely_torrent_warning",
"translation": "คุณไม่สามารถกู้ไฟล์ได้ นอกจากหยุดคนอื่นไม่ให้อัพโหลดซ้ำ!!"
"translation": "คุณจะกู้ไฟล์กลับมาไม่ได้ จะหยุดคนอื่นไม่ให้อัพโหลดซ้ำก็ไม่ได้!"
},
{
"id": "delete_definitely",

Voir le fichier

@ -4,9 +4,11 @@ package metainfo
import (
"crypto/sha1"
"encoding/hex"
"io"
"os"
"path/filepath"
"strings"
"github.com/zeebo/bencode"
)
@ -120,19 +122,6 @@ func (tf *TorrentFile) IsPrivate() bool {
return tf.Info.Private != nil && *tf.Info.Private == 1
}
// Infohash : calculate infohash
func (tf *TorrentFile) Infohash() (ih [20]byte, err error) {
s := sha1.New()
enc := bencode.NewEncoder(s)
err = enc.Encode(&tf.Info)
if err != nil {
return
}
d := s.Sum(nil)
copy(ih[:], d[:])
return
}
// IsSingleFile : return true if this torrent is for a single file
func (tf *TorrentFile) IsSingleFile() bool {
return tf.Info.Length > 0
@ -151,3 +140,27 @@ func (tf *TorrentFile) Decode(r io.Reader) (err error) {
err = dec.Decode(tf)
return
}
type torrentRaw struct {
InfoRaw bencode.RawMessage `bencode:"info"`
}
// DecodeInfohash : Decode and calculate the info hash
func DecodeInfohash(r io.Reader) (hash string, err error) {
var t torrentRaw
d := bencode.NewDecoder(r)
err = d.Decode(&t)
if err != nil {
return
}
s := sha1.New()
_, err = s.Write(t.InfoRaw)
if err != nil {
return
}
rawHash := s.Sum(nil)
hash = strings.ToUpper(hex.EncodeToString(rawHash))
return
}

Voir le fichier

@ -1,26 +0,0 @@
package signals
import (
"io"
"sync"
)
var (
closeAccess sync.Mutex
closers []io.Closer
)
// RegisterCloser adds an io.Closer to close on interrupt
func RegisterCloser(c io.Closer) {
closeAccess.Lock()
closers = append(closers, c)
closeAccess.Unlock()
}
func closeClosers() {
closeAccess.Lock()
for _, c := range closers {
c.Close()
}
closeAccess.Unlock()
}

Voir le fichier

@ -3,8 +3,22 @@ package signals
import (
"github.com/NyaaPantsu/nyaa/router"
"github.com/NyaaPantsu/nyaa/util/log"
"sync"
)
// registered interrupt callbacks.
// currently only used to gracefully close the server.
var intEvents struct {
lock sync.Mutex
funcs []func()
}
func OnInterrupt(fn func()) {
intEvents.lock.Lock()
intEvents.funcs = append(intEvents.funcs, fn)
intEvents.lock.Unlock()
}
func handleReload() {
log.Info("Got SIGHUP")
router.ReloadTemplates()
@ -13,5 +27,9 @@ func handleReload() {
// handle interrupt signal, platform independent
func interrupted() {
closeClosers()
intEvents.lock.Lock()
for _, fn := range intEvents.funcs {
fn()
}
intEvents.lock.Unlock()
}