Skip to content

Commit

Permalink
working with minimal config window
Browse files Browse the repository at this point in the history
  • Loading branch information
LisaGlaser committed Jan 2, 2023
0 parents commit 281d195
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 0 deletions.
151 changes: 151 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai

from calibre.customize import InterfaceActionBase
import subprocess
### actually using the gui
### customisations for the user

class ActionSendByrmapi(InterfaceActionBase):
name = 'Send by rmapi'
description = 'Send book(s) to the remarkable tablet using rmapi'
supported_platforms = ['linux'] # Platform I tested this on
author = 'l glaser'
version = (0, 0, 5)

default_site_customization = 'rmapi ; /Books/Unread/Calibre;EPUB,PDF ; 1'

def __init__(self, path_to_plugin):
InterfaceActionBase.__init__(self, path_to_plugin)
from calibre.customize.ui import customize_plugin,plugin_customization
if not plugin_customization(self):
customize_plugin(self, self.default_site_customization)

def customization_help(self, gui=1):
# this is rather ugly; more fields input would be nicer...
message = []
message.append( "This plugin uses rmapi")
message.append( "Example (using the default values:\n" )
message.append( "rmapi ; /Books/Unread/Calibre/;EPUB,PDF" )
message.append( "You might need to restart Calibre for the changes to take effect." )
return ''.join(message)

# def config_widget(self):
# '''
# Implement this method and :meth:`save_settings` in your plugin to
# use a custom configuration dialog.

# This method, if implemented, must return a QWidget. The widget can have
# an optional method validate() that takes no arguments and is called
# immediately after the user clicks OK. Changes are applied if and only
# if the method returns True.

# If for some reason you cannot perform the configuration at this time,
# return a tuple of two strings (message, details), these will be
# displayed as a warning dialog to the user and the process will be
# aborted.

# The base class implementation of this method raises NotImplementedError
# so by default no user configuration is possible.
# '''
# # It is important to put this import statement here rather than at the
# # top of the module as importing the config class will also cause the
# # GUI libraries to be loaded, which we do not want when using calibre
# # from the command line
# from calibre_plugins.send_by_rmapi.config import ConfigWidget
# return ConfigWidget()

def about(self):
# Get the about text from a file inside the plugin zip file
# The get_resources function is a builtin function defined for all your
# plugin code. It loads files from the plugin zip file. It returns
# the bytes from the specified file.
#
# Note that if you are loading more than one file, for performance, you
# should pass a list of names to get_resources. In this case,
# get_resources will return a dictionary mapping names to bytes. Names that
# are not found in the zip file will not be in the returned dictionary.
text = get_resources('about.txt')
QMessageBox.about(self, 'About the Interface Plugin Demo',
text.decode('utf-8'))

def load_actual_plugin(self, gui):
from calibre.gui2.actions import InterfaceAction
from calibre.gui2 import error_dialog,info_dialog


class SendByrmapiAction(InterfaceAction):
name = 'Send by rmapi'
action_spec = (_('Send by rmapi'), None, None, _('Ctrl+Shift+S'))
action_type = 'current'

rmapi_path='rmapi'
remote_dir='/Books/Unread/Calibre'
formats=('epub','pdf')

def __init__(self, gui, site_customization):

InterfaceAction.__init__(self, gui, site_customization)
# parse the user's customized input
if site_customization:
user_args = site_customization.split(';')
user_args = [ usr_arg.strip() for usr_arg in user_args ]
if user_args:
self.rmapi_path=user_args[0]
self.remote_dir=user_args[1]
try:
self.formats=user_args[2].split(',')
except:
pass

def genesis(self):
self.qaction.triggered.connect(self.send_by_rmapi)

def send_by_rmapi(self, ):
rows = self.gui.library_view.selectionModel().selectedRows()
if not rows or len(rows) == 0:
return error_dialog(self.gui, 'Cannot send books',
'No books selected', show=True)
# Map the rows to book ids
ids = list(map(self.gui.library_view.model().id, rows))
count=0
errors=[]
files=[]
for book_id in ids:
count+=1
print("Sending file {} of {}".format(count,len(ids)))
path_to_file = None

print('debug -1')

for format in self.formats:
try:
path_to_file = self.gui.library_view.model().db.format_abspath(book_id, format, index_is_id=True)
except:
pass
else:
if path_to_file is not None:
break
# Confirm we have defined the path etc
print("Ready to send {0} to {1}".format(path_to_file,self.remote_dir))
p = subprocess.Popen(['rmapi','put',path_to_file,self.remote_dir],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate()
print( "Standard Output:", out)
print( "Standard Error Output:", err)
print( "Return Code:", p.returncode)
info=p.returncode
## if the transfer failed we will keep going, but save which file failed and why
if not info == 0:
errors.append(err)
files.append(path_to_file.split('/')[-1])

for f,e in zip(files,errors):
print(e)
for f,e in zip(files,errors):
error_dialog(self.gui, 'Cannot send book {}'.format(f),'rmapi sends an error \n {}'.format(e), show=True)

#end of SendByrmapiAction class

return SendByrmapiAction(gui, self.site_customization)
1 change: 1 addition & 0 deletions about.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This plugin uses the rmapi interface to send epub files directly to your remarkable tablet.
45 changes: 45 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from calibre.utils.config import JSONConfig
from qt.core import QWidget, QHBoxLayout, QLabel, QLineEdit,QPushButton

# This is where all preferences for this plugin will be stored
# Remember that this name (i.e. plugins/interface_demo) is also
# in a global namespace, so make it as unique as possible.
# You should always prefix your config file name with plugins/,
# so as to ensure you dont accidentally clobber a calibre config file
prefs = JSONConfig('plugins/send_by_rmapi')

# Set defaults
## default_site_customization = 'rmapi ; /Books/Unread/Calibre;EPUB,PDF ; 1'
prefs.defaults['command_rmapi'] = 'rmapi'
prefs.defaults['folder_on_device'] = '/Books/Unread/Calibre'


class ConfigWidget(QWidget):

def __init__(self):
QWidget.__init__(self)
self.l = QHBoxLayout()
self.setLayout(self.l)

self.label = QLabel('Command to run rmapi:')
self.l.addWidget(self.label)

self.msg0 = QLineEdit(self)
self.msg0.setText(prefs['command_rmapi'])
self.l.addWidget(self.msg0)
self.label.setBuddy(self.msg0)


self.l.addSpacing(5)
self.label = QLabel('Folder on reMarkable to send files to:')
self.l.addWidget(self.label)

self.msg1 = QLineEdit(self)
self.msg1.setText(prefs['folder_on_device'])
self.l.addWidget(self.msg1)
self.label.setBuddy(self.msg1)


def save_settings(self):
prefs['command_rmapi'] = self.msg0.text()
prefs['folder_on_device'] = self.msg1.text()
77 changes: 77 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import subprocess


class SendByrmapiAction(InterfaceAction):
name = 'Send by rmapi'
action_spec = (_('Send by rmapi'), None, None, _('Ctrl+Shift+S'))
action_type = 'current'

rmapi_path='rmapi'
remote_dir='/Books/Unread/Calibre'
formats=('epub','pdf')

def __init__(self, gui, site_customization):
InterfaceAction.__init__(self, gui, site_customization)
# parse the user's customized input
if site_customization:
user_args = site_customization.split(';')
user_args = [ usr_arg.strip() for usr_arg in user_args ]
if user_args:
self.rmapi_path=user_args[0]
self.remote_dir=user_args[1]
try:
self.formats=user_args[2].split(',')
except:
pass

def genesis(self):
self.qaction.triggered.connect(self.send_by_rmapi)

def send_by_rmapi(self, ):
rows = self.gui.library_view.selectionModel().selectedRows()
if not rows or len(rows) == 0:
return error_dialog(self.gui, 'Cannot send books',
'No books selected', show=True)
# Map the rows to book ids
ids = list(map(self.gui.library_view.model().id, rows))
count=0
errors=[]
files=[]
for book_id in ids:
count+=1
print("Sending file {} of {}".format(count,len(ids)))
path_to_file = None

print('debug -1')

for format in self.formats:
try:
path_to_file = self.gui.library_view.model().db.format_abspath(book_id, format, index_is_id=True)
except:
pass
else:
if path_to_file is not None:
break
# Confirm we have defined the path etc
print("Ready to send {0} to {1}".format(path_to_file,self.remote_dir))
p = subprocess.Popen(['rmapi','put',path_to_file,self.remote_dir],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate()
print( "Standard Output:", out)
print( "Standard Error Output:", err)
print( "Return Code:", p.returncode)
info=p.returncode
## if the transfer failed we will keep going, but save which file failed and why
if not info == 0:
errors.append(err)
files.append(path_to_file.split('/')[-1])

for f,e in zip(files,errors):
print(e)
for f,e in zip(files,errors):
error_dialog(self.gui, 'Cannot send book {}'.format(f),'rmapi sends an error \n {}'.format(e), show=True)

#end of SendByrmapiAction class

return SendByrmapiAction(gui, self.site_customization)
Empty file.

0 comments on commit 281d195

Please sign in to comment.