import items
This commit is contained in:
parent
e41942ea99
commit
823ae2a676
11 changed files with 181 additions and 36 deletions
5
README
5
README
|
@ -21,6 +21,11 @@ you need a working python 2.7.x installation and the following packages:
|
||||||
python-simplejson python-lxml
|
python-simplejson python-lxml
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
On Linux you need to always install:
|
||||||
|
|
||||||
|
apt-get install \
|
||||||
|
python-imaging python-lxml ghostscript
|
||||||
|
|
||||||
== Development ==
|
== Development ==
|
||||||
|
|
||||||
mkdir client
|
mkdir client
|
||||||
|
|
|
@ -56,6 +56,15 @@ class Changelog(db.Model):
|
||||||
def timestamp(self):
|
def timestamp(self):
|
||||||
return self.created.strftime('%s')
|
return self.created.strftime('%s')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def apply_changes(cls, user, changes):
|
||||||
|
for change in changes:
|
||||||
|
if not Changelog.apply_change(user, change):
|
||||||
|
print 'FAIL', change
|
||||||
|
break
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def apply_change(cls, user, change, rebuild=False):
|
def apply_change(cls, user, change, rebuild=False):
|
||||||
revision, timestamp, sig, data = change
|
revision, timestamp, sig, data = change
|
||||||
|
|
|
@ -183,6 +183,8 @@ actions.register(scan, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def _import(request):
|
def _import(request):
|
||||||
state.main.add_callback(state.websockets[0].put, json.dumps(['import', {}]))
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
|
print 'api.import', data
|
||||||
|
state.main.add_callback(state.websockets[0].put, json.dumps(['import', data]))
|
||||||
return {}
|
return {}
|
||||||
actions.register(_import, 'import', cache=False)
|
actions.register(_import, 'import', cache=False)
|
||||||
|
|
|
@ -12,13 +12,15 @@ from app import app
|
||||||
import settings
|
import settings
|
||||||
from settings import db
|
from settings import db
|
||||||
from item.models import File
|
from item.models import File
|
||||||
from user.models import User
|
from user.models import User, List
|
||||||
|
|
||||||
from changelog import Changelog
|
from changelog import Changelog
|
||||||
|
|
||||||
import media
|
import media
|
||||||
from websocket import trigger_event
|
from websocket import trigger_event
|
||||||
|
|
||||||
|
extensions = ['epub', 'pdf', 'txt']
|
||||||
|
|
||||||
def remove_missing():
|
def remove_missing():
|
||||||
dirty = False
|
dirty = False
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
@ -49,7 +51,6 @@ def run_scan():
|
||||||
prefix += '/'
|
prefix += '/'
|
||||||
user = User.get_or_create(settings.USER_ID)
|
user = User.get_or_create(settings.USER_ID)
|
||||||
assert isinstance(prefix, unicode)
|
assert isinstance(prefix, unicode)
|
||||||
extensions = ['pdf', 'epub', 'txt']
|
|
||||||
books = []
|
books = []
|
||||||
for root, folders, files in os.walk(prefix):
|
for root, folders, files in os.walk(prefix):
|
||||||
for f in files:
|
for f in files:
|
||||||
|
@ -62,8 +63,7 @@ def run_scan():
|
||||||
books.append(f)
|
books.append(f)
|
||||||
|
|
||||||
trigger_event('scan', {
|
trigger_event('scan', {
|
||||||
'path': prefix,
|
'progress': [0, len(books)],
|
||||||
'files': len(books)
|
|
||||||
})
|
})
|
||||||
position = 0
|
position = 0
|
||||||
added = 0
|
added = 0
|
||||||
|
@ -96,29 +96,37 @@ def run_scan():
|
||||||
item.scrape()
|
item.scrape()
|
||||||
added += 1
|
added += 1
|
||||||
trigger_event('scan', {
|
trigger_event('scan', {
|
||||||
'position': position,
|
|
||||||
'length': len(books),
|
|
||||||
'path': path,
|
|
||||||
'progress': position/len(books),
|
|
||||||
'added': added,
|
'added': added,
|
||||||
|
'progress': [position, len(books)],
|
||||||
|
'path': path,
|
||||||
})
|
})
|
||||||
trigger_event('scan', {
|
trigger_event('scan', {
|
||||||
'progress': 1,
|
'progress': [position, len(books)],
|
||||||
'added': added,
|
'added': added,
|
||||||
'done': True
|
'status': {'code': 200, 'text': ''}
|
||||||
})
|
})
|
||||||
|
|
||||||
def run_import():
|
def run_import(options=None):
|
||||||
|
options = options or {}
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
prefs = settings.preferences
|
prefs = settings.preferences
|
||||||
prefix = os.path.expanduser(prefs['importPath'])
|
prefix = options.get('path', os.path.expanduser(prefs['importPath']))
|
||||||
prefix_books = os.path.join(os.path.expanduser(prefs['libraryPath']), 'Books/')
|
prefix_books = os.path.join(os.path.expanduser(prefs['libraryPath']), 'Books/')
|
||||||
prefix_imported = os.path.join(prefix_books, 'Imported/')
|
prefix_imported = os.path.join(prefix_books, 'Imported/')
|
||||||
if not prefix[-1] == '/':
|
if not prefix[-1] == '/':
|
||||||
prefix += '/'
|
prefix += '/'
|
||||||
|
|
||||||
|
if not os.path.exists(prefix):
|
||||||
|
trigger_event('import', {
|
||||||
|
'progress': [0, 0],
|
||||||
|
'status': {'code': 404, 'text': 'path not found'}
|
||||||
|
})
|
||||||
user = User.get_or_create(settings.USER_ID)
|
user = User.get_or_create(settings.USER_ID)
|
||||||
|
listname = options.get('list')
|
||||||
|
if listname:
|
||||||
|
listitems = []
|
||||||
assert isinstance(prefix, unicode)
|
assert isinstance(prefix, unicode)
|
||||||
extensions = ['pdf', 'epub', 'txt']
|
|
||||||
books = []
|
books = []
|
||||||
for root, folders, files in os.walk(prefix):
|
for root, folders, files in os.walk(prefix):
|
||||||
for f in files:
|
for f in files:
|
||||||
|
@ -131,8 +139,7 @@ def run_import():
|
||||||
books.append(f)
|
books.append(f)
|
||||||
|
|
||||||
trigger_event('import', {
|
trigger_event('import', {
|
||||||
'path': prefix,
|
'progress': [0, len(books)],
|
||||||
'files': len(books)
|
|
||||||
})
|
})
|
||||||
position = 0
|
position = 0
|
||||||
added = 0
|
added = 0
|
||||||
|
@ -145,7 +152,10 @@ def run_import():
|
||||||
f_import = f
|
f_import = f
|
||||||
f = f.replace(prefix, prefix_imported)
|
f = f.replace(prefix, prefix_imported)
|
||||||
ox.makedirs(os.path.dirname(f))
|
ox.makedirs(os.path.dirname(f))
|
||||||
|
if options.get('mode') == 'move':
|
||||||
shutil.move(f_import, f)
|
shutil.move(f_import, f)
|
||||||
|
else:
|
||||||
|
shutil.copy(f_import, f)
|
||||||
path = f[len(prefix_books):]
|
path = f[len(prefix_books):]
|
||||||
data = media.metadata(f)
|
data = media.metadata(f)
|
||||||
ext = f.split('.')[-1]
|
ext = f.split('.')[-1]
|
||||||
|
@ -167,16 +177,19 @@ def run_import():
|
||||||
item.meta['mainid']: item.meta[item.meta['mainid']]
|
item.meta['mainid']: item.meta[item.meta['mainid']]
|
||||||
})
|
})
|
||||||
item.scrape()
|
item.scrape()
|
||||||
|
if listname:
|
||||||
|
listitems.append(item.id)
|
||||||
added += 1
|
added += 1
|
||||||
trigger_event('import', {
|
trigger_event('import', {
|
||||||
'position': position,
|
'progress': [position, len(books)],
|
||||||
'length': len(books),
|
|
||||||
'path': path,
|
'path': path,
|
||||||
'progress': position/len(books),
|
|
||||||
'added': added,
|
'added': added,
|
||||||
})
|
})
|
||||||
|
if listname:
|
||||||
|
l = List.get_or_create(settings.USER_ID, listname)
|
||||||
|
l.add_items(listitems)
|
||||||
trigger_event('import', {
|
trigger_event('import', {
|
||||||
'progress': 1,
|
'progress': [position, len(books)],
|
||||||
|
'status': {'code': 200, 'text': ''},
|
||||||
'added': added,
|
'added': added,
|
||||||
'done': True
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -46,6 +46,8 @@ def info(epub):
|
||||||
isbn = extract_isbn(text)
|
isbn = extract_isbn(text)
|
||||||
if isbn:
|
if isbn:
|
||||||
data['isbn'] = isbn
|
data['isbn'] = isbn
|
||||||
|
if 'date' in data and 'T' in data['date']:
|
||||||
|
data['date'] = data['date'].split('T')[0]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def extract_text(path):
|
def extract_text(path):
|
||||||
|
|
|
@ -29,9 +29,8 @@ def api_pullChanges(app, remote_id, user_id=None, from_=None, to=None):
|
||||||
|
|
||||||
def api_pushChanges(app, user_id, changes):
|
def api_pushChanges(app, user_id, changes):
|
||||||
user = User.get(user_id)
|
user = User.get(user_id)
|
||||||
for change in changes:
|
if not Changelog.apply_changes(user, changes):
|
||||||
if not Changelog.apply_change(user, change):
|
print 'FAILED TO APPLY CHANGE'
|
||||||
print 'FAILED TO APPLY CHANGE', change
|
|
||||||
state.nodes.queue(user_id, 'pullChanges')
|
state.nodes.queue(user_id, 'pullChanges')
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -164,12 +164,7 @@ class Node(object):
|
||||||
changes = self.request('pullChanges', from_revision)
|
changes = self.request('pullChanges', from_revision)
|
||||||
if not changes:
|
if not changes:
|
||||||
return False
|
return False
|
||||||
for change in changes:
|
return Changelog.apply_changes(self.user, changes)
|
||||||
if not Changelog.apply_change(self.user, change):
|
|
||||||
print 'FAIL', change
|
|
||||||
break
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def pushChanges(self, changes):
|
def pushChanges(self, changes):
|
||||||
print 'pushing changes to', self.user_id, changes
|
print 'pushing changes to', self.user_id, changes
|
||||||
|
|
|
@ -33,7 +33,7 @@ class Background:
|
||||||
if action == 'ping':
|
if action == 'ping':
|
||||||
self.post(['pong', data])
|
self.post(['pong', data])
|
||||||
elif action == 'import':
|
elif action == 'import':
|
||||||
item.scan.run_import()
|
item.scan.run_import(data)
|
||||||
elif action == 'scan':
|
elif action == 'scan':
|
||||||
item.scan.run_scan()
|
item.scan.run_scan()
|
||||||
elif action == 'update':
|
elif action == 'update':
|
||||||
|
|
117
static/js/importDialog.js
Normal file
117
static/js/importDialog.js
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
oml.ui.importDialog = function() {
|
||||||
|
|
||||||
|
var ui = oml.user.ui,
|
||||||
|
username = oml.user.preferences.username,
|
||||||
|
|
||||||
|
lists = ui._lists.filter(function(list) {
|
||||||
|
return list.user == username && list.type == 'static';
|
||||||
|
}),
|
||||||
|
items = [
|
||||||
|
{id: ':', title: Ox._('Library')}
|
||||||
|
].concat(
|
||||||
|
lists.length ? [{}] : []
|
||||||
|
).concat(
|
||||||
|
lists.map(function(list) {
|
||||||
|
return {id: list.id, title: list.name};
|
||||||
|
})
|
||||||
|
).concat([
|
||||||
|
{},
|
||||||
|
{id: '', title: Ox._('New List...')}
|
||||||
|
]),
|
||||||
|
|
||||||
|
listNames = ui._lists.filter(function(list) {
|
||||||
|
return list.user == username;
|
||||||
|
}).map(function(list) {
|
||||||
|
return list.name;
|
||||||
|
}),
|
||||||
|
|
||||||
|
$form = Ox.Form({
|
||||||
|
items: [
|
||||||
|
Ox.Input({
|
||||||
|
changeOnKeypress: true,
|
||||||
|
id: 'path',
|
||||||
|
label: 'Source Path',
|
||||||
|
labelWidth: 128,
|
||||||
|
width: 480
|
||||||
|
}),
|
||||||
|
Ox.SelectInput({
|
||||||
|
id: 'list',
|
||||||
|
inputValue: oml.validateName(Ox._('Untitled'), listNames),
|
||||||
|
inputWidth: 224,
|
||||||
|
items: items,
|
||||||
|
label: 'Destination',
|
||||||
|
labelWidth: 128,
|
||||||
|
max: 1,
|
||||||
|
min: 1,
|
||||||
|
value: ':',
|
||||||
|
width: 480
|
||||||
|
}),
|
||||||
|
Ox.Select({
|
||||||
|
id: 'action',
|
||||||
|
items: [
|
||||||
|
{id: 'copy', title: Ox._('Keep files in source path')},
|
||||||
|
{id: 'move', title: Ox._('Remove files from source path')}
|
||||||
|
],
|
||||||
|
label: Ox._('Import Mode'),
|
||||||
|
labelWidth: 128,
|
||||||
|
width: 480
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.css({margin: '16px'})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
var values = $form.values();
|
||||||
|
Ox.print('FORM CHANGE', data);
|
||||||
|
if (data.id == 'list') {
|
||||||
|
// FIXME: WRONG
|
||||||
|
if (data.data.value[0] != ':') {
|
||||||
|
$form.values('list', oml.validateName(data.data.value, listNames))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
that[
|
||||||
|
values.path && values.list ? 'enableButton' : 'disableButton'
|
||||||
|
]('import');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
that = Ox.Dialog({
|
||||||
|
buttons: [
|
||||||
|
Ox.Button({
|
||||||
|
id: 'dontimport',
|
||||||
|
title: Ox._('No, Don\'t Import')
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
that.close();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Ox.Button({
|
||||||
|
disabled: true,
|
||||||
|
id: 'import',
|
||||||
|
title: Ox._('Yes, Import')
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
var data = $form.values();
|
||||||
|
oml.api.import({
|
||||||
|
path: data.path,
|
||||||
|
list: data.list == ':' ? null : data.list
|
||||||
|
}, function() {
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
that.close();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
content: $form,
|
||||||
|
height: 128,
|
||||||
|
title: Ox._('Import Books'),
|
||||||
|
width: 512
|
||||||
|
});
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
};
|
|
@ -370,6 +370,8 @@ oml.ui.mainMenu = function() {
|
||||||
oml.ui.listDialog.open();
|
oml.ui.listDialog.open();
|
||||||
} else if (id == 'deletelist') {
|
} else if (id == 'deletelist') {
|
||||||
oml.ui.deleteListDialog().open();
|
oml.ui.deleteListDialog().open();
|
||||||
|
} else if (id == 'import') {
|
||||||
|
oml.ui.importDialog().open();
|
||||||
} else if (id == 'selectall') {
|
} else if (id == 'selectall') {
|
||||||
oml.$ui.list.selectAll();
|
oml.$ui.list.selectAll();
|
||||||
} else if (id == 'selectnone') {
|
} else if (id == 'selectnone') {
|
||||||
|
@ -545,11 +547,11 @@ oml.ui.mainMenu = function() {
|
||||||
title: Ox._('Edit'),
|
title: Ox._('Edit'),
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 'importitems',
|
id: 'import',
|
||||||
title: Ox._('Import Books...')
|
title: Ox._('Import Books...')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'exportitems',
|
id: 'export',
|
||||||
title: Ox._('Export Books...')
|
title: Ox._('Export Books...')
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"gridView.js",
|
"gridView.js",
|
||||||
"iconDialog.js",
|
"iconDialog.js",
|
||||||
"identifyDialog.js",
|
"identifyDialog.js",
|
||||||
|
"importDialog.js",
|
||||||
"info.js",
|
"info.js",
|
||||||
"infoView.js",
|
"infoView.js",
|
||||||
"itemInnerPanel.js",
|
"itemInnerPanel.js",
|
||||||
|
|
Loading…
Reference in a new issue