[magneticow] the search now works, but need to change our approach
This commit is contained in:
parent
ac7d0a514f
commit
0c54cc80dc
@ -5,11 +5,11 @@ import (
|
|||||||
|
|
||||||
"github.com/anacrolix/torrent/bencode"
|
"github.com/anacrolix/torrent/bencode"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"strings"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Transport struct {
|
type Transport struct {
|
||||||
conn *net.UDPConn
|
fd int
|
||||||
laddr *net.UDPAddr
|
laddr *net.UDPAddr
|
||||||
started bool
|
started bool
|
||||||
|
|
||||||
@ -46,16 +46,18 @@ func (t *Transport) Start() {
|
|||||||
t.started = true
|
t.started = true
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
t.conn, err = net.ListenUDP("udp", t.laddr)
|
t.fd, err = unix.Socket(unix.SOCK_DGRAM, unix.AF_INET, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.L().Fatal("Could NOT create a UDP socket!", zap.Error(err))
|
zap.L().Fatal("Could NOT create a UDP socket!", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unix.Bind(t.fd, unix.SockaddrInet4{Addr: t.laddr.IP, Port: t.laddr.Port})
|
||||||
|
|
||||||
go t.readMessages()
|
go t.readMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) Terminate() {
|
func (t *Transport) Terminate() {
|
||||||
t.conn.Close()
|
unix.Close(t.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// readMessages is a goroutine!
|
// readMessages is a goroutine!
|
||||||
@ -63,14 +65,10 @@ func (t *Transport) readMessages() {
|
|||||||
buffer := make([]byte, 65536)
|
buffer := make([]byte, 65536)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
n, addr, err := t.conn.ReadFrom(buffer)
|
n, from, err := unix.Recvfrom(t.fd, buffer, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: isn't there a more reliable way to detect if UDPConn is closed?
|
// TODO: isn't there a more reliable way to detect if UDPConn is closed?
|
||||||
if strings.HasSuffix(err.Error(), "use of closed network connection") {
|
zap.L().Debug("Could NOT read an UDP packet!", zap.Error(err))
|
||||||
break
|
|
||||||
} else {
|
|
||||||
zap.L().Debug("Could NOT read an UDP packet!", zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg Message
|
var msg Message
|
||||||
@ -79,7 +77,7 @@ func (t *Transport) readMessages() {
|
|||||||
zap.L().Debug("Could NOT unmarshal packet data!", zap.Error(err))
|
zap.L().Debug("Could NOT unmarshal packet data!", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
t.onMessage(&msg, addr)
|
t.onMessage(&msg, from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,9 +87,9 @@ func (t *Transport) WriteMessages(msg *Message, addr net.Addr) {
|
|||||||
zap.L().Panic("Could NOT marshal an outgoing message! (Programmer error.)")
|
zap.L().Panic("Could NOT marshal an outgoing message! (Programmer error.)")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = t.conn.WriteTo(data, addr)
|
err = unix.Sendto(t.fd, data, 0, addr)
|
||||||
// TODO: isn't there a more reliable way to detect if UDPConn is closed?
|
// TODO: isn't there a more reliable way to detect if UDPConn is closed?
|
||||||
if err != nil && !strings.HasSuffix(err.Error(), "use of closed network connection") {
|
if err != nil {
|
||||||
zap.L().Debug("Could NOT write an UDP packet!", zap.Error(err))
|
zap.L().Debug("Could NOT write an UDP packet!", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
cmd/magneticow/data/static/scripts/torrents.js
Normal file
4
cmd/magneticow/data/static/scripts/torrents.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
function loadMore() {
|
||||||
|
console.log("lastX", canLoadMore, lastID, lastOrderedValue);
|
||||||
|
|
||||||
|
}
|
@ -12,12 +12,12 @@
|
|||||||
<main>
|
<main>
|
||||||
<div><b>magnetico<sup>w</sup></b>​<sub>(pre-alpha)</sub></div>
|
<div><b>magnetico<sup>w</sup></b>​<sub>(pre-alpha)</sub></div>
|
||||||
<form action="/torrents" method="get" autocomplete="off" role="search">
|
<form action="/torrents" method="get" autocomplete="off" role="search">
|
||||||
<input type="search" name="search" placeholder="Search the BitTorrent DHT" autofocus>
|
<input type="search" name="query" placeholder="Search the BitTorrent DHT" autofocus>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
~{{ "{:,}".format(n_torrents) }} torrents available (see the <a href="/statistics">statistics</a>).
|
~{{ comma .NTorrents }} torrents available (see the <a href="/statistics">statistics</a>).
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<header>
|
<header>
|
||||||
<div><a href="/"><b>magnetico<sup>w</sup></b></a>​<sub>(pre-alpha)</sub></div>
|
<div><a href="/"><b>magnetico<sup>w</sup></b></a>​<sub>(pre-alpha)</sub></div>
|
||||||
<form action="/torrents" method="get" autocomplete="off" role="search">
|
<form action="/torrents" method="get" autocomplete="off" role="search">
|
||||||
<input type="search" name="search" placeholder="Search the BitTorrent DHT">
|
<input type="search" name="query" placeholder="Search the BitTorrent DHT">
|
||||||
</form>
|
</form>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
|
@ -2,20 +2,25 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>{% if search %}"{{search}}"{% else %}Most recent torrents{% endif %} - magneticow</title>
|
<title>{{ if .Query }}"{{ .Query }}"{{ else }}Most recent torrents{{ end }} - magneticow</title>
|
||||||
<link rel="stylesheet" href="static/styles/reset.css">
|
<link rel="stylesheet" href="static/styles/reset.css">
|
||||||
<link rel="stylesheet" href="static/styles/essential.css">
|
<link rel="stylesheet" href="static/styles/essential.css">
|
||||||
<link rel="stylesheet" href="static/styles/torrents.css">
|
<link rel="stylesheet" href="static/styles/torrents.css">
|
||||||
<!-- <script src="script.js"></script> -->
|
<script>
|
||||||
|
var canLoadMore = {{ if .CanLoadMore }} true {{ else }} false {{ end }};
|
||||||
|
var lastOrderedValue = {{ .LastOrderedValue }};
|
||||||
|
var lastID = {{ .LastID }};
|
||||||
|
</script>
|
||||||
|
<script src="static/scripts/torrents.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<div><a href="/"><b>magnetico<sup>w</sup></b></a>​<sub>(pre-alpha)</sub></div>
|
<div><a href="/"><b>magnetico<sup>w</sup></b></a>​<sub>(pre-alpha)</sub></div>
|
||||||
<form action="/torrents" method="get" autocomplete="off" role="search">
|
<form action="/torrents" method="get" autocomplete="off" role="search">
|
||||||
<input type="search" name="search" placeholder="Search the BitTorrent DHT" value="{{ search }}">
|
<input type="search" name="query" placeholder="Search the BitTorrent DHT" value="{{ .Query }}">
|
||||||
</form>
|
</form>
|
||||||
<div>
|
<div>
|
||||||
<a href="{{ subscription_url }}"><img src="static/assets/feed.png"
|
<a href="{{ .SubscriptionURL }}"><img src="static/assets/feed.png"
|
||||||
alt="feed icon" title="subscribe" /> subscribe</a>
|
alt="feed icon" title="subscribe" /> subscribe</a>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -24,67 +29,29 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><!-- Magnet link --></th>
|
<th><!-- Magnet link --></th>
|
||||||
<th>
|
<th>Name</th>
|
||||||
{% if sorted_by == "name ASC" %}
|
<th>Size</th>
|
||||||
<a href="/torrents/?search={{ search }}&sort_by=name+DESC">Name ▲</a>
|
<th>Discovered on</th>
|
||||||
{% elif sorted_by == "name DESC" %}
|
|
||||||
<a href="/torrents/?search={{ search }}&sort_by=name+ASC">Name ▼</a>
|
|
||||||
{% else %}
|
|
||||||
<a href="/torrents/?search={{ search }}&sort_by=name+ASC">Name</a>
|
|
||||||
{% endif %}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{% if sorted_by == "total_size ASC" %}
|
|
||||||
<a href="/torrents/?search={{ search }}&sort_by=total_size+DESC">Size ▲</a>
|
|
||||||
{% elif sorted_by == "total_size DESC" %}
|
|
||||||
<a href="/torrents/?search={{ search }}&sort_by=total_size+ASC">Size ▼</a>
|
|
||||||
{% else %}
|
|
||||||
<a href="/torrents/?search={{ search }}&sort_by=total_size+ASC">Size</a>
|
|
||||||
{% endif %}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{% if sorted_by == "discovered_on ASC" %}
|
|
||||||
<a href="/torrents/?search={{ search }}&sort_by=discovered_on+DESC">Discovered on ▲</a>
|
|
||||||
{% elif sorted_by == "discovered_on DESC" %}
|
|
||||||
<a href="/torrents/?search={{ search }}&sort_by=discovered_on+ASC">Discovered on ▼</a>
|
|
||||||
{% else %}
|
|
||||||
<a href="/torrents/?search={{ search }}&sort_by=discovered_on+DESC">Discovered on</a>
|
|
||||||
{% endif %}
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for torrent in torrents %}
|
{{ range .Torrents }}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="magnet:?xt=urn:btih:{{ torrent.info_hash }}&dn={{ torrent.name }}">
|
<td><a href="magnet:?xt=urn:btih:{{ bytesToHex .InfoHash }}&dn={{ .Name }}">
|
||||||
<img src="static/assets/magnet.gif') }}" alt="Magnet link"
|
<img src="static/assets/magnet.gif" alt="Magnet link"
|
||||||
title="Download this torrent using magnet" /></a></td>
|
title="Download this torrent using magnet" /></a></td>
|
||||||
<td><a href="/torrents/{{ torrent.info_hash }}/{{ torrent.name }}">{{ torrent.name }}</a></td>
|
<td><a href="/torrents/{{ bytesToHex .InfoHash }}/{{ .Name }}">{{ .Name }}</a></td>
|
||||||
<td>{{ torrent.size }}</td>
|
<td>{{ humanizeSize .Size }}</td>
|
||||||
<td>{{ torrent.discovered_on }}</td>
|
<td>{{ unixTimeToYearMonthDay .DiscoveredOn }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{{ end }}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
|
<button onclick="loadMore();" {{ if not .CanLoadMore }} disabled {{ end }}>
|
||||||
<form action="/torrents" method="get">
|
Load More Results
|
||||||
<button {% if page == 0 %}disabled{% endif %}>Previous</button>
|
</button>
|
||||||
<input type="text" name="search" value="{{ search }}" hidden>
|
|
||||||
{% if sorted_by %}
|
|
||||||
<input type="text" name="sort_by" value="{{ sorted_by }}" hidden>
|
|
||||||
{% endif %}
|
|
||||||
<input type="number" name="page" value="{{ page - 1 }}" hidden>
|
|
||||||
</form>
|
|
||||||
<form action="/torrents" method="get">
|
|
||||||
<button {% if not next_page_exists %}disabled{% endif %}>Next</button>
|
|
||||||
<input type="text" name="search" value="{{ search }}" hidden>
|
|
||||||
{% if sorted_by %}
|
|
||||||
<input type="text" name="sort_by" value="{{ sorted_by }}" hidden>
|
|
||||||
{% endif %}
|
|
||||||
<input type="number" name="page" value="{{ page + 1 }}" hidden>
|
|
||||||
</form>
|
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -2,16 +2,20 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
|
||||||
|
//"strconv"
|
||||||
|
"strings"
|
||||||
|
// "time"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
|
// "github.com/dustin/go-humanize"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
@ -26,17 +30,20 @@ var database persistence.Database
|
|||||||
|
|
||||||
// ========= TD: TemplateData =========
|
// ========= TD: TemplateData =========
|
||||||
type HomepageTD struct {
|
type HomepageTD struct {
|
||||||
Count uint
|
NTorrents uint
|
||||||
}
|
}
|
||||||
|
|
||||||
type TorrentsTD struct {
|
type TorrentsTD struct {
|
||||||
Search string
|
CanLoadMore bool
|
||||||
SubscriptionURL string
|
Query string
|
||||||
Torrents []persistence.TorrentMetadata
|
SubscriptionURL string
|
||||||
Before int64
|
Torrents []persistence.TorrentMetadata
|
||||||
After int64
|
SortedBy string
|
||||||
SortedBy string
|
NextPageExists bool
|
||||||
NextPageExists bool
|
Epoch int64
|
||||||
|
LastOrderedValue uint64
|
||||||
|
LastID uint64
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TorrentTD struct {
|
type TorrentTD struct {
|
||||||
@ -100,17 +107,21 @@ func main() {
|
|||||||
"humanizeSize": func(s uint64) string {
|
"humanizeSize": func(s uint64) string {
|
||||||
return humanize.IBytes(s)
|
return humanize.IBytes(s)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"comma": func(s uint) string {
|
||||||
|
return humanize.Comma(int64(s))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
templates = make(map[string]*template.Template)
|
templates = make(map[string]*template.Template)
|
||||||
templates["feed"] = template.Must(template.New("feed").Parse(string(mustAsset("templates/feed.xml"))))
|
// templates["feed"] = template.Must(template.New("feed").Parse(string(mustAsset("templates/feed.xml"))))
|
||||||
templates["homepage"] = template.Must(template.New("homepage").Parse(string(mustAsset("templates/homepage.html"))))
|
templates["homepage"] = template.Must(template.New("homepage").Funcs(templateFunctions).Parse(string(mustAsset("templates/homepage.html"))))
|
||||||
templates["statistics"] = template.Must(template.New("statistics").Parse(string(mustAsset("templates/statistics.html"))))
|
// templates["statistics"] = template.Must(template.New("statistics").Parse(string(mustAsset("templates/statistics.html"))))
|
||||||
templates["torrent"] = template.Must(template.New("torrent").Funcs(templateFunctions).Parse(string(mustAsset("templates/torrent.html"))))
|
// templates["torrent"] = template.Must(template.New("torrent").Funcs(templateFunctions).Parse(string(mustAsset("templates/torrent.html"))))
|
||||||
templates["torrents"] = template.Must(template.New("torrents").Funcs(templateFunctions).Parse(string(mustAsset("templates/torrents.html"))))
|
templates["torrents"] = template.Must(template.New("torrents").Funcs(templateFunctions).Parse(string(mustAsset("templates/torrents.html"))))
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
database, err = persistence.MakeDatabase("sqlite3:///home/bora/.local/share/magneticod/database.sqlite3", unsafe.Pointer(logger))
|
database, err = persistence.MakeDatabase("sqlite3:///home/bora/.local/share/magneticod/database.sqlite3", logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
@ -121,135 +132,85 @@ func main() {
|
|||||||
|
|
||||||
// DONE
|
// DONE
|
||||||
func rootHandler(w http.ResponseWriter, r *http.Request) {
|
func rootHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
count, err := database.GetNumberOfTorrents()
|
nTorrents, err := database.GetNumberOfTorrents()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
templates["homepage"].Execute(w, HomepageTD{
|
templates["homepage"].Execute(w, HomepageTD{
|
||||||
Count: count,
|
NTorrents: nTorrents,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func respondError(w http.ResponseWriter, statusCode int, format string, a ...interface{}) {
|
||||||
|
w.WriteHeader(statusCode)
|
||||||
|
w.Write([]byte(fmt.Sprintf(format, a...)))
|
||||||
|
}
|
||||||
|
|
||||||
func torrentsHandler(w http.ResponseWriter, r *http.Request) {
|
func torrentsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// TODO: Parsing URL Query is tedious and looks stupid... can we do better?
|
||||||
queryValues := r.URL.Query()
|
queryValues := r.URL.Query()
|
||||||
|
|
||||||
// Parses `before` and `after` parameters in the URL query following the conditions below:
|
var query string
|
||||||
// * `before` and `after` cannot be both supplied at the same time.
|
epoch := time.Now().Unix() // epoch, if not supplied, is NOW.
|
||||||
// * `before` -if supplied- cannot be less than or equal to zero.
|
var lastOrderedValue, lastID *uint64
|
||||||
// * `after` -if supplied- cannot be greater than the current Unix time.
|
|
||||||
// * if `before` is not supplied, it is set to the current Unix time.
|
if query = queryValues.Get("query"); query == "" {
|
||||||
qBefore, qAfter := (int64)(-1), (int64)(-1)
|
respondError(w, 400, "query is missing")
|
||||||
var err error
|
return
|
||||||
if queryValues.Get("before") != "" {
|
|
||||||
qBefore, err = strconv.ParseInt(queryValues.Get("before"), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
if qBefore <= 0 {
|
|
||||||
panic("before parameter is less than or equal to zero!")
|
|
||||||
}
|
|
||||||
} else if queryValues.Get("after") != "" {
|
|
||||||
if qBefore != -1 {
|
|
||||||
panic("both before and after supplied")
|
|
||||||
}
|
|
||||||
qAfter, err = strconv.ParseInt(queryValues.Get("after"), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
if qAfter > time.Now().Unix() {
|
|
||||||
panic("after parameter is greater than the current Unix time!")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qBefore = time.Now().Unix()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var torrents []persistence.TorrentMetadata
|
if queryValues.Get("epoch") != "" && queryValues.Get("lastOrderedValue") != "" && queryValues.Get("lastID") != "" {
|
||||||
if qBefore != -1 {
|
var err error
|
||||||
torrents, err = database.GetNewestTorrents(N_TORRENTS, qBefore)
|
|
||||||
} else {
|
epoch, err = strconv.ParseInt(queryValues.Get("epoch"), 10, 64)
|
||||||
torrents, err = database.QueryTorrents(
|
if err != nil {
|
||||||
queryValues.Get("search"),
|
respondError(w, 400, "error while parsing epoch: %s", err.Error())
|
||||||
persistence.BY_DISCOVERED_ON,
|
return
|
||||||
true,
|
}
|
||||||
false,
|
if epoch <= 0 {
|
||||||
N_TORRENTS,
|
respondError(w, 400, "epoch has to be greater than zero")
|
||||||
qAfter,
|
return
|
||||||
true,
|
}
|
||||||
)
|
|
||||||
|
*lastOrderedValue, err = strconv.ParseUint(queryValues.Get("lastOrderedValue"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
respondError(w, 400, "error while parsing lastOrderedValue: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *lastOrderedValue <= 0 {
|
||||||
|
respondError(w, 400, "lastOrderedValue has to be greater than zero")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*lastID, err = strconv.ParseUint(queryValues.Get("lastID"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
respondError(w, 400, "error while parsing lastID: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *lastID <= 0 {
|
||||||
|
respondError(w, 400, "lastID has to be greater than zero")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if !(queryValues.Get("epoch") == "" && queryValues.Get("lastOrderedValue") == "" && queryValues.Get("lastID") == "") {
|
||||||
|
respondError(w, 400, "`epoch`, `lastOrderedValue`, `lastID` must be supplied altogether, if supplied.")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
torrents, err := database.QueryTorrents(query, epoch, persistence.ByRelevance, true, 20, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
respondError(w, 400, "query error: %s", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: for testing, REMOVE
|
if torrents == nil {
|
||||||
torrents[2].HasReadme = true
|
panic("torrents is nil!!!")
|
||||||
|
|
||||||
templates["torrents"].Execute(w, TorrentsTD{
|
|
||||||
Search: "",
|
|
||||||
SubscriptionURL: "borabora",
|
|
||||||
Torrents: torrents,
|
|
||||||
Before: torrents[len(torrents)-1].DiscoveredOn,
|
|
||||||
After: torrents[0].DiscoveredOn,
|
|
||||||
SortedBy: "anan",
|
|
||||||
NextPageExists: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func newestTorrentsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
queryValues := r.URL.Query()
|
|
||||||
|
|
||||||
qBefore, qAfter := (int64)(-1), (int64)(-1)
|
|
||||||
var err error
|
|
||||||
if queryValues.Get("before") != "" {
|
|
||||||
qBefore, err = strconv.ParseInt(queryValues.Get("before"), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
} else if queryValues.Get("after") != "" {
|
|
||||||
if qBefore != -1 {
|
|
||||||
panic("both before and after supplied")
|
|
||||||
}
|
|
||||||
qAfter, err = strconv.ParseInt(queryValues.Get("after"), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qBefore = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
var torrents []persistence.TorrentMetadata
|
|
||||||
if qBefore != -1 {
|
|
||||||
torrents, err = database.QueryTorrents(
|
|
||||||
"",
|
|
||||||
persistence.BY_DISCOVERED_ON,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
N_TORRENTS,
|
|
||||||
qBefore,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
torrents, err = database.QueryTorrents(
|
|
||||||
"",
|
|
||||||
persistence.BY_DISCOVERED_ON,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
N_TORRENTS,
|
|
||||||
qAfter,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates["torrents"].Execute(w, TorrentsTD{
|
templates["torrents"].Execute(w, TorrentsTD{
|
||||||
Search: "",
|
CanLoadMore: true,
|
||||||
|
Query: query,
|
||||||
SubscriptionURL: "borabora",
|
SubscriptionURL: "borabora",
|
||||||
Torrents: torrents,
|
Torrents: torrents,
|
||||||
Before: torrents[len(torrents)-1].DiscoveredOn,
|
|
||||||
After: torrents[0].DiscoveredOn,
|
|
||||||
SortedBy: "anan",
|
SortedBy: "anan",
|
||||||
NextPageExists: true,
|
NextPageExists: true,
|
||||||
})
|
})
|
||||||
|
@ -27,8 +27,8 @@ type Database interface {
|
|||||||
orderBy orderingCriteria,
|
orderBy orderingCriteria,
|
||||||
ascending bool,
|
ascending bool,
|
||||||
limit uint,
|
limit uint,
|
||||||
lastOrderedValue *uint,
|
lastOrderedValue *uint64,
|
||||||
lastID *uint,
|
lastID *uint64,
|
||||||
) ([]TorrentMetadata, error)
|
) ([]TorrentMetadata, error)
|
||||||
// GetTorrents returns the TorrentExtMetadata for the torrent of the given InfoHash. Will return
|
// GetTorrents returns the TorrentExtMetadata for the torrent of the given InfoHash. Will return
|
||||||
// nil, nil if the torrent does not exist in the database.
|
// nil, nil if the torrent does not exist in the database.
|
||||||
|
@ -166,8 +166,8 @@ func (db *sqlite3Database) QueryTorrents(
|
|||||||
orderBy orderingCriteria,
|
orderBy orderingCriteria,
|
||||||
ascending bool,
|
ascending bool,
|
||||||
limit uint,
|
limit uint,
|
||||||
lastOrderedValue *uint,
|
lastOrderedValue *uint64,
|
||||||
lastID *uint,
|
lastID *uint64,
|
||||||
) ([]TorrentMetadata, error) {
|
) ([]TorrentMetadata, error) {
|
||||||
if query == "" && orderBy == ByRelevance {
|
if query == "" && orderBy == ByRelevance {
|
||||||
return nil, fmt.Errorf("torrents cannot be ordered by relevance when the query is empty")
|
return nil, fmt.Errorf("torrents cannot be ordered by relevance when the query is empty")
|
||||||
@ -176,8 +176,8 @@ func (db *sqlite3Database) QueryTorrents(
|
|||||||
return nil, fmt.Errorf("lastOrderedValue and lastID should be supplied together, if supplied")
|
return nil, fmt.Errorf("lastOrderedValue and lastID should be supplied together, if supplied")
|
||||||
}
|
}
|
||||||
|
|
||||||
doJoin := query != ""
|
doJoin := query != ""
|
||||||
firstPage := lastID != nil
|
firstPage := true // lastID != nil
|
||||||
|
|
||||||
// executeTemplate is used to prepare the SQL query, WITH PLACEHOLDERS FOR USER INPUT.
|
// executeTemplate is used to prepare the SQL query, WITH PLACEHOLDERS FOR USER INPUT.
|
||||||
sqlQuery := executeTemplate(`
|
sqlQuery := executeTemplate(`
|
||||||
@ -196,11 +196,11 @@ func (db *sqlite3Database) QueryTorrents(
|
|||||||
) AS idx USING(id)
|
) AS idx USING(id)
|
||||||
{{ end }}
|
{{ end }}
|
||||||
WHERE modified_on <= ?
|
WHERE modified_on <= ?
|
||||||
{{ if not FirstPage }}
|
{{ if not .FirstPage }}
|
||||||
AND id > ?
|
AND id > ?
|
||||||
AND {{ .OrderOn }} {{ GTEorLTE(.Ascending) }} ?
|
AND {{ .OrderOn }} {{ GTEorLTE .Ascending }} ?
|
||||||
{{ end }}
|
{{ end }}
|
||||||
ORDER BY {{ .OrderOn }} {{ AscOrDesc(.Ascending) }}, id ASC
|
ORDER BY {{ .OrderOn }} {{ AscOrDesc .Ascending }}, id ASC
|
||||||
LIMIT ?;
|
LIMIT ?;
|
||||||
`, struct {
|
`, struct {
|
||||||
DoJoin bool
|
DoJoin bool
|
||||||
@ -208,17 +208,17 @@ func (db *sqlite3Database) QueryTorrents(
|
|||||||
OrderOn string
|
OrderOn string
|
||||||
Ascending bool
|
Ascending bool
|
||||||
}{
|
}{
|
||||||
DoJoin: doJoin, // if there is a query, do join
|
DoJoin: doJoin,
|
||||||
FirstPage: firstPage, // lastID != nil implies that lastOrderedValue != nil as well
|
FirstPage: firstPage,
|
||||||
OrderOn: orderOn(orderBy),
|
OrderOn: orderOn(orderBy),
|
||||||
Ascending: ascending,
|
Ascending: ascending,
|
||||||
}, template.FuncMap{
|
}, template.FuncMap{
|
||||||
"GTEorLTE": func(ascending bool) string {
|
"GTEorLTE": func(ascending bool) string {
|
||||||
// TODO: or maybe vice versa idk
|
// TODO: or maybe vice versa idk
|
||||||
if ascending {
|
if ascending {
|
||||||
return "<"
|
|
||||||
} else {
|
|
||||||
return ">"
|
return ">"
|
||||||
|
} else {
|
||||||
|
return "<"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AscOrDesc": func(ascending bool) string {
|
"AscOrDesc": func(ascending bool) string {
|
||||||
@ -236,7 +236,7 @@ func (db *sqlite3Database) QueryTorrents(
|
|||||||
queryArgs = append(queryArgs, query)
|
queryArgs = append(queryArgs, query)
|
||||||
}
|
}
|
||||||
queryArgs = append(queryArgs, epoch)
|
queryArgs = append(queryArgs, epoch)
|
||||||
if firstPage {
|
if !firstPage {
|
||||||
queryArgs = append(queryArgs, lastID)
|
queryArgs = append(queryArgs, lastID)
|
||||||
queryArgs = append(queryArgs, lastOrderedValue)
|
queryArgs = append(queryArgs, lastOrderedValue)
|
||||||
}
|
}
|
||||||
@ -247,8 +247,7 @@ func (db *sqlite3Database) QueryTorrents(
|
|||||||
return nil, fmt.Errorf("error while querying torrents: %s", err.Error())
|
return nil, fmt.Errorf("error while querying torrents: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
torrents := make([]TorrentMetadata, 0)
|
||||||
var torrents []TorrentMetadata
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var torrent TorrentMetadata
|
var torrent TorrentMetadata
|
||||||
if err = rows.Scan(&torrent.InfoHash, &torrent.Name, &torrent.Size, &torrent.DiscoveredOn, &torrent.NFiles); err != nil {
|
if err = rows.Scan(&torrent.InfoHash, &torrent.Name, &torrent.Size, &torrent.DiscoveredOn, &torrent.NFiles); err != nil {
|
||||||
@ -338,12 +337,14 @@ func (db *sqlite3Database) GetFiles(infoHash []byte) ([]File, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *sqlite3Database) GetStatistics(n uint, to string) (*Statistics, error) {
|
func (db *sqlite3Database) GetStatistics(n uint, to string) (*Statistics, error) {
|
||||||
|
/*
|
||||||
to_time, granularity, err := ParseISO8601(to)
|
to_time, granularity, err := ParseISO8601(to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing @to error: %s", err.Error())
|
return nil, fmt.Errorf("parsing @to error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
*/
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -497,8 +498,8 @@ func (db *sqlite3Database) setupDatabase() error {
|
|||||||
//
|
//
|
||||||
// * Added `n_files` column to the `torrents` table.
|
// * Added `n_files` column to the `torrents` table.
|
||||||
zap.L().Warn("Updating database schema from 2 to 3... (this might take a while)")
|
zap.L().Warn("Updating database schema from 2 to 3... (this might take a while)")
|
||||||
tx.Exec(`
|
_, err = tx.Exec(`
|
||||||
CREATE VIRTUAL TABLE torrents_idx USING fts5(name, content='torrents', content_rowid='id', tokenize="porter unicode61 separators ' !""#$%&''()*+,-./:;<=>?@[\]^_` + "`" + `{|}~'");
|
CREATE VIRTUAL TABLE IF NOT EXISTS torrents_idx USING fts5(name, content='torrents', content_rowid='id', tokenize="porter unicode61 separators ' !""#$%&''()*+,-./:;<=>?@[\]^_` + "`" + `{|}~'");
|
||||||
|
|
||||||
-- Populate the index
|
-- Populate the index
|
||||||
INSERT INTO torrents_idx(rowid, name) SELECT id, name FROM torrents;
|
INSERT INTO torrents_idx(rowid, name) SELECT id, name FROM torrents;
|
||||||
@ -517,8 +518,8 @@ func (db *sqlite3Database) setupDatabase() error {
|
|||||||
|
|
||||||
-- Add column modified_on
|
-- Add column modified_on
|
||||||
ALTER TABLE torrents ADD COLUMN modified_on INTEGER;
|
ALTER TABLE torrents ADD COLUMN modified_on INTEGER;
|
||||||
|
UPDATE torrents SET modified_on = (SELECT discovered_on);
|
||||||
CREATE INDEX modified_on_index ON torrents (modified_on);
|
CREATE INDEX modified_on_index ON torrents (modified_on);
|
||||||
UPDATE torrents SET torrents.modified_on = (SELECT discovered_on);
|
|
||||||
|
|
||||||
PRAGMA user_version = 3;
|
PRAGMA user_version = 3;
|
||||||
`)
|
`)
|
||||||
|
Loading…
Reference in New Issue
Block a user