Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Api Last Torrent + xhr automatic refresh

* Added a fiel torrentID in search param
* Search can be limited to torrentID > id provided
* Templates creation through simple JS object
* XHR management through simple JS object
* Torrents object that interface with Templates and Query to get new
torrent uploaded according to the search context
Cette révision appartient à :
akuma06 2017-05-30 00:28:21 +02:00
Parent 517b59b02f
révision 402fce9f02
9 fichiers modifiés avec 225 ajouts et 52 suppressions

Voir le fichier

@ -156,13 +156,14 @@ func (c *Category) Parse(s string) (ok bool) {
// deprecated for TorrentParam
type SearchParam struct {
Order bool // True means acsending
Status Status
Sort SortMode
Category Category
Page int
UserID uint
Max uint
NotNull string
Query string
TorrentID uint
Order bool // True means acsending
Status Status
Sort SortMode
Category Category
Page int
UserID uint
Max uint
NotNull string
Query string
}

Voir le fichier

@ -3,11 +3,12 @@ package common
import (
"context"
"encoding/json"
"github.com/gorilla/mux"
elastic "gopkg.in/olivere/elastic.v5"
"net/http"
"strconv"
"github.com/gorilla/mux"
elastic "gopkg.in/olivere/elastic.v5"
"github.com/NyaaPantsu/nyaa/config"
"github.com/NyaaPantsu/nyaa/db"
"github.com/NyaaPantsu/nyaa/model"
@ -31,6 +32,7 @@ type TorrentParam struct {
NameLike string // csv
}
// FromRequest : parse a request in torrent param
// TODO Should probably return an error ?
func (p *TorrentParam) FromRequest(r *http.Request) {
var err error
@ -54,9 +56,15 @@ func (p *TorrentParam) FromRequest(r *http.Request) {
}
// FIXME 0 means no userId defined
userId, err := strconv.ParseUint(r.URL.Query().Get("userID"), 10, 32)
userID, err := strconv.ParseUint(r.URL.Query().Get("userID"), 10, 32)
if err != nil {
userId = 0
userID = 0
}
// FIXME 0 means no userId defined
torrentID, err := strconv.ParseUint(r.URL.Query().Get("torrentID"), 10, 32)
if err != nil {
torrentID = 0
}
var status Status
@ -76,7 +84,7 @@ func (p *TorrentParam) FromRequest(r *http.Request) {
p.NameLike = nameLike
p.Offset = uint32(pagenum)
p.Max = uint32(max)
p.UserID = uint32(userId)
p.UserID = uint32(userID)
// TODO Use All
p.All = false
// TODO Use Full
@ -86,11 +94,11 @@ func (p *TorrentParam) FromRequest(r *http.Request) {
p.Sort = sortMode
p.Category = category
// FIXME 0 means no TorrentId defined
// Do we even need that ?
p.TorrentID = 0
// Do we even need that ? I do now :p
p.TorrentID = uint32(torrentID)
}
// Builds a query string with for es query string query defined here
// ToFilterQuery : Builds a query string with for es query string query defined here
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html
func (p *TorrentParam) ToFilterQuery() string {
// Don't set sub category unless main category is set
@ -109,9 +117,14 @@ func (p *TorrentParam) ToFilterQuery() string {
if p.Status != ShowAll {
query += " status:" + p.Status.ToString()
}
if p.TorrentID != 0 {
query += " id:>" + strconv.FormatInt(int64(p.TorrentID), 10)
}
return query
}
// Find :
/* Uses elasticsearch to find the torrents based on TorrentParam
* We decided to fetch only the ids from ES and then query these ids to the
* database
@ -136,7 +149,7 @@ func (p *TorrentParam) Find(client *elastic.Client) (int64, []model.Torrent, err
From(int((p.Offset-1)*p.Max)).
Size(int(p.Max)).
Sort(p.Sort.ToESField(), p.Order).
Sort("_score", false). // Don't put _score before the field sort, it messes with the sorting
Sort("_score", false). // Don't put _score before the field sort, it messes with the sorting
FetchSourceContext(fsc)
filterQueryString := p.ToFilterQuery()
@ -191,6 +204,7 @@ func (p *TorrentParam) Find(client *elastic.Client) (int64, []model.Torrent, err
}
// Clone : To clone a torrent params
func (p *TorrentParam) Clone() TorrentParam {
return TorrentParam{
Order: p.Order,

Voir le fichier

@ -52,25 +52,27 @@ function toggleLayer(elem) {
else
elem.classList.add("hide");
}
function parseAllDates() {
// Date formatting
var lang = document.getElementsByTagName("html")[0].getAttribute("lang");
var ymdOpt = { year: "numeric", month: "short", day: "numeric" };
var hmOpt = { hour: "numeric", minute: "numeric" };
// Date formatting
var lang = document.getElementsByTagName("html")[0].getAttribute("lang");
var ymdOpt = { year: "numeric", month: "short", day: "numeric" };
var hmOpt = { hour: "numeric", minute: "numeric" };
var list = document.getElementsByClassName("date-short");
for(var i in list) {
var e = list[i];
e.title = e.innerText;
e.innerText = new Date(e.innerText).toLocaleString(lang, ymdOpt);
}
var list = document.getElementsByClassName("date-short");
for(var i in list) {
var e = list[i];
e.title = e.innerText;
e.innerText = new Date(e.innerText).toLocaleString(lang, ymdOpt);
}
var list = document.getElementsByClassName("date-full");
for(var i in list) {
var e = list[i];
e.title = e.innerText;
e.innerText = new Date(e.innerText).toLocaleString(lang);
var list = document.getElementsByClassName("date-full");
for(var i in list) {
var e = list[i];
e.title = e.innerText;
e.innerText = new Date(e.innerText).toLocaleString(lang);
}
}
parseAllDates();
/*Fixed-Navbar offset fix*/
document.addEventListener("DOMContentLoaded", function(event) {
var shiftWindow = function() { scrollBy(0, -70) };

16
public/js/query.js Fichier normal
Voir le fichier

@ -0,0 +1,16 @@
var Query = {
Get: function(url, renderer, callback) {
var xhr = new XMLHttpRequest();
console.log(url)
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onload = function(e) {
if (this.status == 200) {
console.log("Getting results...")
renderer(this.response);
callback(this.response)
}
};
xhr.send();
}
}

24
public/js/template.js Fichier normal
Voir le fichier

@ -0,0 +1,24 @@
// Templates variable
var Templates = {
tmpl: [],
Add: function(templateName, template) {
this.tmpl[templateName] = template
},
Render: function(templateName, model) {
console.log(model)
return this.tmpl[templateName](model)
},
ApplyItemListRenderer: function(params) {
return function(models) {
console.log("Parsing results...")
for (var i=0; i < models.length; i++) {
var object = Templates.Render(params.templateName, models[i]);
if (params.method == "append") {
params.element.innerHTML = params.element.innerHTML + object
} else if (params.method == "prepend") {
params.element.innerHTML = object + params.element.innerHTML
}
}
};
}
};

51
public/js/torrents.js Fichier normal
Voir le fichier

@ -0,0 +1,51 @@
var Torrents = {
CanRefresh: false,
timeout: undefined,
Seconds: 3,
SearchURL: "/api/search",
Method: "prepend",
LastID: 0,
StopRefresh: function() {
clearTimeout(this.timeout)
this.timeout = undefined
},
StartRefresh: function() {
console.log("Start Refresh...")
this.timeout = setTimeout(function() {
var searchArgs = (window.location.search != "") ? window.location.search.substr(1) : ""
searchArgs = (Torrents.LastID > 0) ? "?torrentID="+Torrents.LastID+"&"+searchArgs : "?"+searchArgs
Query.Get(Torrents.SearchURL+searchArgs,
Templates.ApplyItemListRenderer({
templateName: "torrents.item", method: "prepend", element: document.getElementById("torrentListResults")
}), function(torrents) {
for (var i =0; i < torrents.length; i++) { if (Torrents.LastID < torrents[i].id) Torrents.LastID = torrents[i].id; }
parseAllDates();
Torrents.StartRefresh()
});
}, this.Seconds*1000);
}
}
document.addEventListener("DOMContentLoaded", function() {
if (Torrents.CanRefresh) {
Torrents.StartRefresh()
}
})
// Credits to mpen (StackOverflow)
function humanFileSize(bytes, si) {
var thresh = si ? 1000 : 1024;
if(Math.abs(bytes) < thresh) {
return bytes + ' B';
}
var units = si
? ['kB','MB','GB','TB','PB','EB','ZB','YB']
: ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
var u = -1;
do {
bytes /= thresh;
++u;
} while(Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1)+' '+units[u];
}

Voir le fichier

@ -8,6 +8,7 @@ import (
"time"
"github.com/NyaaPantsu/nyaa/config"
"github.com/NyaaPantsu/nyaa/model"
"github.com/NyaaPantsu/nyaa/service/user/permission"
"github.com/NyaaPantsu/nyaa/util"
"github.com/NyaaPantsu/nyaa/util/categories"
@ -193,11 +194,33 @@ var FuncMap = template.FuncMap{
return config.DefaultUserSettings[s]
},
"makeTreeViewData": func(f *filelist.FileListFolder, nestLevel int, T publicSettings.TemplateTfunc, identifierChain string) interface{} {
return struct{
Folder *filelist.FileListFolder
NestLevel int
T publicSettings.TemplateTfunc
return struct {
Folder *filelist.FileListFolder
NestLevel int
T publicSettings.TemplateTfunc
IdentifierChain string
}{ f, nestLevel, T, identifierChain }
}{f, nestLevel, T, identifierChain}
},
"lastID": func(currentUrl url.URL, torrents []model.TorrentJSON) int {
values := currentUrl.Query()
order := false
sort := "2"
if _, ok := values["order"]; ok {
order, _ = strconv.ParseBool(values["order"][0])
}
if _, ok := values["sort"]; ok {
sort = values["sort"][0]
}
lastID := 0
if sort == "2" || sort == "" {
if order {
lastID = int(torrents[len(torrents)-1].ID)
} else {
lastID = int(torrents[0].ID)
}
}
return lastID
},
}

Voir le fichier

@ -37,6 +37,7 @@ Your browser does not support the audio element.
<th class="tr-date hide-xs"><a href="{{ genSearchWithOrdering .URL "2" }}">{{call $.T "date"}}<span class="sort-arrows">{{ genSortArrows .URL "2" }}</span></a></th>
</tr>
</thead>
<tbody id="torrentListResults">
{{ range .Models}}
<tr class="torrent-info
{{if eq .Status 2}}remake{{end}}
@ -84,6 +85,40 @@ Your browser does not support the audio element.
<td class="tr-date home-td date-short hide-xs">{{.Date}}</td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{end}}
{{ define "footer_js"}}
<script type="text/javascript" src="{{ $.URL.Parse "/js/template.js" }}"></script>
<script type="text/javascript" src="{{ $.URL.Parse "/js/query.js" }}"></script>
<script type="text/javascript" src="{{ $.URL.Parse "/js/torrents.js" }}"></script>
<!-- JS Template -->
<script type="text/javascript">
Templates.Add("torrents.item", function(torrent) {
return "<tr class=\"torrent-info"+ ((torrent.status == 2) ? " remake" : ((torrent.status == 3) ? " trusted" : ((torrent.status == 3) ? " aplus" : "" )))+"\">"+
"<td class=\"tr-cat home-td\">"+
"<a href=\"{{$.URL.Parse "/search?c=" }}"+ torrent.category + "_" + torrent.sub_category +"\">"+
{{ if Sukebei }}
"<img src=\"{{ $.URL.Parse "/img/torrents/sukebei/" }}"+ torrent.category + torrent.sub_category+".png\" title=\""+ torrent.CategoryName +"\">"+
{{ else }}
"<img src=\"{{ $.URL.Parse "/img/torrents/" }}"+ torrent.sub_category +".png\" title=\""+ torrent.CategoryName +"\">"+
{{ end }}
"</a>"+
"</td>"+
"<td class=\"tr-name home-td\"><a href=\"/view/"+torrent.id+"\">"+ torrent.name +"</a></td>"+
"<td class=\"tr-links home-td\">"+
"<a href=\""+torrent.magnet +"\" title=\"{{ call $.T "magnet_link" }}\">"+
"<div class=\"magnet-icon\"></div>"+
"</a>"+(torrent.torrent != "" ? "<a href=\""+torrent.torrent+"\" title=\"{{ call $.T "torrent_file" }}\"><div class=\"download-icon\"></div></a>" : "") +
"</td>"+
"<td class=\"tr-size home-td hide-xs\">"+humanFileSize(torrent.filesize)+"</td>"+
"<td class=\"tr-se home-td hide-xs\">"+torrent.seeders+"</td>"+
"<td class=\"tr-le home-td hide-xs\">"+torrent.leechers+"</td>"+
"<td class=\"tr-dl home-td hide-xs\">"+torrent.completed+"</td>"+
"<td class=\"tr-date home-td date-short hide-xs\">"+torrent.date+"</td>"+
"</tr>";
});
Torrents.LastID = {{ lastID .URL .Models }};
</script>
{{end}}

Voir le fichier

@ -6,6 +6,7 @@ import (
"strings"
"unicode"
"unicode/utf8"
elastic "gopkg.in/olivere/elastic.v5"
"github.com/NyaaPantsu/nyaa/cache"
@ -83,23 +84,23 @@ func searchByQuery(r *http.Request, pagenum int, countAll bool, withUser bool, d
torrentParam.FromRequest(r)
totalHits, torrents, err := torrentParam.Find(client)
searchParam := common.SearchParam{
Order: torrentParam.Order,
Status: torrentParam.Status,
Sort: torrentParam.Sort,
Category: torrentParam.Category,
Page: int(torrentParam.Offset),
UserID: uint(torrentParam.UserID),
Max: uint(torrentParam.Max),
NotNull: torrentParam.NotNull,
Query: torrentParam.NameLike,
TorrentID: uint(torrentParam.TorrentID),
Order: torrentParam.Order,
Status: torrentParam.Status,
Sort: torrentParam.Sort,
Category: torrentParam.Category,
Page: int(torrentParam.Offset),
UserID: uint(torrentParam.UserID),
Max: uint(torrentParam.Max),
NotNull: torrentParam.NotNull,
Query: torrentParam.NameLike,
}
// Convert back to non-json torrents
return searchParam, torrents, int(totalHits), err
} else {
log.Errorf("Unable to create elasticsearch client: %s", err)
log.Errorf("Falling back to postgresql query")
return searchByQueryPostgres(r, pagenum, countAll, withUser, deleted)
}
log.Errorf("Unable to create elasticsearch client: %s", err)
log.Errorf("Falling back to postgresql query")
return searchByQueryPostgres(r, pagenum, countAll, withUser, deleted)
}
func searchByQueryPostgres(r *http.Request, pagenum int, countAll bool, withUser bool, deleted bool) (
@ -117,6 +118,8 @@ func searchByQueryPostgres(r *http.Request, pagenum int, countAll bool, withUser
search.Query = r.URL.Query().Get("q")
userID, _ := strconv.Atoi(r.URL.Query().Get("userID"))
search.UserID = uint(userID)
torrentID, _ := strconv.Atoi(r.URL.Query().Get("torrentID"))
search.TorrentID = uint(torrentID)
switch s := r.URL.Query().Get("s"); s {
case "1":
@ -216,6 +219,10 @@ func searchByQueryPostgres(r *http.Request, pagenum int, countAll bool, withUser
conditions = append(conditions, "uploader = ?")
parameters.Params = append(parameters.Params, search.UserID)
}
if search.TorrentID != 0 {
conditions = append(conditions, "torrent_id > ?")
parameters.Params = append(parameters.Params, search.TorrentID)
}
if search.Category.Sub != 0 {
conditions = append(conditions, "sub_category = ?")
parameters.Params = append(parameters.Params, search.Category.Sub)