diff --git a/cmd/magneticow/api.go b/cmd/magneticow/api.go
index 69fd709..fb96634 100644
--- a/cmd/magneticow/api.go
+++ b/cmd/magneticow/api.go
@@ -13,7 +13,7 @@ import (
"go.uber.org/zap"
)
-func apiTorrentsHandler(w http.ResponseWriter, r *http.Request) {
+func apiTorrents(w http.ResponseWriter, r *http.Request) {
// @lastOrderedValue AND @lastID are either both supplied or neither of them should be supplied
// at all; and if that is NOT the case, then return an error.
if q := r.URL.Query(); !((q.Get("lastOrderedValue") != "" && q.Get("lastID") != "") ||
@@ -29,6 +29,7 @@ func apiTorrentsHandler(w http.ResponseWriter, r *http.Request) {
Ascending *bool `schema:"ascending"`
LastOrderedValue *float64 `schema:"lastOrderedValue"`
LastID *uint64 `schema:"lastID"`
+ Limit *uint `schema:"limit"`
}
if err := decoder.Decode(&tq, r.URL.Query()); err != nil {
respondError(w, 400, "error while parsing the URL: %s", err.Error())
@@ -69,27 +70,26 @@ func apiTorrentsHandler(w http.ResponseWriter, r *http.Request) {
}
}
+ if tq.Limit == nil {
+ tq.Limit = new(uint)
+ *tq.Limit = 20
+ }
+
torrents, err := database.QueryTorrents(
*tq.Query, *tq.Epoch, orderBy,
- *tq.Ascending, N_TORRENTS, tq.LastOrderedValue, tq.LastID)
+ *tq.Ascending, *tq.Limit, tq.LastOrderedValue, tq.LastID)
if err != nil {
respondError(w, 400, "query error: %s", err.Error())
return
}
- // TODO: use plain Marshal
- jm, err := json.MarshalIndent(torrents, "", " ")
- if err != nil {
- respondError(w, 500, "json marshalling error: %s", err.Error())
- return
- }
-
- if _, err = w.Write(jm); err != nil {
- zap.L().Warn("couldn't write http.ResponseWriter", zap.Error(err))
+ w.Header().Set("Content-Type", "application/json; charset=utf-8")
+ if err = json.NewEncoder(w).Encode(torrents); err != nil {
+ zap.L().Warn("JSON encode error", zap.Error(err))
}
}
-func apiTorrentsInfohashHandler(w http.ResponseWriter, r *http.Request) {
+func apiTorrent(w http.ResponseWriter, r *http.Request) {
infohashHex := mux.Vars(r)["infohash"]
infohash, err := hex.DecodeString(infohashHex)
@@ -107,19 +107,13 @@ func apiTorrentsInfohashHandler(w http.ResponseWriter, r *http.Request) {
return
}
- // TODO: use plain Marshal
- jm, err := json.MarshalIndent(torrent, "", " ")
- if err != nil {
- respondError(w, 500, "json marshalling error: %s", err.Error())
- return
- }
-
- if _, err = w.Write(jm); err != nil {
- zap.L().Warn("couldn't write http.ResponseWriter", zap.Error(err))
+ w.Header().Set("Content-Type", "application/json; charset=utf-8")
+ if err = json.NewEncoder(w).Encode(torrent); err != nil {
+ zap.L().Warn("JSON encode error", zap.Error(err))
}
}
-func apiFilesInfohashHandler(w http.ResponseWriter, r *http.Request) {
+func apiFilelist(w http.ResponseWriter, r *http.Request) {
infohashHex := mux.Vars(r)["infohash"]
infohash, err := hex.DecodeString(infohashHex)
@@ -137,19 +131,13 @@ func apiFilesInfohashHandler(w http.ResponseWriter, r *http.Request) {
return
}
- // TODO: use plain Marshal
- jm, err := json.MarshalIndent(files, "", " ")
- if err != nil {
- respondError(w, 500, "json marshalling error: %s", err.Error())
- return
- }
-
- if _, err = w.Write(jm); err != nil {
- zap.L().Warn("couldn't write http.ResponseWriter", zap.Error(err))
+ w.Header().Set("Content-Type", "application/json; charset=utf-8")
+ if err = json.NewEncoder(w).Encode(files); err != nil {
+ zap.L().Warn("JSON encode error", zap.Error(err))
}
}
-func apiStatisticsHandler(w http.ResponseWriter, r *http.Request) {
+func apiStatistics(w http.ResponseWriter, r *http.Request) {
from := r.URL.Query().Get("from")
// TODO: use gorilla?
@@ -175,15 +163,9 @@ func apiStatisticsHandler(w http.ResponseWriter, r *http.Request) {
return
}
- // TODO: use plain Marshal
- jm, err := json.MarshalIndent(stats, "", " ")
- if err != nil {
- respondError(w, 500, "json marshalling error: %s", err.Error())
- return
- }
-
- if _, err = w.Write(jm); err != nil {
- zap.L().Warn("couldn't write http.ResponseWriter", zap.Error(err))
+ w.Header().Set("Content-Type", "application/json; charset=utf-8")
+ if err = json.NewEncoder(w).Encode(stats); err != nil {
+ zap.L().Warn("JSON encode error", zap.Error(err))
}
}
diff --git a/cmd/magneticow/data/static/scripts/common.js b/cmd/magneticow/data/static/scripts/common.js
index f935705..7c7a0bf 100644
--- a/cmd/magneticow/data/static/scripts/common.js
+++ b/cmd/magneticow/data/static/scripts/common.js
@@ -24,6 +24,14 @@ function fileSize(fileSizeInBytes) {
return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
}
+function humaniseDate(unixTime) {
+ return (new Date(unixTime * 1000)).toLocaleDateString("en-GB", {
+ day: "2-digit",
+ month: "2-digit",
+ year: "numeric"
+ });
+}
+
/**
* Returns the ISO 8601 week number for this date.
*
diff --git a/cmd/magneticow/data/static/scripts/torrent.js b/cmd/magneticow/data/static/scripts/torrent.js
index 1c9cbc9..4be0c8d 100644
--- a/cmd/magneticow/data/static/scripts/torrent.js
+++ b/cmd/magneticow/data/static/scripts/torrent.js
@@ -9,52 +9,37 @@
window.onload = function() {
- var pre_element = document.getElementsByTagName("pre")[0];
- var paths = pre_element.textContent.replace(/\s+$/, "").split("\n");
- paths.sort(naturalSort);
- paths = paths.map(function(path) { return path.split('/'); });
- pre_element.textContent = stringify(structurise(paths)).join("\n");
-};
+ let infoHash = window.location.pathname.split("/")[2];
+ fetch("/api/v0.1/torrents/" + infoHash).then(x => x.json()).then(x => {
+ document.querySelector("title").innerText = x.name + " - magneticow";
-function structurise(paths) {
- var items = [];
- for(var i = 0, l = paths.length; i < l; i++) {
- var path = paths[i];
- var name = path[0];
- var rest = path.slice(1);
- var item = null;
- for(var j = 0, m = items.length; j < m; j++) {
- if(items[j].name === name) {
- item = items[j];
- break;
+ const template = document.getElementById("main-template").innerHTML;
+ document.querySelector("main").innerHTML = Mustache.render(template, {
+ name: x.name,
+ infoHash: x.infoHash,
+ sizeHumanised: fileSize(x.size),
+ discoveredOnHumanised: humaniseDate(x.discoveredOn),
+ nFiles: x.nFiles,
+ });
+
+ fetch("/api/v0.1/torrents/" + infoHash + "/filelist").then(x => x.json()).then(x => {
+ const tree = new VanillaTree('#fileTree', {
+ placeholder: 'Loading...',
+ });
+
+ for (let e of x) {
+ let pathElems = e.path.split("/");
+
+ for (let i = 0; i < pathElems.length; i++) {
+ tree.add({
+ id: pathElems.slice(0, i + 1).join("/"),
+ parent: i >= 1 ? pathElems.slice(0, i).join("/") : undefined,
+ label: pathElems[i] + ( i === pathElems.length - 1 ? " " + fileSize(e.size) + "" : ""),
+ opened: true,
+ });
+ }
}
- }
- if(item === null) {
- item = {name: name, children: []};
- items.push(item);
- }
- if(rest.length > 0) {
- item.children.push(rest);
- }
- }
- for(i = 0, l = items.length; i < l; i++) {
- item = items[i];
- item.children = structurise(item.children);
- }
- return items;
-}
-
-
-function stringify(items) {
- var lines = [];
- for(var i = 0, l = items.length; i < l; i++) {
- var item = items[i];
- lines.push(item.name);
- var subLines = stringify(item.children);
- for(var j = 0, m = subLines.length; j < m; j++) {
- lines.push(" " + subLines[j]);
- }
- }
- return lines;
-}
+ });
+ });
+};
diff --git a/cmd/magneticow/data/static/scripts/torrents.js b/cmd/magneticow/data/static/scripts/torrents.js
index 8ab5589..44f0bc8 100644
--- a/cmd/magneticow/data/static/scripts/torrents.js
+++ b/cmd/magneticow/data/static/scripts/torrents.js
@@ -104,11 +104,7 @@ function load() {
for (let t of torrents) {
t.size = fileSize(t.size);
- t.discoveredOn = (new Date(t.discoveredOn * 1000)).toLocaleDateString("en-GB", {
- day: "2-digit",
- month: "2-digit",
- year: "numeric"
- });
+ t.discoveredOn = humaniseDate(t.discoveredOn);
ul.innerHTML += Mustache.render(template, t);
}
diff --git a/cmd/magneticow/data/static/scripts/vanillatree-v0.0.3.js b/cmd/magneticow/data/static/scripts/vanillatree-v0.0.3.js
new file mode 100644
index 0000000..202ae07
--- /dev/null
+++ b/cmd/magneticow/data/static/scripts/vanillatree-v0.0.3.js
@@ -0,0 +1,214 @@
+(function (root, factory) {
+ if (typeof define == 'function' && define.amd) {
+ define( factory );
+ } else if (typeof module === 'object' && module.exports) {
+ module.exports = factory();
+ } else {
+ root.VanillaTree = factory();
+ }
+}(this, function () {
+ "use strict";
+ // Look at the Balalaika https://github.com/finom/balalaika
+ var $=function(n,e,k,h,p,m,l,b,d,g,f,c){c=function(a,b){return new c.i(a,b)};c.i=function(a,d){k.push.apply(this,a?a.nodeType||a==n?[a]:""+a===a?/' + this.placeholder + ''
+ } else if( p = this.tree.querySelector( '.vtree-placeholder' ) ) {
+ this.tree.removeChild( p );
+ }
+ return this;
+ },
+ getLeaf: function( id, notThrow ) {
+ var leaf = $( '[data-vtree-id="' + id + '"]', this.tree )[ 0 ];
+ if( !notThrow && !leaf ) throw Error( 'No VanillaTree leaf with id "' + id + '"' )
+ return leaf;
+ },
+ getChildList: function( id ) {
+ var list,
+ parent;
+ if( id ) {
+ parent = this.getLeaf( id );
+ if( !( list = $( 'ul', parent )[ 0 ] ) ) {
+ list = parent.appendChild( create( 'ul', {
+ className: 'vtree-subtree'
+ }) );
+ }
+ } else {
+ list = this.tree;
+ }
+
+ return list;
+ },
+ add: function( options ) {
+ // see https://github.com/finom/vanillatree/issues/8
+ if(this.getLeaf( options.id,true )){return;}// don't add leaves that already exist
+ var id,
+ leaf = create( 'li', {
+ className: 'vtree-leaf'
+ }),
+ parentList = this.getChildList( options.parent );
+
+ leaf.setAttribute( 'data-vtree-id', id = options.id || Math.random() );
+
+ leaf.appendChild( create( 'span', {
+ className: 'vtree-toggle'
+ }) );
+
+ leaf.appendChild( create( 'a', {
+ className: 'vtree-leaf-label',
+ innerHTML: options.label
+ }) );
+
+ parentList.appendChild( leaf );
+
+ if( parentList !== this.tree ) {
+ parentList.parentNode.classList.add( 'vtree-has-children' );
+ }
+
+ this.leafs[ id ] = options;
+
+ if( !options.opened ) {
+ this.close( id );
+ }
+
+ if( options.selected ) {
+ this.select( id );
+ }
+
+ return this._placeholder()._dispatch( 'add', id );
+ },
+ move: function( id, parentId ) {
+ var leaf = this.getLeaf( id ),
+ oldParent = leaf.parentNode,
+ newParent = this.getLeaf( parentId, true );
+
+ if( newParent ) {
+ newParent.classList.add( 'vtree-has-children' );
+ }
+
+ this.getChildList( parentId ).appendChild( leaf );
+ oldParent.parentNode.classList.toggle( 'vtree-has-children', !!oldParent.children.length );
+
+ return this._dispatch( 'move', id );
+ },
+ remove: function( id ) {
+ var leaf = this.getLeaf( id ),
+ oldParent = leaf.parentNode;
+ oldParent.removeChild( leaf );
+ oldParent.parentNode.classList.toggle( 'vtree-has-children', !!oldParent.children.length );
+
+ return this._placeholder()._dispatch( 'remove', id );
+ },
+ open: function( id ) {
+ this.getLeaf( id ).classList.remove( 'closed' );
+ return this._dispatch( 'open', id );
+ },
+ close: function( id ) {
+ this.getLeaf( id ).classList.add( 'closed' );
+ return this._dispatch( 'close', id );
+ },
+ toggle: function( id ) {
+ return this[ this.getLeaf( id ).classList.contains( 'closed' ) ? 'open' : 'close' ]( id );
+ },
+ select: function( id ) {
+ var leaf = this.getLeaf( id );
+
+ if( !leaf.classList.contains( 'vtree-selected' ) ) {
+ $( 'li.vtree-leaf', this.tree ).forEach( function( leaf ) {
+ leaf.classList.remove( 'vtree-selected' );
+ });
+
+ leaf.classList.add( 'vtree-selected' );
+ this._dispatch( 'select', id );
+ }
+
+ return this;
+ }
+ };
+
+ return Tree;
+ // Look at the Balalaika https://github.com/finom/balalaika
+}));
diff --git a/cmd/magneticow/data/static/styles/torrent.css b/cmd/magneticow/data/static/styles/torrent.css
index 6527a40..610ac6c 100644
--- a/cmd/magneticow/data/static/styles/torrent.css
+++ b/cmd/magneticow/data/static/styles/torrent.css
@@ -37,7 +37,8 @@ header > div {
}
#title h2 {
- margin-bottom: 0px;
+ margin-bottom: 0;
+ word-break: break-all;
}
#title {
@@ -49,12 +50,6 @@ header > div {
color: inherit;
}
-pre {
- background-color: black;
- color: white;
- overflow: auto;
-}
-
table {
max-width: 700px;
width: 700px;
@@ -77,4 +72,9 @@ th {
text-align: left;
width: 1%;
+}
+
+tt {
+ font-style: italic;
+ font-size: smaller;
}
\ No newline at end of file
diff --git a/cmd/magneticow/data/static/styles/torrents.css b/cmd/magneticow/data/static/styles/torrents.css
index 5485068..e0e2c86 100644
--- a/cmd/magneticow/data/static/styles/torrents.css
+++ b/cmd/magneticow/data/static/styles/torrents.css
@@ -79,6 +79,7 @@ ul li div {
ul li div h3 {
margin: 0;
+ word-break: break-all;
}
a {
diff --git a/cmd/magneticow/data/static/styles/vanillatree-v0.0.3.css b/cmd/magneticow/data/static/styles/vanillatree-v0.0.3.css
new file mode 100644
index 0000000..683c124
--- /dev/null
+++ b/cmd/magneticow/data/static/styles/vanillatree-v0.0.3.css
@@ -0,0 +1,96 @@
+.vtree ul.vtree-subtree, .vtree li.vtree-leaf {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+ position: relative;
+}
+
+.vtree li.vtree-leaf {
+ background-position: -90px 0;
+ background-repeat: repeat-y;
+ min-height: 18px;
+ line-height: 18px;
+}
+
+.vtree li.vtree-leaf::before {
+ content: '';
+ width: 18px;
+ height: 18px;
+ position: absolute;
+ background-position: -36px 0;
+}
+
+.vtree li.vtree-leaf li.vtree-leaf {
+ margin-left: 18px;
+}
+
+.vtree li.vtree-leaf:last-child {
+ background-image: none;
+}
+
+.vtree li.vtree-leaf.closed ul.vtree-subtree {
+ display: none;
+}
+
+.vtree li.vtree-leaf.vtree-has-children > span.vtree-toggle {
+ display: block;
+ width: 18px;
+ height: 18px;
+ background-position: -72px 0;
+ position: absolute;
+ left: 0;
+ top: 0;
+}
+
+.vtree li.vtree-leaf.vtree-has-children.closed > span.vtree-toggle {
+ background-position: -54px 0;
+}
+
+.vtree a.vtree-leaf-label {
+ line-height: 18px;
+ display: inline-block;
+ vertical-align: top;
+ cursor: pointer;
+ max-width: 100%;
+ margin-left: 18px;
+ padding: 0 2px;
+}
+
+.vtree li.vtree-leaf a.vtree-leaf-label:hover {
+ background-color: #e7f4f9;
+ outline: 1px solid #d8f0fa;
+}
+
+.vtree li.vtree-leaf.vtree-selected > a.vtree-leaf-label {
+ background-color: #beebff;
+ outline: 1px solid #99defd;
+}
+
+.vtree-contextmenu {
+ position: absolute;
+ z-index: 9999999;
+ border: solid 1px #ccc;
+ background: #eee;
+ padding: 0px;
+ margin: 0px;
+ display: none;
+}
+
+.vtree-contextmenu li {
+ list-style: none;
+ padding: 1px 5px;
+ margin: 0px;
+ color: #333;
+ line-height: 20px;
+ height: 20px;
+ cursor: default;
+}
+
+.vtree-contextmenu li:hover {
+ color: #fff;
+ background-color: #3399ff;
+}
+
+.vtree li.vtree-leaf, .vtree li.vtree-leaf::before, .vtree li.vtree-leaf.vtree-has-children > span.vtree-toggle {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGwAAAASCAYAAAC+Tjt8AAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfeAgYPCDEpEBZMAAABNElEQVRYw+2YIRKDMBBFdzu9AOgOlgNwBCQKh8RyBjRXQKOYSBSSG3AAbKcajrBVzFRACiGFUPabCDI7+/j7QwYkImCdR7ezNew4D7pyHeSEccI4YZww1teElWX5Eydd16UjJ3rk0p2MvbikR6IQgnSbtTXRz+cLt/YhhCAddcZ+9uSSGub7PhRFQbrM8jwPEBGOTNjZuaSGWZYFQRBAnuekA0rXRG+tcWYuqWGICLZtQxiGkGUZHQ31OYlz66Kb1gTX2jpzXCr9rNl/lz0kIhiGAaqqgjRNUcUsIoK2bbUZNk7i3LpEU1xr6si4VPpZs19qWN/3UNc1JEmidAx1XYdgoEzkcpzHoovQTTaFTdNAHMdGvnRVmcq1+RuGiBBF0V+ZZTIX/+m42p8OFieMxQm7XsLeiBbbT7ejthwAAAAASUVORK5CYII=);
+}
diff --git a/cmd/magneticow/data/templates/torrent.html b/cmd/magneticow/data/templates/torrent.html
index e0af377..102a2ee 100644
--- a/cmd/magneticow/data/templates/torrent.html
+++ b/cmd/magneticow/data/templates/torrent.html
@@ -3,12 +3,48 @@
- {{ .T.Name }} - magnetico
+ Loading... - magneticow
+
+
-
-
+
+
+
+
+
+
+
+
+
-
-
-
- Size |
- {{ humanizeSize .T.Size }} |
-
-
- Discovered on |
- {{ unixTimeToYearMonthDay .T.DiscoveredOn }} |
-
-
- Files |
- {{ .T.NFiles }} |
-
-
-
- Files
-
- {{ range .F }}{{ .Path }}{{ "\t" }}{{ humanizeSizeF .Size }}{{ "\n" }}{{ end }}