I thought I should share a button I made that adds book files and folders to the Calibre library whether the GUI or server is running. The Calibre database cannot be modified directly from the command line when the GUI is running without the server. This script will test if the server and GUI are running and will terminate the GUI and restart if the server is off. If the server is running, the option to allow unauthenticated local connections to make changes must be enabled for this to work.
The selected files are iterated through and all files matching the book extensions are added. If a selected folder contains at least one book, the books are added to the library and the folder is deleted to the recycle bin if supported.
I attempted to account for folders that contain the source code for a book by testing if the folder name contains the word 'code', if the folder contains more than 10 items and if there is a pdf that has the words 'list' and 'hardware' or 'software'. These folders are not deleted.
The current book formats tested for are pdf, epub, mobi, azw, azw3 & chm.
The port is set to the default 8080 but can be easily changed at the top of the script, along with the location of the Calibre executable. The maximum number of files tested for before deleted a directory can also be changed at the top of the script with max_num_files.
Any thoughts/suggestions welcome
The button can be downloaded here or pasted into the toolbar:
<?xml version="1.0"?>
<button backcol="none" display="both" icon_size="large" label_pos="right" textcol="none">
<label>Calibre+</label>
<icon1>#newcommand</icon1>
<function type="script">
<instruction>@script JScript</instruction>
<instruction>var calibre_location = "C:\\Program Files\\Calibre2\\";</instruction>
<instruction>var server_port = "8080";</instruction>
<instruction />
<instruction>var max_num_files = 10;</instruction>
<instruction />
<instruction>function OnClick(clickData) {</instruction>
<instruction />
<instruction> DOpus.ClearOutput();</instruction>
<instruction />
<instruction> var num_books = 0;</instruction>
<instruction> //----------------------------</instruction>
<instruction> //Test if server is running</instruction>
<instruction> //----------------------------</instruction>
<instruction> var server_switch = false;</instruction>
<instruction> var objShell = new ActiveXObject("WScript.Shell");</instruction>
<instruction> var objExec = objShell.Exec("%comspec% /c netstat -a -n | find \"LISTENING\" | find \"" + server_port + '"');</instruction>
<instruction> var port_str = objExec.StdOut.ReadAll();</instruction>
<instruction />
<instruction> if (port_str.indexOf(server_port) >= 0) {</instruction>
<instruction> server_switch = true;</instruction>
<instruction> DOpus.Output("Server on");</instruction>
<instruction> }</instruction>
<instruction />
<instruction> //------------------------------------------------</instruction>
<instruction> //Terminate gui if server isn't running</instruction>
<instruction> //------------------------------------------------</instruction>
<instruction> if (!server_switch) {</instruction>
<instruction> var gui_switch = false;</instruction>
<instruction> var wmi = GetObject("winmgmts:");</instruction>
<instruction> var proc = wmi.execquery("select * from Win32_process where Name='calibre.exe'");</instruction>
<instruction />
<instruction> var proc_itm</instruction>
<instruction> var enumProc = new Enumerator(proc);</instruction>
<instruction> for (; !enumProc.atEnd(); enumProc.moveNext()) {</instruction>
<instruction> proc_itm = enumProc.item();</instruction>
<instruction> proc_itm.terminate();</instruction>
<instruction> DOpus.delay(2000);</instruction>
<instruction> DOpus.Output("Calibre Terminated");</instruction>
<instruction> gui_switch = true;</instruction>
<instruction> }</instruction>
<instruction> }</instruction>
<instruction> //------------------------------------</instruction>
<instruction> //Process selected files and folders</instruction>
<instruction> //------------------------------------</instruction>
<instruction> var cmd = clickData.func.command;</instruction>
<instruction> cmd.deselect = false;</instruction>
<instruction> var selected_dirs = clickData.func.sourcetab.selected_dirs;</instruction>
<instruction> var selected_files = clickData.func.sourcetab.selected_files;</instruction>
<instruction />
<instruction> //------------------------------</instruction>
<instruction> //Ensure files or folders selected</instruction>
<instruction> //------------------------------</instruction>
<instruction> if ((selected_dirs.count < 1) && (selected_files.count < 1)) {</instruction>
<instruction> DOpus.Output("No directories or files selected");</instruction>
<instruction> return;</instruction>
<instruction> }</instruction>
<instruction />
<instruction> var book_exts = [".pdf", ".azw", ".azw3", ".epub", ".mobi", ".chm"];</instruction>
<instruction />
<instruction> var exec = DOpus.Create.Command();</instruction>
<instruction />
<instruction> //-----------------------------------------</instruction>
<instruction> //Add selected books to calibre and delete</instruction>
<instruction> //-----------------------------------------</instruction>
<instruction> var book_files = 0</instruction>
<instruction> if (selected_files.count > 0) {</instruction>
<instruction> var itm</instruction>
<instruction> for (var eItems = new Enumerator(selected_files); !eItems.atEnd(); eItems.moveNext()) {</instruction>
<instruction> itm = eItems.item();</instruction>
<instruction> DOpus.Output(itm.ext);</instruction>
<instruction> for (var i = 0; i < book_exts.length; i++) {</instruction>
<instruction> if (book_exts[i] == itm.ext.toLowerCase()) {</instruction>
<instruction> var objExec = objShell.Exec(PathBuilder(itm.realpath, server_switch));</instruction>
<instruction> while (objExec.Status == 0) {</instruction>
<instruction> DOpus.Delay(100);</instruction>
<instruction> }</instruction>
<instruction> exec.RunCommand("Delete RECYCLE \"" + itm.realpath + "\" QUIET");</instruction>
<instruction> DOpus.Output(itm.name + " added to calibre");</instruction>
<instruction> }</instruction>
<instruction> }</instruction>
<instruction> }</instruction>
<instruction> }</instruction>
<instruction />
<instruction> //----------------------------------</instruction>
<instruction> //Add folders with books and delete</instruction>
<instruction> //----------------------------------</instruction>
<instruction />
<instruction> if (selected_dirs.count > 0){</instruction>
<instruction> //Loop through selected dirs</instruction>
<instruction> for (var dirs = new Enumerator(selected_dirs); !dirs.atEnd(); dirs.moveNext()){</instruction>
<instruction> var dir = dirs.item();</instruction>
<instruction />
<instruction> //Test if 'code' is in the folder name</instruction>
<instruction> var code_in_name = false;</instruction>
<instruction> code_in_name = (dir.name.toLowerCase().indexOf('code') > -1);</instruction>
<instruction> if (code_in_name){</instruction>
<instruction> DOpus.Output("Folder name contains 'code'" + dir.name)</instruction>
<instruction> }</instruction>
<instruction />
<instruction> var book_found = false;</instruction>
<instruction> var files_test = false;</instruction>
<instruction> var code_dir = false;</instruction>
<instruction />
<instruction> var folderEnum = DOpus.FSUtil.ReadDir(dir.realpath, 'r');</instruction>
<instruction />
<instruction> //Test if number of files is greater than max_num_files</instruction>
<instruction> var enumTest = DOpus.FSUtil.ReadDir(dir.realpath, 'r');</instruction>
<instruction> var files_test = TooManyFiles(enumTest);</instruction>
<instruction> if (files_test){</instruction>
<instruction> DOpus.Output("Folder with more than " + max_num_files + " found - " + dir.name);</instruction>
<instruction> }</instruction>
<instruction />
<instruction> //Loop through folder</instruction>
<instruction> book_search:</instruction>
<instruction> while (!folderEnum.complete) {</instruction>
<instruction />
<instruction> var folder_itm = folderEnum.Next();</instruction>
<instruction />
<instruction> var file_name = folder_itm.name.toLowerCase();</instruction>
<instruction> for (var j = 0; j < book_exts.length; j++){</instruction>
<instruction> //Ignore files with name containing 'list' and 'hardware' or 'software'</instruction>
<instruction> if ((file_name.indexOf("list") > -1) && ((folder_name.indexOf("software") > -1) || (folder_name.indexOf("hardware") > -1))){</instruction>
<instruction> var code_dir = true;</instruction>
<instruction> DOpus.Output("Not adding Software Hardware List file");</instruction>
<instruction> continue book_search;</instruction>
<instruction> }</instruction>
<instruction> if (book_exts[j] == folder_itm.ext.toLowerCase()){</instruction>
<instruction> book_found = true;</instruction>
<instruction> DOpus.Output("Book found - " + folder_itm.name);</instruction>
<instruction> var objExec2 = objShell.Exec(PathBuilder(folder_itm.realpath, server_switch));</instruction>
<instruction> while (objExec2.status == 0){</instruction>
<instruction> DOpus.Delay(100);</instruction>
<instruction> }</instruction>
<instruction> }</instruction>
<instruction> }</instruction>
<instruction> }</instruction>
<instruction> if ((book_found)&&(!code_dir)&&(!files_test)&&(!code_in_name)) {</instruction>
<instruction> DOpus.Output(dir.name + " deleted");</instruction>
<instruction> exec.RunCommand("Delete RECYCLE \"" + dir.realpath + "\" QUIET");</instruction>
<instruction> }</instruction>
<instruction> }</instruction>
<instruction> }</instruction>
<instruction> //-----------------------------------------</instruction>
<instruction> //Restart Calibre process if already running</instruction>
<instruction> //-----------------------------------------</instruction>
<instruction> if (gui_switch){</instruction>
<instruction> objShell.Run(calibre_location + "calibre.exe", 2, false);</instruction>
<instruction> }</instruction>
<instruction>}</instruction>
<instruction />
<instruction>//------------------------------------------------</instruction>
<instruction>function PathBuilder(s_path, s_switch) {</instruction>
<instruction> var calibre_path = calibre_location + "calibredb add -d " + "\"" + s_path + "\"";</instruction>
<instruction> if (s_switch) {</instruction>
<instruction> var path_build = calibre_path + " --with-library \"http://127.0.0.1:" + server_port + "\"";</instruction>
<instruction> }</instruction>
<instruction> if (!s_switch) {</instruction>
<instruction> var path_build = calibre_path;</instruction>
<instruction> }</instruction>
<instruction> return path_build;</instruction>
<instruction>}</instruction>
<instruction />
<instruction />
<instruction>//---------------------------------------------------</instruction>
<instruction>function TooManyFiles(dir_enum){</instruction>
<instruction> var file_vector = dir_enum.Next(-1);</instruction>
<instruction> if (file_vector.count > max_num_files){</instruction>
<instruction> return true;</instruction>
<instruction> }</instruction>
<instruction> return false;</instruction>
<instruction>}</instruction>
</function>
</button>
For those interested, the source code:
var calibre_location = "C:\\Program Files\\Calibre2\\";
var server_port = "8080";
var max_num_files = 10;
function OnClick(clickData) {
DOpus.ClearOutput();
var num_books = 0;
//----------------------------
//Test if server is running
//----------------------------
var server_switch = false;
var objShell = new ActiveXObject("WScript.Shell");
var objExec = objShell.Exec("%comspec% /c netstat -a -n | find \"LISTENING\" | find \"" + server_port + '"');
var port_str = objExec.StdOut.ReadAll();
if (port_str.indexOf(server_port) >= 0) {
server_switch = true;
DOpus.Output("Server on");
}
//------------------------------------------------
//Terminate gui if server isn't running
//------------------------------------------------
if (!server_switch) {
var gui_switch = false;
var wmi = GetObject("winmgmts:");
var proc = wmi.execquery("select * from Win32_process where Name='calibre.exe'");
var proc_itm
var enumProc = new Enumerator(proc);
for (; !enumProc.atEnd(); enumProc.moveNext()) {
proc_itm = enumProc.item();
proc_itm.terminate();
DOpus.delay(2000);
DOpus.Output("Calibre Terminated");
gui_switch = true;
}
}
//------------------------------------
//Process selected files and folders
//------------------------------------
var cmd = clickData.func.command;
cmd.deselect = false;
var selected_dirs = clickData.func.sourcetab.selected_dirs;
var selected_files = clickData.func.sourcetab.selected_files;
//------------------------------
//Ensure files or folders selected
//------------------------------
if ((selected_dirs.count < 1) && (selected_files.count < 1)) {
DOpus.Output("No directories or files selected");
return;
}
var book_exts = [".pdf", ".azw", ".azw3", ".epub", ".mobi", ".chm"];
var exec = DOpus.Create.Command();
//-----------------------------------------
//Add selected books to calibre and delete
//-----------------------------------------
var book_files = 0
if (selected_files.count > 0) {
var itm
for (var eItems = new Enumerator(selected_files); !eItems.atEnd(); eItems.moveNext()) {
itm = eItems.item();
DOpus.Output(itm.ext);
for (var i = 0; i < book_exts.length; i++) {
if (book_exts[i] == itm.ext.toLowerCase()) {
var objExec = objShell.Exec(PathBuilder(itm.realpath, server_switch));
while (objExec.Status == 0) {
DOpus.Delay(100);
}
exec.RunCommand("Delete RECYCLE \"" + itm.realpath + "\" QUIET");
DOpus.Output(itm.name + " added to calibre");
}
}
}
}
//----------------------------------
//Add folders with books and delete
//----------------------------------
if (selected_dirs.count > 0){
//Loop through selected dirs
for (var dirs = new Enumerator(selected_dirs); !dirs.atEnd(); dirs.moveNext()){
var dir = dirs.item();
//Test if 'code' is in the folder name
var code_in_name = false;
code_in_name = (dir.name.toLowerCase().indexOf('code') > -1);
if (code_in_name){
DOpus.Output("Folder name contains 'code'" + dir.name)
}
var book_found = false;
var files_test = false;
var code_dir = false;
var folderEnum = DOpus.FSUtil.ReadDir(dir.realpath, 'r');
//Test if number of files is greater than max_num_files
var enumTest = DOpus.FSUtil.ReadDir(dir.realpath, 'r');
var files_test = TooManyFiles(enumTest);
if (files_test){
DOpus.Output("Folder with more than " + max_num_files + " found - " + dir.name);
}
//Loop through folder
book_search:
while (!folderEnum.complete) {
var folder_itm = folderEnum.Next();
var file_name = folder_itm.name.toLowerCase();
for (var j = 0; j < book_exts.length; j++){
//Ignore files with name containing 'list' and 'hardware' or 'software'
if ((file_name.indexOf("list") > -1) && ((folder_name.indexOf("software") > -1) || (folder_name.indexOf("hardware") > -1))){
var code_dir = true;
DOpus.Output("Not adding Software Hardware List file");
continue book_search;
}
if (book_exts[j] == folder_itm.ext.toLowerCase()){
book_found = true;
DOpus.Output("Book found - " + folder_itm.name);
var objExec2 = objShell.Exec(PathBuilder(folder_itm.realpath, server_switch));
while (objExec2.status == 0){
DOpus.Delay(100);
}
}
}
}
if ((book_found)&&(!code_dir)&&(!files_test)&&(!code_in_name)) {
DOpus.Output(dir.name + " deleted");
exec.RunCommand("Delete RECYCLE \"" + dir.realpath + "\" QUIET");
}
}
}
//-----------------------------------------
//Restart Calibre process if already running
//-----------------------------------------
if (gui_switch){
objShell.Run(calibre_location + "calibre.exe", 2, false);
}
}
//------------------------------------------------
function PathBuilder(s_path, s_switch) {
var calibre_path = calibre_location + "calibredb add -d " + "\"" + s_path + "\"";
if (s_switch) {
var path_build = calibre_path + " --with-library \"http://127.0.0.1:" + server_port + "\"";
}
if (!s_switch) {
var path_build = calibre_path;
}
return path_build;
}
//---------------------------------------------------
function TooManyFiles(dir_enum){
var file_vector = dir_enum.Next(-1);
if (file_vector.count > max_num_files){
return true;
}
return false;
}