modified create dialog to display torrent infos detected from files
This commit is contained in:
parent
af9a8eb81b
commit
ebc7da1d05
5 changed files with 270 additions and 37 deletions
22
indexer.py
22
indexer.py
|
@ -43,8 +43,7 @@ def init():
|
||||||
def initDb():
|
def initDb():
|
||||||
connection = sqlite3.connect("torrentdb.sqlite")
|
connection = sqlite3.connect("torrentdb.sqlite")
|
||||||
c = connection.cursor()
|
c = connection.cursor()
|
||||||
c.execute('CREATE TABLE IF NOT EXISTS torrents (fileid TEXT PRIMARY KEY NOT NULL, name TEXT NOT NULL, category TEXT NOT NULL, subcategory TEXT NOT NULL, description TEXT NOT NULL);')
|
c.execute('CREATE TABLE IF NOT EXISTS torrents (fileid TEXT PRIMARY KEY NOT NULL, name TEXT NOT NULL, category TEXT NOT NULL, subcategory TEXT NOT NULL, description TEXT NOT NULL, audioquality_description TEXT NOT NULL, videoquality_description TEXT NOT NULL);')
|
||||||
c.execute('CREATE TABLE IF NOT EXISTS language_mapping (fileid TEXT NOT NULL, language TEXT NOT NULL);')
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
@ -78,8 +77,9 @@ def createNewTorrent(reuqest):
|
||||||
category = request.form["category"]
|
category = request.form["category"]
|
||||||
subcategory = request.form["subcategory"]
|
subcategory = request.form["subcategory"]
|
||||||
description = request.form["description"]
|
description = request.form["description"]
|
||||||
languages = []
|
audioquality_description = request.form["audioquality_description"]
|
||||||
newTFile = TorrentFile(safeFilename, name, category, subcategory, description, languages)
|
videoquality_description = request.form["videoquality_description"]
|
||||||
|
newTFile = TorrentFile(safeFilename, name, category, subcategory, description, audioquality_description, videoquality_description)
|
||||||
connection = sqlite3.connect("torrentdb.sqlite")
|
connection = sqlite3.connect("torrentdb.sqlite")
|
||||||
newTFile.writeToDb(connection.cursor())
|
newTFile.writeToDb(connection.cursor())
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
@ -92,21 +92,23 @@ class TorrentFile():
|
||||||
category = None
|
category = None
|
||||||
subcategory = None
|
subcategory = None
|
||||||
description = None
|
description = None
|
||||||
languages = []
|
audioquality_description = None
|
||||||
def __init__(self, fileid=fileid, name=name, category=category, subcategory=subcategory, description=description, languages=languages):
|
videoquality_description = None
|
||||||
|
def __init__(self, fileid=fileid, name=name, category=category, subcategory=subcategory, description=description, audioquality_description=audioquality_description, videoquality_description=videoquality_description):
|
||||||
self.fileid = fileid
|
self.fileid = fileid
|
||||||
self.name = name
|
self.name = name
|
||||||
self.category = category
|
self.category = category
|
||||||
self.subcategory = subcategory
|
self.subcategory = subcategory
|
||||||
self.description = description
|
self.description = description
|
||||||
self.languages = languages
|
self.audioquality_description = audioquality_description
|
||||||
|
self.videoquality_description = videoquality_description
|
||||||
|
|
||||||
def writeToDb(self, cursor):
|
def writeToDb(self, cursor):
|
||||||
c = cursor
|
c = cursor
|
||||||
b64description = base64.b64encode(self.description.encode())
|
b64description = base64.b64encode(self.description.encode())
|
||||||
c.execute("INSERT INTO torrents(fileid, name, category, subcategory, description) VALUES(:fileid, :name, :category, :subcategory, :description)", { 'fileid' : self.fileid, 'name' : self.name, 'category' : self.category, 'subcategory' : self.subcategory, 'description' : b64description })
|
b64audioquality_description = base64.b64encode(self.audioquality_description.encode())
|
||||||
for language in self.languages:
|
b64videoquality_description = base64.b64encode(self.videoquality_description.encode())
|
||||||
c.execute("INSERT INTO language_mapping(fileid, language)", { "fileid" : self.fileid, "language" : language })
|
c.execute("INSERT INTO torrents(fileid, name, category, subcategory, description, audioquality_description, videoquality_description) VALUES(:fileid, :name, :category, :subcategory, :description, :audioquality_description, :videoquality_description)", { 'fileid' : self.fileid, 'name' : self.name, 'category' : self.category, 'subcategory' : self.subcategory, 'description' : b64description , 'audioquality_description' : b64audioquality_description, 'videoquality_description' : b64videoquality_description})
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
init()
|
init()
|
||||||
|
|
|
@ -108,6 +108,15 @@ body {
|
||||||
content: ":";
|
content: ":";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detectedFiles {
|
||||||
|
max-height: 100px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detectedInfos h5,p {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,34 +2,13 @@ window.onload = function () {
|
||||||
$('.dropdown li > a').click(function(e){
|
$('.dropdown li > a').click(function(e){
|
||||||
setDropdownButtonText(this.innerHTML, this.parentElement.parentElement.parentElement) //set the display-text to the selected value
|
setDropdownButtonText(this.innerHTML, this.parentElement.parentElement.parentElement) //set the display-text to the selected value
|
||||||
fillDropdownByValue(this.innerHTML, $(".dropdown")["1"])
|
fillDropdownByValue(this.innerHTML, $(".dropdown")["1"])
|
||||||
|
this.parentElement.parentElement.parentElement.classList.remove("open");
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
customizeUploadButton()
|
customizeUploadButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
//function fillDropdownByValue(value, dropdownToFill) {
|
|
||||||
// dropdownToFill.getElementsByClassName("text")[0].innerHTML = "Subcategory"
|
|
||||||
// var dropdownToFillUl = dropdownToFill.getElementsByClassName("dropdown-menu")[0]
|
|
||||||
// dropdownToFillUl.innerHTML = ""
|
|
||||||
//
|
|
||||||
// subCategory = getDescriptorByLocalString("english", value)
|
|
||||||
// catList = document.getElementById("subcategory_list")
|
|
||||||
// catList.innerHTML = ""
|
|
||||||
// categories = global_categories[subCategory].sort()
|
|
||||||
// for(var key in categories) {
|
|
||||||
// var newEntry = document.createElement("li")
|
|
||||||
// var newEntryLink = document.createElement("a")
|
|
||||||
// newEntry.setAttribute("role", "presentation")
|
|
||||||
// newEntryLink.setAttribute("role", "menuitem")
|
|
||||||
// newEntryLink.tabIndex = -1
|
|
||||||
// newEntryLink.href = "#"
|
|
||||||
// newEntryLink.innerHTML = getLocalString("english", categories[key])
|
|
||||||
// newEntryLink.onclick = function(){ setDropdownButtonText(this.innerHTML, this.parentElement.parentElement.parentElement) }
|
|
||||||
// newEntry.appendChild(newEntryLink)
|
|
||||||
// dropdownToFillUl.appendChild(newEntry)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
function setDropdownButtonText(text, dropdownButton) {
|
function setDropdownButtonText(text, dropdownButton) {
|
||||||
var dropdownTextSpan = dropdownButton.getElementsByClassName("text")[0]
|
var dropdownTextSpan = dropdownButton.getElementsByClassName("text")[0]
|
||||||
dropdownTextSpan.innerHTML = text
|
dropdownTextSpan.innerHTML = text
|
||||||
|
@ -44,6 +23,40 @@ function sortCategories(categories) {
|
||||||
return sortedCategories
|
return sortedCategories
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fillDetectedFilelist(file) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
fileList = document.querySelectorAll(".detectedFiles ul")[0]
|
||||||
|
fileList.innerHTML = ""
|
||||||
|
reader.onload = function() {
|
||||||
|
b = new bencode()
|
||||||
|
torrentObject = b.decode(reader.result)
|
||||||
|
for(var fileIndex = 0; fileIndex < torrentObject.info.files.length; fileIndex++){
|
||||||
|
newListElement = document.createElement("li")
|
||||||
|
newListElement.innerHTML = torrentObject.info.files[fileIndex].path[0]
|
||||||
|
fileList.appendChild(newListElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll("input.name")[0].value = torrentObject.info.name
|
||||||
|
|
||||||
|
sizeGroup = document.querySelectorAll(".sizeGroup")[0]
|
||||||
|
if(torrentObject.info.pieces.length && torrentObject.info["piece length"]){
|
||||||
|
sizeGroup.style.display = ""
|
||||||
|
document.querySelectorAll(".detectedSize")[0].innerHTML = ((Math.round((torrentObject.info.pieces.length / 20) * torrentObject.info["piece length"]) / 1024 / 1024 * 100) / 100) + " MB"
|
||||||
|
} else {
|
||||||
|
sizeGroup.style.display = "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
trackerGroup = document.querySelectorAll(".trackerGroup")[0]
|
||||||
|
if(torrentObject.announce){
|
||||||
|
trackerGroup.style.display = ""
|
||||||
|
document.querySelectorAll(".detectedTracker")[0].innerHTML = torrentObject.announce
|
||||||
|
} else {
|
||||||
|
trackerGroup.style.display = "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.readAsArrayBuffer(file)
|
||||||
|
}
|
||||||
|
|
||||||
// Fill a defined dropdown with values.
|
// Fill a defined dropdown with values.
|
||||||
// These values will be generated out of the categories.json
|
// These values will be generated out of the categories.json
|
||||||
function fillDropdownByValue(value, dropdownToFill) {
|
function fillDropdownByValue(value, dropdownToFill) {
|
||||||
|
@ -63,7 +76,11 @@ function fillDropdownByValue(value, dropdownToFill) {
|
||||||
newEntryLink.tabIndex = -1
|
newEntryLink.tabIndex = -1
|
||||||
newEntryLink.href = "#"
|
newEntryLink.href = "#"
|
||||||
newEntryLink.innerHTML = subcategoryLocalString
|
newEntryLink.innerHTML = subcategoryLocalString
|
||||||
newEntryLink.onclick = function(){ setDropdownButtonText(this.innerHTML, this.parentElement.parentElement.parentElement) }
|
newEntryLink.onclick = function(e){
|
||||||
|
setDropdownButtonText(this.innerHTML, this.parentElement.parentElement.parentElement)
|
||||||
|
this.parentElement.parentElement.parentElement.classList.remove("open");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
newEntry.appendChild(newEntryLink)
|
newEntry.appendChild(newEntryLink)
|
||||||
dropdownToFillUl.appendChild(newEntry)
|
dropdownToFillUl.appendChild(newEntry)
|
||||||
}
|
}
|
||||||
|
@ -85,6 +102,7 @@ function setButtonToFilename(event) {
|
||||||
targetInput = event["target"]
|
targetInput = event["target"]
|
||||||
button = targetInput.previousSibling.getElementsByClassName("text")[0]
|
button = targetInput.previousSibling.getElementsByClassName("text")[0]
|
||||||
button.innerHTML = chunk(fileName, 40)
|
button.innerHTML = chunk(fileName, 40)
|
||||||
|
fillDetectedFilelist(this.files[0])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,3 +180,171 @@ function chunk(string, n) {
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Bencode Library
|
||||||
|
// Copyright 2014 Steven Goodwin
|
||||||
|
// Released under the GPL, version 2
|
||||||
|
|
||||||
|
// For format details, please see:
|
||||||
|
// http://en.wikipedia.org/wiki/Bencode
|
||||||
|
|
||||||
|
bencode = function() {
|
||||||
|
this.STATE_NULL = 0;
|
||||||
|
this.STATE_INTEGER = 1; // i-?[0-9]e
|
||||||
|
this.STATE_STRING_LENGTH = 2; // [0-9]+:\a+
|
||||||
|
this.STATE_STRING_CONTENT = 3; // [0-9]+:\a+
|
||||||
|
this.STATE_LIST = 4; // l<contents>e
|
||||||
|
this.STATE_DICTIONARY = 5; // d<contents>e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse accepts an array of characters to process, and the index of the
|
||||||
|
// first character to parse.
|
||||||
|
// It returns an object containing the parsed result (in result.o), and the index
|
||||||
|
// of the next character to parse in result.idx
|
||||||
|
bencode.prototype.parse = function(dataArray, fromIndex) {
|
||||||
|
var length = dataArray.byteLength;
|
||||||
|
var idx = fromIndex;
|
||||||
|
var state = this.STATE_NULL;
|
||||||
|
|
||||||
|
// State data
|
||||||
|
var current = "";
|
||||||
|
var currentObject = null;;
|
||||||
|
// String-specific state data
|
||||||
|
var stringLength;
|
||||||
|
|
||||||
|
while(idx < length) {
|
||||||
|
var c = String.fromCharCode(dataArray[idx]) ;
|
||||||
|
|
||||||
|
switch(state) {
|
||||||
|
case this.STATE_NULL:
|
||||||
|
switch(c) {
|
||||||
|
case 'i':
|
||||||
|
state = this.STATE_INTEGER;
|
||||||
|
current = "";
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
state = this.STATE_STRING_LENGTH;
|
||||||
|
current = c;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
currentObject = new Array();
|
||||||
|
state = this.STATE_LIST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
currentObject = new Object();
|
||||||
|
state = this.STATE_DICTIONARY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
++idx;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case this.STATE_INTEGER:
|
||||||
|
switch(c) {
|
||||||
|
case '-': // we assume that negative numbers start with -
|
||||||
|
current = "-";
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
return { o : current, idx : idx+1 };
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
current += c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++idx;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case this.STATE_STRING_LENGTH:
|
||||||
|
switch(c) {
|
||||||
|
case ':': // the separator between length and content
|
||||||
|
stringLength = parseInt(current, 10);
|
||||||
|
state = this.STATE_STRING_CONTENT;
|
||||||
|
current = ""; // We now parse the string content
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
current += c;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
++idx;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case this.STATE_STRING_CONTENT:
|
||||||
|
current += c;
|
||||||
|
if (--stringLength == 0) {
|
||||||
|
return { o : current, idx : idx+1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
++idx;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case this.STATE_DICTIONARY:
|
||||||
|
|
||||||
|
if (c == 'e') {
|
||||||
|
return { o : currentObject, idx : idx+1 };
|
||||||
|
} else {
|
||||||
|
var objKey = this.parse(dataArray, idx);
|
||||||
|
var objValue = this.parse(dataArray, objKey.idx);
|
||||||
|
|
||||||
|
currentObject[objKey.o] = objValue.o;
|
||||||
|
idx = objValue.idx;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case this.STATE_LIST:
|
||||||
|
|
||||||
|
if (c == 'e') {
|
||||||
|
return { o : currentObject, idx : idx+1 };
|
||||||
|
} else {
|
||||||
|
var obj = this.parse(dataArray, idx);
|
||||||
|
|
||||||
|
currentObject.push(obj.o);
|
||||||
|
idx = obj.idx;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bencode.prototype.decode = function(byteArray) {
|
||||||
|
var dataArray = new Uint8Array(byteArray);
|
||||||
|
var result = this.parse(dataArray, 0);
|
||||||
|
|
||||||
|
return result.o;
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,9 @@
|
||||||
"create" : "Create",
|
"create" : "Create",
|
||||||
"torrent_file" : "Torrent file",
|
"torrent_file" : "Torrent file",
|
||||||
"name" : "Name",
|
"name" : "Name",
|
||||||
"create_new_torrent" : "Create new torrent",
|
"create_new_torrent" : "Create a new torrent",
|
||||||
"description" : "Description"
|
"description" : "Description",
|
||||||
|
"audio_quality" : "Audio quality",
|
||||||
|
"video_quality" : "Video quality"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,26 @@ vim: ts=2 noexpandtab
|
||||||
<input name="torrentFile" class="file" type="file" size="50" maxlength="100000" accept="text/*" onchange="setButtonToFilename(event)">
|
<input name="torrentFile" class="file" type="file" size="50" maxlength="100000" accept="text/*" onchange="setButtonToFilename(event)">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputTorrentFile" class="col-sm-3 control-label">File info</label>
|
||||||
|
<div class="col-sm-9 detectedInfos">
|
||||||
|
<div class="detectedGroup sizeGroup">
|
||||||
|
<h5>Size:</h5>
|
||||||
|
<p class="detectedSize">123MB</p>
|
||||||
|
</div>
|
||||||
|
<div class="detectedGroup trackerGroup">
|
||||||
|
<h5>Tracker:</h5>
|
||||||
|
<p class="detectedTracker">http://foo.bar/tracker</p>
|
||||||
|
</div>
|
||||||
|
<div class="detectedGroup filesGroup">
|
||||||
|
<h5>Detected files:</h5>
|
||||||
|
<div class="detectedFiles">
|
||||||
|
<ul>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputCategory" class="col-sm-3 control-label">{{ getLocalString(language, "category") }}</label>
|
<label for="inputCategory" class="col-sm-3 control-label">{{ getLocalString(language, "category") }}</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
|
@ -62,6 +82,19 @@ vim: ts=2 noexpandtab
|
||||||
<input type="text" name="name" class="form-control name" placeholder="e.g. Attack of the Killer Tomatoes" aria-describedby="basic-addon1">
|
<input type="text" name="name" class="form-control name" placeholder="e.g. Attack of the Killer Tomatoes" aria-describedby="basic-addon1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputName" class="col-sm-3 control-label">{{ getLocalString(language, "audio_quality") }}</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" name="audioquality_description" class="form-control name" placeholder="e.g. English, AC-3 at 384kbps" aria-describedby="basic-addon1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputName" class="col-sm-3 control-label">{{ getLocalString(language, "video_quality") }}</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" name="videoquality_description" class="form-control name" placeholder="e.g. XviD, 720×400 at 1809 kbps" aria-describedby="basic-addon1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputName" class="col-sm-3 control-label">Audio-Quality</label>
|
<label for="inputName" class="col-sm-3 control-label">Audio-Quality</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
|
@ -84,10 +117,11 @@ vim: ts=2 noexpandtab
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputName" class="col-sm-3 control-label">{{ getLocalString(language, "description") }}</label>
|
<label for="inputName" class="col-sm-3 control-label">{{ getLocalString(language, "description") }}</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea name="description" class="form-control description" rows="10"></textarea>
|
<textarea name="description" class="form-control description" rows="10" placeholder="Hint: Markdown is supported in this field"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
Loading…
Reference in a new issue