magneticow: display readme!
This commit is contained in:
parent
9069506acc
commit
1ca410774d
@ -1,18 +1,163 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/boramalper/magnetico/pkg/persistence"
|
"golang.org/x/text/encoding/charmap"
|
||||||
|
"github.com/anacrolix/torrent"
|
||||||
|
"github.com/anacrolix/torrent/storage"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/boramalper/magnetico/pkg/persistence"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ApiReadmeHandler struct {
|
||||||
|
client *torrent.Client
|
||||||
|
tempdir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApiReadmeHandler() (*ApiReadmeHandler, error) {
|
||||||
|
h := new(ApiReadmeHandler)
|
||||||
|
var err error
|
||||||
|
|
||||||
|
h.tempdir, err = ioutil.TempDir("", "magneticod_")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
config := torrent.NewDefaultClientConfig()
|
||||||
|
config.ListenPort = 0
|
||||||
|
config.DefaultStorage = storage.NewFileByInfoHash(h.tempdir)
|
||||||
|
|
||||||
|
h.client, err = torrent.NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.RemoveAll(h.tempdir)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ApiReadmeHandler) Close() {
|
||||||
|
h.client.Close()
|
||||||
|
_ = os.RemoveAll(h.tempdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ApiReadmeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
infohashHex := mux.Vars(r)["infohash"]
|
||||||
|
|
||||||
|
infohash, err := hex.DecodeString(infohashHex)
|
||||||
|
if err != nil {
|
||||||
|
respondError(w, http.StatusBadRequest, "couldn't decode infohash: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := database.GetFiles(infohash)
|
||||||
|
if err != nil {
|
||||||
|
zap.L().Error("GetFiles error", zap.Error(err))
|
||||||
|
respondError(w, http.StatusInternalServerError, "Internal Server Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := false
|
||||||
|
for _, file := range files {
|
||||||
|
if strings.HasSuffix(file.Path, ".nfo") {
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
} else if strings.Contains(file.Path, "read") {
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
zap.L().Warn("README")
|
||||||
|
|
||||||
|
t, err := h.client.AddMagnet("magnet:?xt=urn:btih:" + infohashHex)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer t.Drop()
|
||||||
|
|
||||||
|
zap.L().Warn("WAITING FOR INFO")
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <- t.GotInfo():
|
||||||
|
|
||||||
|
case <- time.After(30 * time.Second):
|
||||||
|
respondError(w, http.StatusInternalServerError, "Timeout")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
zap.L().Warn("GOT INFO!")
|
||||||
|
|
||||||
|
t.CancelPieces(0, t.NumPieces())
|
||||||
|
|
||||||
|
var file *torrent.File
|
||||||
|
for _, file = range t.Files() {
|
||||||
|
filePath := file.Path()
|
||||||
|
if strings.HasSuffix(filePath, ".nfo") {
|
||||||
|
break
|
||||||
|
} else if strings.Contains(filePath, "read") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if file == nil {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel if the file is larger than 50 KiB
|
||||||
|
if file.Length() > 50 * 1024 {
|
||||||
|
w.WriteHeader(http.StatusRequestEntityTooLarge)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file.Download()
|
||||||
|
|
||||||
|
reader := file.NewReader()
|
||||||
|
// BEWARE:
|
||||||
|
// ioutil.ReadAll(reader)
|
||||||
|
// returns some adjancent garbage too, for reasons unknown...
|
||||||
|
content := make([]byte, file.Length())
|
||||||
|
_, err = io.ReadFull(reader, content)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
if strings.HasSuffix(file.Path(), ".nfo") {
|
||||||
|
content, err = charmap.CodePage437.NewDecoder().Bytes(content)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because .nfo files are right padded with \x00'es.
|
||||||
|
content = bytes.TrimRight(content, "\x00")
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text/plain;charset=UTF-8")
|
||||||
|
_, _ = w.Write(content)
|
||||||
|
}
|
||||||
|
|
||||||
func apiTorrents(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
|
// @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.
|
// at all; and if that is NOT the case, then return an error.
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
# PxPlus_IBM_VGA8
|
||||||
|
|
||||||
|
**Source:** https://int10h.org/oldschool-pc-fonts/ released under the
|
||||||
|
[CC BY-SA 4.0 license](http://creativecommons.org/licenses/by-sa/4.0/).
|
||||||
|
|
||||||
|
.woff is generated by [Font Squirrel](https://www.fontsquirrel.com/tools/webfont-generator) from
|
||||||
|
`PxPlus_IBM_VGA8.ttf`.
|
Binary file not shown.
@ -32,6 +32,22 @@ function humaniseDate(unixTime) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a fetch() that errs on anything but HTTP 2XX
|
||||||
|
// Source: https://github.com/github/fetch/issues/155#issuecomment-108288863
|
||||||
|
function myFetch(url, options) {
|
||||||
|
if (options == null) options = {}
|
||||||
|
if (options.credentials == null) options.credentials = 'same-origin'
|
||||||
|
return fetch(url, options).then(function(response) {
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return Promise.resolve(response)
|
||||||
|
} else {
|
||||||
|
var error = new Error(response.statusText || response.status)
|
||||||
|
error.response = response
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ISO 8601 week number for this date.
|
* Returns the ISO 8601 week number for this date.
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function () {
|
||||||
let infoHash = window.location.pathname.split("/")[2];
|
let infoHash = window.location.pathname.split("/")[2];
|
||||||
|
|
||||||
fetch("/api/v0.1/torrents/" + infoHash).then(x => x.json()).then(x => {
|
fetch("/api/v0.1/torrents/" + infoHash).then(x => x.json()).then(x => {
|
||||||
@ -16,7 +16,7 @@ window.onload = function() {
|
|||||||
|
|
||||||
const template = document.getElementById("main-template").innerHTML;
|
const template = document.getElementById("main-template").innerHTML;
|
||||||
document.querySelector("main").innerHTML = Mustache.render(template, {
|
document.querySelector("main").innerHTML = Mustache.render(template, {
|
||||||
name: x.name,
|
name: x.name,
|
||||||
infoHash: x.infoHash,
|
infoHash: x.infoHash,
|
||||||
sizeHumanised: fileSize(x.size),
|
sizeHumanised: fileSize(x.size),
|
||||||
discoveredOnHumanised: humaniseDate(x.discoveredOn),
|
discoveredOnHumanised: humaniseDate(x.discoveredOn),
|
||||||
@ -35,11 +35,24 @@ window.onload = function() {
|
|||||||
tree.add({
|
tree.add({
|
||||||
id: pathElems.slice(0, i + 1).join("/"),
|
id: pathElems.slice(0, i + 1).join("/"),
|
||||||
parent: i >= 1 ? pathElems.slice(0, i).join("/") : undefined,
|
parent: i >= 1 ? pathElems.slice(0, i).join("/") : undefined,
|
||||||
label: pathElems[i] + ( i === pathElems.length - 1 ? " <tt>" + fileSize(e.size) + "</tt>" : ""),
|
label: pathElems[i] + (i === pathElems.length - 1 ? " <tt>" + fileSize(e.size) + "</tt>" : ""),
|
||||||
opened: true,
|
opened: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
myFetch("/api/v0.1/torrents/" + infoHash + "/readme")
|
||||||
|
.then(response => {
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then(x => {
|
||||||
|
const readme = document.getElementById("readme");
|
||||||
|
readme.innerText = x;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
const readme = document.getElementById("readme");
|
||||||
|
readme.innerText = err;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -77,4 +77,19 @@ th {
|
|||||||
tt {
|
tt {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'PxPlus-IBM-VGA8';
|
||||||
|
src: URL('/static/fonts/PxPlus_IBM_VGA8/pxplus_ibm_vga8-webfont.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
#readme {
|
||||||
|
padding: 1em;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
font-family: 'PxPlus-IBM-VGA8', monospace;
|
||||||
|
line-height: 1em;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
}
|
||||||
|
@ -13,8 +13,6 @@
|
|||||||
<script src="/static/scripts/naturalSort-v0.8.1.js"></script>
|
<script src="/static/scripts/naturalSort-v0.8.1.js"></script>
|
||||||
<script src="/static/scripts/mustache-v2.3.0.min.js"></script>
|
<script src="/static/scripts/mustache-v2.3.0.min.js"></script>
|
||||||
<script src="/static/scripts/vanillatree-v0.0.3.js"></script>
|
<script src="/static/scripts/vanillatree-v0.0.3.js"></script>
|
||||||
<script src="/static/scripts/common.js"></script>
|
|
||||||
<script src="/static/scripts/torrent.js"></script>
|
|
||||||
|
|
||||||
<!-- Goes into <main> -->
|
<!-- Goes into <main> -->
|
||||||
<script id="main-template" type="text/x-handlebars-template">
|
<script id="main-template" type="text/x-handlebars-template">
|
||||||
@ -22,7 +20,7 @@
|
|||||||
<h2>{{ name }}</h2>
|
<h2>{{ name }}</h2>
|
||||||
<a href="magnet:?xt=urn:btih:{{ infoHash }}&dn={{ name }}">
|
<a href="magnet:?xt=urn:btih:{{ 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" />
|
title="Download this torrent using magnet"/>
|
||||||
<small>{{ infoHash }}</small>
|
<small>{{ infoHash }}</small>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -42,8 +40,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Better Files</h3>
|
<h3>Files</h3>
|
||||||
<div id="fileTree"></div>
|
<div id="fileTree"></div>
|
||||||
|
|
||||||
|
<h3>Readme</h3>
|
||||||
|
<pre id="readme">Loading...</pre>
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -56,5 +57,8 @@
|
|||||||
<main>
|
<main>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<script src="/static/scripts/common.js"></script>
|
||||||
|
<script src="/static/scripts/torrent.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -102,6 +102,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
apiReadmeHandler, err := NewApiReadmeHandler()
|
||||||
|
if err != nil {
|
||||||
|
zap.L().Fatal("Could not initialise readme handler", zap.Error(err))
|
||||||
|
}
|
||||||
|
defer apiReadmeHandler.Close()
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.HandleFunc("/",
|
router.HandleFunc("/",
|
||||||
BasicAuth(rootHandler, "magneticow"))
|
BasicAuth(rootHandler, "magneticow"))
|
||||||
@ -114,6 +120,8 @@ func main() {
|
|||||||
BasicAuth(apiTorrent, "magneticow"))
|
BasicAuth(apiTorrent, "magneticow"))
|
||||||
router.HandleFunc("/api/v0.1/torrents/{infohash:[a-f0-9]{40}}/filelist",
|
router.HandleFunc("/api/v0.1/torrents/{infohash:[a-f0-9]{40}}/filelist",
|
||||||
BasicAuth(apiFilelist, "magneticow"))
|
BasicAuth(apiFilelist, "magneticow"))
|
||||||
|
router.Handle("/api/v0.1/torrents/{infohash:[a-f0-9]{40}}/readme",
|
||||||
|
apiReadmeHandler)
|
||||||
|
|
||||||
router.HandleFunc("/feed",
|
router.HandleFunc("/feed",
|
||||||
BasicAuth(feedHandler, "magneticow"))
|
BasicAuth(feedHandler, "magneticow"))
|
||||||
@ -171,7 +179,6 @@ func main() {
|
|||||||
templates["feed"] = template.Must(template.New("feed").Funcs(templateFunctions).Parse(string(mustAsset("templates/feed.xml"))))
|
templates["feed"] = template.Must(template.New("feed").Funcs(templateFunctions).Parse(string(mustAsset("templates/feed.xml"))))
|
||||||
templates["homepage"] = template.Must(template.New("homepage").Funcs(templateFunctions).Parse(string(mustAsset("templates/homepage.html"))))
|
templates["homepage"] = template.Must(template.New("homepage").Funcs(templateFunctions).Parse(string(mustAsset("templates/homepage.html"))))
|
||||||
|
|
||||||
var err error
|
|
||||||
database, err = persistence.MakeDatabase(opts.Database, logger)
|
database, err = persistence.MakeDatabase(opts.Database, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.L().Fatal("could not access to database", zap.Error(err))
|
zap.L().Fatal("could not access to database", zap.Error(err))
|
||||||
@ -190,7 +197,7 @@ func main() {
|
|||||||
// TODO: I think there is a standard lib. function for this
|
// TODO: I think there is a standard lib. function for this
|
||||||
func respondError(w http.ResponseWriter, statusCode int, format string, a ...interface{}) {
|
func respondError(w http.ResponseWriter, statusCode int, format string, a ...interface{}) {
|
||||||
w.WriteHeader(statusCode)
|
w.WriteHeader(statusCode)
|
||||||
w.Write([]byte(fmt.Sprintf(format, a...)))
|
_, _ = w.Write([]byte(fmt.Sprintf(format, a...)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustAsset(name string) []byte {
|
func mustAsset(name string) []byte {
|
||||||
|
1
go.mod
1
go.mod
@ -22,6 +22,7 @@ require (
|
|||||||
go.uber.org/zap v1.10.0
|
go.uber.org/zap v1.10.0
|
||||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f
|
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f
|
||||||
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb
|
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb
|
||||||
|
golang.org/x/text v0.3.0
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
Loading…
Reference in New Issue
Block a user