end support for mobi

pull/94/head
eric 2022-07-28 15:05:13 +02:00
parent e4a514566f
commit 88669d7a35
35 changed files with 70 additions and 779 deletions

View File

@ -64,7 +64,6 @@ class BooXtream(object):
# fake it, so you can test other functions without hitting booxtream
boox = Boox.objects.create(
download_link_epub='https://github.com/eshellman/42_ebook/blob/master/download/42.epub?raw=true&extra=download.booxtream.com/',
download_link_mobi='https://github.com/eshellman/42_ebook/blob/master/download/42.mobi?raw=true',
referenceid= kwargs.get('referenceid', '42'),
downloads_remaining=kwargs.get('downloadlimit', 10),
expirydays=kwargs.get('expirydays', 30),
@ -81,12 +80,8 @@ class BooXtream(object):
download_link_epub = doc.find('.//DownloadLink[@type="epub"]')
if download_link_epub is not None:
download_link_epub = download_link_epub.text
download_link_mobi = doc.find('.//DownloadLink[@type="mobi"]')
if download_link_mobi is not None:
download_link_mobi = download_link_mobi.text
boox = Boox.objects.create(
download_link_epub=download_link_epub,
download_link_mobi=download_link_mobi,
referenceid=kwargs.get('referenceid'),
downloads_remaining=kwargs.get('downloadlimit'),
expirydays=kwargs.get('expirydays'),

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2022-07-28 06:16
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('booxtream', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='boox',
name='download_link_mobi',
),
]

View File

@ -7,7 +7,6 @@ class Boox(models.Model):
keeps a record of a file that's been watermarked
"""
download_link_epub = models.URLField(null=True)
download_link_mobi = models.URLField(null=True)
referenceid = models.CharField(max_length=32)
downloads_remaining = models.PositiveSmallIntegerField(default=0)
expirydays = models.PositiveSmallIntegerField()
@ -20,7 +19,5 @@ class Boox(models.Model):
def download_link(self, format):
if format == 'epub':
return self.download_link_epub
elif format == 'mobi':
return self.download_link_mobi
return None

View File

@ -225,7 +225,7 @@ class EbookFileAdmin(ModelAdmin):
list_display = ('created', 'format', 'ebook_link', 'asking')
date_hierarchy = 'created'
ordering = ('edition__work',)
fields = ('file', 'format', 'edition', 'edition_link', 'ebook', 'ebook_link', 'source', 'mobied')
fields = ('file', 'format', 'edition', 'edition_link', 'ebook', 'ebook_link', 'source')
readonly_fields = ('file', 'edition_link', 'ebook_link', 'source')
def edition_link(self, obj):
if obj.edition:

View File

@ -107,7 +107,7 @@ class FormatFacetGroup(FacetGroup):
def __init__(self):
super(FacetGroup,self).__init__()
self.title = 'Format'
self.facets = ['pdf', 'epub', 'mobi']
self.facets = ['pdf', 'epub']
self.label = '{} is ...'.format(self.title)
def get_facet_class(self, facet_name):

View File

@ -283,7 +283,7 @@ def load_doab_edition(title, doab_id, url, format, rights,
work.selected_edition = edition
work.save()
if format in ('pdf', 'epub', 'mobi', 'html', 'online') and rights:
if format in ('pdf', 'epub', 'html', 'online') and rights:
ebook = models.Ebook()
ebook.format = format
ebook.provider = provider

View File

@ -618,7 +618,7 @@ def harvest_frontiersin(ebook):
logger.warning('couldn\'t get any dl_url for %s', ebook.url)
return harvested, num
SPRINGERDL = re.compile(r'(EPUB|PDF|MOBI)')
SPRINGERDL = re.compile(r'(EPUB|PDF)')
def harvest_springerlink(ebook):
def selector(doc):
@ -910,7 +910,7 @@ def harvest_doi_coaccess(ebook):
# a new ebook
format = loader.type_for_url(url)
if format in ('pdf', 'epub', 'mobi', 'html', 'online'):
if format in ('pdf', 'epub', 'html', 'online'):
new_ebook = models.Ebook()
new_ebook.format = format
new_ebook.url = url
@ -1000,7 +1000,7 @@ def harvest_fulcrum(ebook):
def harvest_ubiquity(ebook):
def selector(doc):
return doc.find_all('a', attrs={'data-category': re.compile('(epub|mobi|pdf) download')})
return doc.find_all('a', attrs={'data-category': re.compile('(epub|pdf) download')})
return harvest_multiple_generic(ebook, selector)
def harvest_orkana(ebook):

View File

@ -11,7 +11,7 @@ class PressbooksScraper(BaseScraper):
can_scrape_strings = ['pressbooks']
def get_downloads(self):
for dl_type in ['epub', 'mobi', 'pdf']:
for dl_type in ['epub', 'pdf']:
download_el = self.doc.select_one('.{}'.format(dl_type))
value = None
if download_el and download_el.find_parent():

View File

@ -199,8 +199,7 @@ class BaseScraper(object):
'''return a dict of edition keys and ISBNs'''
isbns = {}
isbn_cleaner = identifier_cleaner('isbn', quiet=True)
label_map = {'epub': 'EPUB', 'mobi': 'Mobi',
'paper': 'Paperback', 'pdf':'PDF', 'hard':'Hardback'}
label_map = {'epub': 'EPUB', 'paper': 'Paperback', 'pdf':'PDF', 'hard':'Hardback'}
for key in label_map.keys():
isbn_key = 'isbn_{}'.format(key)
value = self.check_metas(['citation_isbn'], type=label_map[key])
@ -332,7 +331,7 @@ class BaseScraper(object):
self.set('covers', [{'image_url': image_url}])
def get_downloads(self):
for dl_type in ['epub', 'mobi', 'pdf']:
for dl_type in ['epub', 'pdf']:
dl_meta = 'citation_{}_url'.format(dl_type)
value = self.check_metas([dl_meta])
if value:

View File

@ -4,7 +4,7 @@ from django.core.management.base import BaseCommand
from regluit.core.loaders.harvest import archive_dl, RateLimiter, DONT_HARVEST
from regluit.core.models import Ebook
from regluit.core.parameters import GOOD_PROVIDERS
DOWNLOADABLE = ['pdf', 'epub', 'mobi']
DOWNLOADABLE = ['pdf', 'epub']
DONT_CHECK = list(GOOD_PROVIDERS) + DONT_HARVEST

View File

@ -1,44 +0,0 @@
from django.core.management.base import BaseCommand
from regluit.core.models import Work, EbookFile
class Command(BaseCommand):
help = "generate mobi ebooks where needed and possible."
def add_arguments(self, parser):
parser.add_argument('max', nargs='?', type=int, default=1, help="maximum mobis to make")
parser.add_argument('--reset', '-r', action='store_true', help="reset failed mobi conversions")
def handle(self, max=None, **options):
maxbad = 10
if options['reset']:
bads = EbookFile.objects.filter(mobied__lt=0)
for bad in bads:
bad.mobied = 0
bad.save()
epubs = Work.objects.filter(editions__ebooks__format='epub').distinct().order_by('-id')
i = 0
n_bad = 0
for work in epubs:
if not work.ebooks().filter(format="mobi").exists():
for ebook in work.ebooks().filter(format="epub"):
ebf = ebook.get_archive_ebf()
if ebf and ebf.mobied >= 0:
try:
self.stdout.write(u'making mobi for {}'.format(work.title))
if ebf.make_mobi():
self.stdout.write('made mobi')
i += 1
break
else:
self.stdout.write('failed to make mobi')
n_bad += 1
except:
self.stdout.write('failed to make mobi')
n_bad += 1
if i >= max or n_bad >= maxbad:
break

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2022-07-28 06:16
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0024_auto_20210503_1717'),
]
operations = [
migrations.RemoveField(
model_name='ebookfile',
name='mobied',
),
]

View File

@ -1,30 +0,0 @@
import requests
from django.conf import settings
mobigen_url = settings.MOBIGEN_URL
mobigen_user_id = settings.MOBIGEN_USER_ID
mobigen_password = settings.MOBIGEN_PASSWORD
def convert_to_mobi(input_url, input_format="application/epub+zip"):
"""
return a string with the output of mobigen computation
"""
# using verify=False since at the moment, using a self-signed SSL cert.
payload = requests.get(input_url).content
headers = {'Content-Type': input_format}
r = requests.post(mobigen_url, auth=(mobigen_user_id, mobigen_password),
data=payload, headers=headers)
# if HTTP reponse code is ok, the output is the mobi file; else error message
if r.status_code == 200:
return r.content
else:
raise Exception("{0}: {1}".format(r.status_code, r.content))

View File

@ -1,90 +0,0 @@
"""
Utilities for calling mobigen for management. do not use in application
"""
from itertools import islice
import uuid
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile, File
from regluit.core.models import (Campaign, Ebook)
from regluit.core import parameters
from regluit.core.mobi import convert_to_mobi
# compute whether we can apply mobigen to a given edition to produce a mobi file
# need to have an ebook in epub or pdf format
# possible return values: already has a mobi file / can generate a mobi file / not possible
def edition_mobi_status(edition):
"""
for a given edition, return:
* 1 if there is already a mobi ebook
* 0 if there is none but we have an epub or html to convert from
* -1 for no epub/html to convert from
"""
formats = set([ebook.format for ebook in edition.work.ebooks()])
if 'mobi' in formats:
return 1
elif ('epub' in formats) or ('html' in formats):
return 0
else:
return -1
def write_file_to_storage(file_object, content_type, path):
"""
write file_object to the default_storage at given path
"""
file_s3 = ContentFile(file_object)
file_s3.content_type = content_type
default_storage.save(path, file_s3)
return file_s3
# generator for editions to add mobi to
# campaigns that can have mobi files but don't yet.
def editions_to_convert():
for campaign in Campaign.objects.filter(edition__ebooks__isnull=False).distinct():
# need to make sure campaign type is not B2U because kindlegen is for books we give awy free of charge
if (edition_mobi_status(campaign.edition) == 0) and (campaign.type != parameters.BUY2UNGLUE): # possible to generate mobi
yield campaign.edition
def generate_mobi_ebook_for_edition(edition):
# pull out the sister edition to convert from
sister_ebook = edition.ebooks.filter(format__in=['epub', 'html'])[0]
# run the conversion process
output = convert_to_mobi(sister_ebook.url)
#output = open("/Users/raymondyee/Downloads/hello.mobi").read()
file_ = write_file_to_storage(output,
"application/x-mobipocket-ebook",
"/ebf/{0}.mobi".format(uuid.uuid4().hex))
# create a path for the ebookfile: IS THIS NECESSARY?
# https://github.com/Gluejar/regluit/blob/25dcb06f464dc11b5e589ab6859dfcc487f8f3ef/core/models.py#L1771
#ebfile = EbookFile(edition=edition, file=file_, format='mobi')
#ebfile.save()
# maybe need to create an ebook pointing to ebookFile ?
# copy metadata from sister ebook
ebfile_url = default_storage.url(file_.name)
ebook = Ebook(url=ebfile_url,
format="mobi",
provider="Unglue.it",
rights=sister_ebook.rights,
edition=edition)
ebook.save()
return ebook

View File

@ -65,7 +65,6 @@ from regluit.core.parameters import (
)
from regluit.core.epub import personalize, ungluify, ask_epub
from regluit.core.pdf import ask_pdf, pdf_append
from regluit.core import mobi
from regluit.core.signals import (
successful_campaign,
unsuccessful_campaign,
@ -213,8 +212,8 @@ class Acq(models.Model):
class mock_ebook(object):
def __init__(self, acq):
self.url = acq.get_mobi_url()
self.format = 'mobi'
self.url = acq.get_epub_url()
self.format = 'epub'
self.filesize = 0
def save(self):
return True
@ -247,10 +246,6 @@ class Acq(models.Model):
else:
return self.expires < datetime.now()
def get_mobi_url(self):
if self.expired:
return ''
return self.get_watermarked().download_link_mobi
def get_epub_url(self):
if self.expired:
@ -887,17 +882,6 @@ class Campaign(models.Model):
def latest_ending(cls):
return timedelta(days=int(settings.UNGLUEIT_LONGEST_DEADLINE)) + now()
def make_mobis(self):
# make archive files for ebooks, make mobi files for epubs
versions = set()
for ebook in self.work.ebooks().filter(provider__in=GOOD_PROVIDERS, format='mobi'):
versions.add(ebook.version_label)
for ebook in self.work.ebooks_all().exclude(provider='Unglue.it').filter(provider__in=GOOD_PROVIDERS, format='epub'):
if not ebook.version_label in versions:
# now make the mobi file
ebf = ebook.get_archive_ebf()
ebf.make_mobi()
def add_ask_to_ebfs(self, position=0):
if not self.use_add_ask or self.type != THANKS:
return
@ -948,17 +932,6 @@ class Campaign(models.Model):
new_epub_ebf.version = version
new_ebfs.append(new_epub_ebf)
# now make the mobi file
new_mobi_ebf = EbookFile.objects.create(edition=edition, format='mobi', asking=True)
try:
new_mobi_file = ContentFile(mobi.convert_to_mobi(new_epub_ebf.file.url))
except Exception as e:
logger.error("error making mobi for %s" % (new_epub_ebf.file.url))
raise e
new_mobi_ebf.file.save(path_for_file('ebf', None), new_mobi_file)
new_mobi_ebf.save()
new_mobi_ebf.version = version
new_ebfs.append(new_mobi_ebf)
except Exception as e:
logger.error("error making epub ask or mobi %s" % (e))
for ebf in new_ebfs:
@ -1047,7 +1020,6 @@ class Campaign(models.Model):
ungluified.file_obj.seek(0)
watermarked = watermarker.platform(epubfile=ungluified.file_obj, **params)
self.make_unglued_ebf('epub', watermarked)
self.make_unglued_ebf('mobi', watermarked)
return True
return False

View File

@ -31,7 +31,6 @@ from regluit.marc.models import MARCRecord as NewMARC
from questionnaire.models import Landing
from regluit.bisac.models import interpret_notation
from regluit.core import mobi
import regluit.core.cc as cc
from regluit.core.covers import (get_thumbnail,
DEFAULT_COVER, DEFAULT_COVER_LARGE, DEFAULT_COVER_SMALL)
@ -1091,7 +1090,6 @@ class EbookFile(models.Model):
asking = models.BooleanField(default=False)
ebook = models.ForeignKey('Ebook', on_delete=models.CASCADE, related_name='ebook_files', null=True)
source = models.URLField(max_length=1024, null=True, blank=True)
mobied = models.IntegerField(default=0) #-1 indicates a failed conversion attempt
version = None
def check_file(self):
if self.format == 'epub':
@ -1105,43 +1103,6 @@ class EbookFile(models.Model):
except:
return False
def make_mobi(self):
if not self.format == 'epub' or not settings.MOBIGEN_URL:
return False
if self.mobied < 0:
return False
try:
mobi_cf = ContentFile(mobi.convert_to_mobi(self.file.url))
except:
self.mobied = -1
self.save()
return False
new_mobi_ebf = EbookFile.objects.create(
edition=self.edition,
format='mobi',
asking=self.asking,
source=self.file.url,
)
new_mobi_ebf.file.save(path_for_file(new_mobi_ebf, None), mobi_cf)
new_mobi_ebf.save()
if self.ebook:
new_ebook = Ebook.objects.create(
edition=self.edition,
format='mobi',
provider='Unglue.it',
url=new_mobi_ebf.file.url,
rights=self.ebook.rights,
version_label=self.ebook.version_label,
version_iter=self.ebook.version_iter,
filesize=mobi_cf.size,
)
new_mobi_ebf.ebook = new_ebook
new_mobi_ebf.save()
self.mobied = 1
self.save()
return True
send_to_kindle_limit = 7492232
class Ebook(models.Model):

View File

@ -29,8 +29,7 @@ from regluit.core import (
bookloader,
covers,
models,
librarything,
mobigen
librarything
)
from regluit.core.models import Acq, Campaign, EbookFile, Gift, UserProfile, Work
from regluit.core.signals import deadline_impending
@ -157,18 +156,8 @@ def process_ebfs(campaign_id):
campaign.add_ask_to_ebfs()
else:
campaign.revert_asks()
campaign.make_mobis()
@task
def make_mobi(ebookfile_id):
try:
ebookfile = EbookFile.objects.get(ebookfile_id)
except EbookFile.DoesNotExist as e:
logger.error("error getting EbookFile %s", ebookfile_id)
return False
return ebookfile.make_mobi()
@task
def refresh_acqs():
in_10_min = now() + timedelta(minutes=10)
@ -194,10 +183,6 @@ def refresh_acqs():
else:
acq.refreshed = True
@task
def convert_to_mobi(input_url, input_format="application/epub+zip"):
return mobigen.convert_to_mobi(input_url, input_format)
@task
def ml_subscribe_task(profile_id, **kwargs):
try:

View File

@ -943,23 +943,14 @@ class DownloadPageTest(TestCase):
eb1.edition = e1
eb1.format = 'epub'
eb2 = models.Ebook()
eb2.url = "https://example2.com"
eb2.edition = e2
eb2.format = 'mobi'
eb1.save()
eb2.save()
anon_client = Client()
response = anon_client.get("/work/%s/download/" % w.id, follow=True)
self.assertContains(response, "/download_ebook/%s/"% eb1.id, count=11)
self.assertContains(response, "/download_ebook/%s/"% eb2.id, count=5)
self.assertContains(response, "/download_ebook/%s/"% eb1.id, count=12)
self.assertTrue(eb1.edition.work.is_free)
eb1.delete()
self.assertTrue(eb2.edition.work.is_free)
eb2.delete()
self.assertFalse(eb2.edition.work.is_free)
class MailingListTests(TestCase):
#mostly to check that MailChimp account is setp correctly
@ -1057,7 +1048,7 @@ class EbookFileTests(TestCase):
#flip the campaign to success
c.cc_date_initial = datetime(2012, 1, 1)
c.update_status()
self.assertEqual(c.work.ebooks().count(), 2)
self.assertEqual(c.work.ebooks().count(), 1)
c.do_watermark = False
c.save()
url = acq.get_watermarked().download_link_epub
@ -1122,15 +1113,12 @@ class EbookFileTests(TestCase):
ebf.ebook = eb
ebf.save()
temp_file.close()
ebf.make_mobi()
finally:
# make sure we get rid of temp file
os.remove(temp.name)
#test the ask-appender
c.add_ask_to_ebfs()
self.assertTrue(c.work.ebookfiles().filter(asking=True, format='epub').count() > 0)
if settings.MOBIGEN_URL:
self.assertTrue(c.work.ebookfiles().filter(asking=True, format='mobi').count() > 0)
self.assertTrue(c.work.ebookfiles().filter(asking=True, ebook__active=True).count() > 0)
self.assertTrue(c.work.ebookfiles().filter(asking=False, ebook__active=True).count() == 0)
#test the unasker
@ -1154,24 +1142,11 @@ class EbookFileTests(TestCase):
ebf = EbookFile(format='epub', edition=e, file=dj_file)
ebf.save()
temp_file.close()
ebf.make_mobi()
finally:
# make sure we get rid of temp file
os.remove(temp.name)
self.assertTrue(ebf.mobied < 0)
class MobigenTests(TestCase):
def test_convert_to_mobi(self):
"""
check the size of the mobi output of a Moby Dick epub
"""
from regluit.core.mobigen import convert_to_mobi
if settings.TEST_INTEGRATION:
output = convert_to_mobi(
"https://github.com/GITenberg/Moby-Dick--Or-The-Whale_2701/releases/download/0.2.0/Moby-Dick-Or-The-Whale.epub"
)
self.assertTrue(len(output) > 2207877)
@override_settings(LOCAL_TEST=True)
class LibTests(TestCase):
@ -1225,11 +1200,11 @@ class GitHubTests(TestCase):
)
expected_set = set([
('epub', u'Adventures-of-Huckleberry-Finn.epub'),
('mobi', u'Adventures-of-Huckleberry-Finn.mobi'),
('pdf', u'Adventures-of-Huckleberry-Finn.pdf')
])
self.assertEqual(set(ebooks), expected_set)
self.assertTrue(('epub', 'Adventures-of-Huckleberry-Finn.epub') in set(ebooks))
self.assertTrue(('pdf', 'Adventures-of-Huckleberry-Finn.pdf') in set(ebooks))
class OnixLoaderTests(TestCase):
fixtures = ['initial_data.json']

View File

@ -13,7 +13,6 @@ from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _
from pyepub import EPUB
from regluit.mobi import Mobi
from .isbn import ISBN
logger = logging.getLogger(__name__)
@ -112,14 +111,6 @@ def test_file(the_file, fformat):
except Exception as e:
logger.exception(e)
raise ValidationError(_('Are you sure this is an EPUB file?: %s' % e))
elif fformat == 'mobi':
try:
book = Mobi(the_file.file)
book.parse()
except Exception as e:
logger.exception(e)
#raise ValidationError(_('Are you sure this is a MOBI file?: %s' % e))
raise e
elif fformat == 'pdf':
try:
PdfFileReader(the_file.file)

View File

@ -109,7 +109,7 @@ class EbookFileForm(forms.ModelForm):
self.fields['format'].widget = forms.HiddenInput()
if campaign_type == THANKS:
self.fields['format'].widget = forms.Select(
choices = (('pdf', 'PDF'), ('epub', 'EPUB'), ('mobi', 'MOBI'))
choices = (('pdf', 'PDF'), ('epub', 'EPUB'))
)
def clean_version_label(self):

View File

@ -183,12 +183,8 @@ $j(document).ready(function() {
<h3>Download your ebook{% if acq.lib_acq %}{% if acq.on_reserve %}, on reserve for you at{% else %}, on loan to you at{% endif %} {{ acq.lib_acq.user.library }}{% endif %}</h3>
<div class="ebook_download">
<a href="{{ formats.epub }}"><img src="/static/images/epub32.png" height="32" alt="epub" title="epub" /></a>
<a href="{{ formats.epub }}">EPUB</a> (for iBooks, Nook, Kobo)
<a href="{{ formats.epub }}">EPUB</a> (for Kindle, iBooks, Nook, Kobo)
<a class="dropbox-saver" href="{{ xfer_url }}" data-filename="unglueit-{{ work.id }}.epub"></a>
<br /><br />
<a href="{{ formats.mobi }}"><img src="/static/images/mobi32.png" height="32" alt="mobi" title="mobi" /></a>
<a href="{{ formats.mobi }}">MOBI</a> (for Kindle)
<a class="dropbox-saver" href="{{ kindle_url }}" data-filename="unglueit-{{ work.id }}.mobi"></a>
</div>
{% endif %}
{% endif %}
@ -211,7 +207,7 @@ $j(document).ready(function() {
Looks like you're using an embedded browser inside an iOS app. (Maybe you followed a link in Twitter or Facebook?)
</p>
<p>
{% if formats.epub or formats.mobi %}
{% if formats.epub %}
To read this ebook you should open this page in safari, or use one of the "One-click" buttons, above. <br />
{% if iphone %}<img width="357" height="156" src="/static/images/clickhere.png" alt="how to open in safari" />{% else %}<img width="500" height="403" src="/static/images/open_safari.png" alt="how to open in safari" />{% endif %}<br clear="left" />
{% endif %}
@ -271,10 +267,6 @@ $j(document).ready(function() {
<p>
Download the <a href="{{ formats.text }}">text version</a>.
</p>
{% else %}
<p>
This ebook is only available in .mobi. Your best bet is to install the free Amazon Kindle app from the Apple Store and then use the Send-to-Kindle option above.
</p>
{% endif %}
<p class="other_instructions_paragraph">
Not on iOS? Try the instructions for <a class="android other_instructions">Android</a>, <a class="desktop other_instructions">desktop computers</a>, or <a class="ereader other_instructions">ereaders (Kindle, Nook, Kobo, etc.)</a>.
@ -284,6 +276,7 @@ $j(document).ready(function() {
<div id="android_div"{% if android %} class="active"{% endif %}>
<h4>Android devices</h4>
{% if formats.epub %}
You can send EPUB files to your Amazon Kindle using the Send-to-Kindle option above.
<p>
You may already have an app which reads ebooks. Download the <a href="{{ formats.epub }}">epub file</a> and see if you're offered an option for opening the file. If so, you're done! If not...
</p>
@ -313,10 +306,6 @@ $j(document).ready(function() {
<p>
Download the <a href="{{ formats.text }}">text version</a>.
</p>
{% else %}
<p>
This ebook is only available in .mobi. Your best bet is to install the free Amazon Kindle app from Google Play and then use the Send-to-Kindle option above.
</p>
{% endif %}{% endif %}{% endif %}{% endif %}
<p class="other_instructions_paragraph">
Not on Android? Try the instructions for <a class="ios other_instructions">iPhone/iPad</a>, <a class="desktop other_instructions">desktop computers</a>, or <a class="ereader other_instructions">ereaders (Kindle, Nook, Kobo, etc.)</a>.
@ -344,14 +333,6 @@ $j(document).ready(function() {
<li>Open the file in Calibre.</li>
<li>You can <a href="http://blog.marvinapp.com/post/53438723356">use a Calibre plugin</a> to manage files on reader apps.
</ul>
{% elif formats.mobi %}
<p class="ebook_download logo"><img src="/static/images/calibre_logo.png" alt="Calibre Logo" />Calibre</p>
<ul>
<li><a href="http://calibre-ebook.com/download">Download the free Calibre app.</a></li>
<li>Download the <a href="{{ formats.mobi }}">mobi file</a>.</li>
<li>Open the file in Calibre.</li>
<li>You can <a href="http://blog.marvinapp.com/post/53438723356">use a Calibre plugin</a> to manage files on reader apps.
</ul>
{% elif formats.html %}
<p>
You can read the <a href="{{ formats.html }}">HTML version</a> right here in your browser.
@ -372,8 +353,8 @@ $j(document).ready(function() {
{% if formats.mobi or formats.pdf or formats.epub %}
<ul>
<li>
{% if formats.mobi %}
<b>Kindle</b>: download the <a href="{{ formats.mobi }}">mobi file</a> to your computer, or use the <i>Send To Kindle</i> button above.
{% if formats.mobi or formats.epub %}
<b>Kindle</b>: download the <a href="{{ formats.epub }}">epub file</a> to your computer, or use the <i>Send To Kindle</i> button above.
{% elif formats.pdf %}
<b>Kindle</b>: download the <a href="{{ formats.pdf }}">pdf file</a> to your computer, or use the <i>Send To Kindle</i> button above.
{% else %}

View File

@ -44,7 +44,6 @@
<p> Reference id: <b>{{watermarked.referenceid}}</b></p>
<ul>
<li><a href="{{watermarked.download_link_epub}}">Processed epub for testing</a></li>
<li><a href="{{watermarked.download_link_mobi}}">Processed mobi (kindle) for testing</a></li>
</ul>
{% else %}{% if upload_error %}
<p>
@ -65,7 +64,7 @@
{{ upload_error }}
<h2>Upload Ebook files</h2>
{% ifequal edition.work.last_campaign.type 2 %}
<p>At this time, we accept only EPUB files for "Buy to Unglue" campaigns. Files for Kindle will be autogenerated.
<p>At this time, we accept only EPUB files for "Buy to Unglue" campaigns.
{% endifequal %}
{% ifequal edition.work.last_campaign.type 3 %}
<p>You can upload PDF, EPUB and MOBI files for "Thanks for Ungluing" campaigns.

View File

@ -2,10 +2,7 @@
<p style="">
<img src="/static/images/{{ facet.facet_name }}32.png" height="32" alt="{{ facet.facet_name }}" title="{{ facet.facet_name }}" />
{% if facet.facet_name == 'epub' %}
These books are available in EPUB format - good for iBooks, Nook, Kobo.
{% endif %}
{% if facet.facet_name == 'mobi' %}
These books are available in MOBI format - good for Kindle.
These books are available in EPUB format - good for Kindle, iBooks, Nook, Kobo.
{% endif %}
{% if facet.facet_name == 'pdf' %}
These books are available in PDF format - good for desktops, printing.

View File

@ -269,7 +269,7 @@ What does this mean for you? If you're a book lover, you can read unglued ebook
<dd>All unglued ebooks are released under a Creative Commons or other free license. The rights holder chooses which license to apply. Books that we distribute in a Buy-to-Unglue Campaign have creative commons licenses, but the effective date of these licenses is set in the future.<br /><br />
Creative Commons licenses mean that once the license is effective you <b>can</b>: make copies; keep them for as long as you like; shift them to other formats (like .mobi or PDF); share them with friends or on the internet; download them for free.<br /><br />
Creative Commons licenses mean that once the license is effective you <b>can</b>: make copies; keep them for as long as you like; shift them to other formats (like PDF); share them with friends or on the internet; download them for free.<br /><br />
Under NC (non-commercial) licenses, you <b>cannot</b>: sell unglued ebooks, or otherwise use them commercially, without permission from the rights holder.<br /><br />

View File

@ -17,7 +17,7 @@ Reward the courageous creators who've made their ebooks free to all.
<dd>Theres no charge creators to join. Unglue.it charge $0.25 + 8%, which includes credit card fees. So if you pay $1 for a book, 67 cents goes to the creator (or whoever holds commercial rights), and 33 cents, the entirety of our fee, goes to pay bank network fees. If you pay $10 for a book, the split is $8.95 for the creator, $0.60 for the bank network, and $0.35 to support unglue.it. Unglue.it doesn't process contributions less than $1.</dd>
<dt>What file formats are supported?</dt>
<dd>The website is designed to work with pdf, epub, and mobi.</dd>
<dd>The website is designed to work with pdf, epub.</dd>
<dt>Why should libraries support Thanks-for-Ungluing books?</dt>
<dd>Libraries that participate in Thanks-for-Ungluing campaigns make these books more accessible to their users. In addition:

View File

@ -164,20 +164,14 @@ Please fix the following before launching your campaign:
{% endif %}
{% if campaign.work.epubfiles.0 %}
{% for ebf in campaign.work.epubfiles %}
<p>{% if ebf.active %}<span class="yikes">ACTIVE</span> {% elif ebf.ebook.active %} MIRROR {% endif %}EPUB file: <a href="{{ebf.file.url}}">{{ebf.file}}</a> <br />created {{ebf.created}} for edition <a href="#edition_{{ebf.edition_id}}">{{ebf.edition_id}}</a> {% if ebf.asking %}(This file has had the campaign 'ask' added.){% endif %}<br />{% if ebf.active %}{% if action == 'mademobi' %}<span class="yikes">A MOBI file is being generated. </span> (Takes a minute or two.) {% else %}You can <a href="{% url 'makemobi' campaign.id ebf.id %}">generate a MOBI file.</a> {% endif %}{% endif %}</p>
<p>{% if ebf.active %}<span class="yikes">ACTIVE</span> {% elif ebf.ebook.active %} MIRROR {% endif %}EPUB file: <a href="{{ebf.file.url}}">{{ebf.file}}</a> <br />created {{ebf.created}} for edition <a href="#edition_{{ebf.edition_id}}">{{ebf.edition_id}}</a> {% if ebf.asking %}(This file has had the campaign 'ask' added.){% endif %}</p>
{% endfor %}
{% if campaign.work.test_acqs.0 %}
<ul>
<li><a href="{{campaign.work.test_acqs.0.watermarked.download_link_epub}}">Processed epub for testing</a></li>
<li><a href="{{campaign.work.test_acqs.0.watermarked.download_link_mobi}}">Processed mobi (kindle) for testing</a></li>
</ul>
{% endif %}
{% endif %}
{% if campaign.work.mobifiles.0 %}
{% for ebf in campaign.work.mobifiles %}
<p>{% if ebf.active %}<span class="yikes">ACTIVE</span> {% endif %}MOBI file: <a href="{{ebf.file.url}}">{{ebf.file}}</a> <br />created {{ebf.created}} for edition <a href="#edition_{{ebf.edition_id}}">{{ebf.edition_id}}</a> {% if ebf.asking %}(This file has had the campaign 'ask' added.){% endif %}</p>
{% endfor %}
{% endif %}
{% if campaign.work.pdffiles.0 %}
{% for ebf in campaign.work.pdffiles %}
<p>{% if ebf.active %}<span class="yikes">ACTIVE</span> {% endif %}PDF file: <a href="{{ebf.file.url}}">{{ebf.file}}</a> <br />created {{ebf.created}} for edition <a href="#edition_{{ebf.edition_id}}">{{ebf.edition_id}}</a> {% if ebf.asking %}(This file has had the campaign 'ask' added.){% endif %}</p>

View File

@ -18,9 +18,6 @@ onload = function(){
else if(urlInput.value.endsWith('.epub')){
formatInput.value = 'epub'
}
else if(urlInput.value.endsWith('.mobi')){
formatInput.value = 'mobi'
}
else if(urlInput.value.endsWith('.html')){
formatInput.value = 'html'
};
@ -32,9 +29,6 @@ onload = function(){
else if(fileInput.value.endsWith('.epub')){
formatInput.value = 'epub'
}
else if(fileInput.value.endsWith('.mobi')){
formatInput.value = 'mobi'
}
else if(fileInput.value.endsWith('.html')){
formatInput.value = 'html'
};

View File

@ -61,8 +61,7 @@
<div class="find-book">
<h4>Available formats...</h4>
<ul>
<li><span class="format_display"><img src="/static/images/mobi32.png" height="32" alt="mobi" title="mobi" /> (for Kindle) </span></li>
<li><span class="format_display"><img src="/static/images/epub32.png" height="32" alt="epub" title="epub" /> (for iBooks, Nook, Kobo) </span></li>
<li><span class="format_display"><img src="/static/images/epub32.png" height="32" alt="epub" title="epub" /> (for Kindle, iBooks, Nook, Kobo) </span></li>
</ul>
</div>

View File

@ -31,8 +31,6 @@ urlpatterns = [
url(r"^rightsholders/agree/submitted$", TemplateView.as_view(template_name='agreed.html'), name="agreed"),
url(r"^rightsholders/campaign/(?P<id>\d+)/$", views.manage_campaign, name="manage_campaign"),
url(r"^rightsholders/campaign/(?P<id>\d+)/results/$", views.manage_campaign, {'action': 'results'}, name="campaign_results"),
url(r"^rightsholders/campaign/(?P<id>\d+)/(?P<ebf>\d+)/makemobi/$", views.manage_campaign, {'action': 'makemobi'}, name="makemobi"),
url(r"^rightsholders/campaign/(?P<id>\d+)/mademobi/$", views.manage_campaign, {'action': 'mademobi'}, name="mademobi"),
url(r"^rightsholders/edition/(?P<work_id>\d*)/(?P<edition_id>\d*)$", views.edit_edition, {'by': 'rh'}, name="rh_edition"),
url(r"^rightsholders/edition/(?P<edition_id>\d*)/upload/$", views.edition_uploads, name="edition_uploads"),
url(r"^rightsholders/claim/$", login_required(views.claim), name="claim"),

View File

@ -2523,7 +2523,7 @@ class DownloadView(PurchaseView):
unglued_ebooks = work.ebooks().filter(edition__unglued=True)
other_ebooks = work.ebooks().filter(edition__unglued=False)
xfer_url = kindle_url = None
xfer_url = None
acq = None
formats = {} # a dict of format name and url
for ebook in work.ebooks().all():
@ -2546,9 +2546,7 @@ class DownloadView(PurchaseView):
watermark_acq.delay(an_acq.id)
acq = an_acq
formats['epub'] = reverse('download_acq', kwargs={'nonce':acq.nonce, 'format':'epub'})
formats['mobi'] = reverse('download_acq', kwargs={'nonce':acq.nonce, 'format':'mobi'})
xfer_url = settings.BASE_URL_SECURE + formats['epub']
kindle_url = settings.BASE_URL_SECURE + formats['mobi']
can_kindle = True
break
@ -2560,7 +2558,7 @@ class DownloadView(PurchaseView):
#send to kindle
try:
kindle_ebook = non_google_ebooks.filter(format='mobi')[0]
kindle_ebook = non_google_ebooks.filter(format='epub')[0]
can_kindle = kindle_ebook.kindle_sendable()
except IndexError:
try:
@ -2584,7 +2582,6 @@ class DownloadView(PurchaseView):
'other_ebooks': other_ebooks,
'formats': formats,
'xfer_url': xfer_url,
'kindle_url': kindle_url,
'dropbox_key': settings.DROPBOX_KEY,
'can_kindle': can_kindle,
'base_url': settings.BASE_URL_SECURE,
@ -2699,8 +2696,6 @@ def download_acq(request, nonce, format):
acq.borrow()
if format == 'epub':
return HttpResponseRedirect(acq.get_epub_url())
else:
return HttpResponseRedirect(acq.get_mobi_url())
def about(request, facet):
template = "about_" + facet + ".html"
@ -2903,7 +2898,7 @@ def send_to_kindle(request, work_id, javascript='0'):
else:
non_google_ebooks = work.ebooks().exclude(provider='Google Books')
try:
ebook = non_google_ebooks.filter(format='mobi')[0]
ebook = non_google_ebooks.filter(format='epub')[0]
except IndexError:
try:
ebook = non_google_ebooks.filter(format='pdf')[0]

View File

@ -281,16 +281,6 @@ def manage_campaign(request, id, ebf=None, action='manage'):
new_premium_form = CustomPremiumForm(data={'campaign': campaign})
activetab = '#2'
else:
if action == 'makemobi':
try:
ebookfile = get_object_or_404(models.EbookFile, id=ebf)
except ValueError:
raise Http404
tasks.make_mobi.delay(ebookfile.id)
return HttpResponseRedirect(reverse('mademobi', args=[campaign.id]))
elif action == 'mademobi':
alerts.append('A MOBI file is being generated')
form = ManageCampaignForm(
instance=campaign,
initial={'work_description':campaign.work.description}

View File

@ -1,297 +0,0 @@
#!/usr/bin/env python
# encoding: utf-8
"""
Mobi.py
Created by Elliot Kroo on 2009-12-25.
Copyright (c) 2009 Elliot Kroo. All rights reserved.
"""
import sys
import os
import unittest
from struct import *
from pprint import pprint
from . import utils
from .lz77 import uncompress_lz77
class Mobi:
def parse(self):
""" reads in the file, then parses record tables"""
self.contents = self.f.read();
self.header = self.parseHeader();
self.records = self.parseRecordInfoList();
self.readRecord0()
def readRecord(self, recordnum, disable_compression=False):
if self.config:
if self.config['palmdoc']['Compression'] == 1 or disable_compression:
return self.contents[self.records[recordnum]['record Data Offset']:self.records[recordnum+1]['record Data Offset']];
elif self.config['palmdoc']['Compression'] == 2:
result = uncompress_lz77(self.contents[self.records[recordnum]['record Data Offset']:self.records[recordnum+1]['record Data Offset']-self.config['mobi']['extra bytes']])
return result
def readImageRecord(self, imgnum):
if self.config:
recordnum = self.config['mobi']['First Image index'] + imgnum;
return self.readRecord(recordnum, disable_compression=True);
def author(self):
"Returns the author of the book"
return str(self.config['exth']['records'][100], 'utf-8')
def title(self):
"Returns the title of the book"
return str(self.config['mobi']['Full Name'], 'utf-8')
########### Private API ###########################
def __init__(self, filename):
try:
if isinstance(filename, str):
self.f = open(filename, "rb");
else:
self.f = filename;
except IOError as e:
sys.stderr.write("Could not open %s! " % filename);
raise e;
self.offset = 0;
def __iter__(self):
if not self.config: return;
for record in range(1, self.config['mobi']['First Non-book index'] - 1):
yield self.readRecord(record);
def parseRecordInfoList(self):
records = {};
# read in all records in info list
for recordID in range(self.header['number of records']):
headerfmt = '>II'
headerlen = calcsize(headerfmt)
fields = [
"record Data Offset",
"UniqueID",
]
# create tuple with info
results = zip(fields, unpack(headerfmt, self.contents[self.offset:self.offset+headerlen]))
# increment offset into file
self.offset += headerlen
# convert tuple to dictionary
resultsDict = utils.toDict(results);
# futz around with the unique ID record, as the uniqueID's top 8 bytes are
# really the "record attributes":
resultsDict['record Attributes'] = (resultsDict['UniqueID'] & 0xFF000000) >> 24;
resultsDict['UniqueID'] = resultsDict['UniqueID'] & 0x00FFFFFF;
# store into the records dict
records[resultsDict['UniqueID']] = resultsDict;
return records;
def parseHeader(self):
headerfmt = '>32shhIIIIII4s4sIIH'
headerlen = calcsize(headerfmt)
fields = [
"name",
"attributes",
"version",
"created",
"modified",
"backup",
"modnum",
"appInfoId",
"sortInfoID",
"type",
"creator",
"uniqueIDseed",
"nextRecordListID",
"number of records"
]
# unpack header, zip up into list of tuples
results = zip(fields, unpack(headerfmt, self.contents[self.offset:self.offset+headerlen]))
# increment offset into file
self.offset += headerlen
# convert tuple array to dictionary
resultsDict = utils.toDict(results);
return resultsDict
def readRecord0(self):
palmdocHeader = self.parsePalmDOCHeader();
MobiHeader = self.parseMobiHeader();
exthHeader = None
if MobiHeader['Has EXTH Header']:
exthHeader = self.parseEXTHHeader();
self.config = {
'palmdoc': palmdocHeader,
'mobi' : MobiHeader,
'exth' : exthHeader
}
def parseEXTHHeader(self):
headerfmt = '>III'
headerlen = calcsize(headerfmt)
fields = [
'identifier',
'header length',
'record Count'
]
# unpack header, zip up into list of tuples
results = zip(fields, unpack(headerfmt, self.contents[self.offset:self.offset+headerlen]))
# convert tuple array to dictionary
resultsDict = utils.toDict(results);
self.offset += headerlen;
resultsDict['records'] = {};
for record in range(resultsDict['record Count']):
recordType, recordLen = unpack(">II", self.contents[self.offset:self.offset+8]);
recordData = self.contents[self.offset+8:self.offset+recordLen];
resultsDict['records'][recordType] = recordData;
self.offset += recordLen;
return resultsDict;
def parseMobiHeader(self):
headerfmt = '> IIII II 40s III IIIII IIII I 36s IIII 8s HHIIIII'
headerlen = calcsize(headerfmt)
fields = [
"identifier",
"header length",
"Mobi type",
"text Encoding",
"Unique-ID",
"Generator version",
"-Reserved",
"First Non-book index",
"Full Name Offset",
"Full Name Length",
"Language",
"Input Language",
"Output Language",
"Format version",
"First Image index",
"First Huff Record",
"Huff Record Count",
"First DATP Record",
"DATP Record Count",
"EXTH flags",
"-36 unknown bytes, if Mobi is long enough",
"DRM Offset",
"DRM Count",
"DRM Size",
"DRM Flags",
"-Usually Zeros, unknown 8 bytes",
"-Unknown",
"Last Image Record",
"-Unknown",
"FCIS record",
"-Unknown",
"FLIS record",
"Unknown"
]
# unpack header, zip up into list of tuples
results = zip(fields, unpack(headerfmt, self.contents[self.offset:self.offset+headerlen]))
# convert tuple array to dictionary
resultsDict = utils.toDict(results);
resultsDict['Start Offset'] = self.offset;
resultsDict['Full Name'] = (self.contents[
self.records[0]['record Data Offset'] + resultsDict['Full Name Offset'] :
self.records[0]['record Data Offset'] + resultsDict['Full Name Offset'] + resultsDict['Full Name Length']])
resultsDict['Has DRM'] = resultsDict['DRM Offset'] != 0xFFFFFFFF;
resultsDict['Has EXTH Header'] = (resultsDict['EXTH flags'] & 0x40) != 0;
self.offset += resultsDict['header length'];
def onebits(x, width=16):
return len(list(filter(lambda x: x == "1", (str((x >> i) & 1) for i in range(width-1, -1, -1)))));
resultsDict['extra bytes'] = 2*onebits(unpack(">H", self.contents[self.offset-2:self.offset])[0] & 0xFFFE)
return resultsDict;
def parsePalmDOCHeader(self):
headerfmt = '>HHIHHHH'
headerlen = calcsize(headerfmt)
fields = [
"Compression",
"Unused",
"text length",
"record count",
"record size",
"Encryption Type",
"Unknown"
]
offset = self.records[0]['record Data Offset'];
# create tuple with info
results = zip(fields, unpack(headerfmt, self.contents[offset:offset+headerlen]))
# convert tuple array to dictionary
resultsDict = utils.toDict(results);
self.offset = offset+headerlen;
return resultsDict
TESTDIR = os.path.join(os.path.dirname(__file__), '../test/')
MOBIFILE = os.path.join(TESTDIR, 'CharlesDarwin.mobi')
class MobiTests(unittest.TestCase):
def setUp(self):
self.mobitest = Mobi(MOBIFILE);
def testParse(self):
self.mobitest.parse();
#pprint (self.mobitest.config)
def testRead(self):
self.mobitest.parse();
content = b''
for i in range(1,5):
content += self.mobitest.readRecord(i);
def testImage(self):
self.mobitest.parse();
#pprint (self.mobitest.records);
for record in range(4):
f = open("imagerecord%d.jpg" % record, 'wb')
f.write(self.mobitest.readImageRecord(record));
f.close();
for record in range(4):
f = os.remove("imagerecord%d.jpg" % record)
def testAuthorTitle(self):
self.mobitest.parse()
self.assertEqual(self.mobitest.author(), 'Charles Darwin')
self.assertEqual(self.mobitest.title(), 'The Origin of Species by means '+
'of Natural Selection, 6th Edition')
if __name__ == '__main__':
unittest.main()

View File

@ -1,85 +0,0 @@
import struct
# ported directly from the PalmDoc Perl library
# http://kobesearch.cpan.org/htdocs/EBook-Tools/EBook/Tools/PalmDoc.pm.html
# ... and reported to py 3
def uncompress_lz77(data):
length = len(data);
offset = 0; # Current offset into data
# char; # Character being examined
# ord; # Ordinal of $char
# lz77; # 16-bit Lempel-Ziv 77 length-offset pair
# lz77offset; # LZ77 offset
# lz77length; # LZ77 length
# lz77pos; # Position inside $lz77length
text = b''; # Output (uncompressed) text
# textlength; # Length of uncompressed text during LZ77 pass
# textpos; # Position inside $text during LZ77 pass
while offset < length:
# char = substr($data, $offset++, 1);
ord_ = data[offset];
byte = ord_.to_bytes(1, byteorder='big')
offset += 1;
# The long if-elsif chain is the best logic for $ord handling
## no critic (Cascading if-elsif chain)
if (ord_ == 0):
# Nulls are literal
text += byte;
elif (ord_ <= 8):
# Next $ord bytes are literal
text += data[offset:offset + ord_] # text .=substr($data, $offset, ord);
offset += ord_;
elif (ord_ <= 0x7f):
# Values from 0x09 through 0x7f are literal
text += byte;
elif (ord_ <= 0xbf):
# Data is LZ77-compressed
# From Wikipedia:
# "A length-distance pair is always encoded by a two-byte
# sequence. Of the 16 bits that make up these two bytes,
# 11 bits go to encoding the distance, 3 go to encoding
# the length, and the remaining two are used to make sure
# the decoder can identify the first byte as the beginning
# of such a two-byte sequence."
offset += 1;
if (offset > len(data)):
print("WARNING: offset to LZ77 bits is outside of the data: %d" % offset);
return text;
lz77, = struct.unpack('>H', data[offset-2:offset])
# Leftmost two bits are ID bits and need to be dropped
lz77 &= 0x3fff;
# Length is rightmost 3 bits + 3
lz77length = (lz77 & 0x0007) + 3;
# Remaining 11 bits are offset
lz77offset = lz77 >> 3;
if (lz77offset < 1):
print("WARNING: LZ77 decompression offset is invalid!");
return text;
# Getting text from the offset is a little tricky, because
# in theory you can be referring to characters you haven't
# actually decompressed yet. You therefore have to check
# the reference one character at a time.
textlength = len(text);
for lz77pos in range(lz77length): # for($lz77pos = 0; $lz77pos < $lz77length; $lz77pos++)
textpos = textlength - lz77offset;
if (textpos < 0):
print("WARNING: LZ77 decompression reference is before"+
" beginning of text! %x" % lz77);
return;
text += text[textpos:textpos + 1]; #text .= substr($text, $textpos, 1);
textlength += 1;
else:
# 0xc0 - 0xff are single characters (XOR 0x80) preceded by
# a space
text += b' ' + (ord_ ^ 0x80).to_bytes(1, byteorder='big');
return text;

View File

@ -1,20 +0,0 @@
#!/usr/bin/env python
# encoding: utf-8
"""
utils.py
Created by Elliot Kroo on 2009-12-25.
Copyright (c) 2009 Elliot Kroo. All rights reserved.
"""
import sys
import os
import unittest
def toDict(tuples):
resultsDict = {}
for field, value in tuples:
if len(field) > 0 and field[0] != "-":
resultsDict[field] = value
return resultsDict;

View File

@ -8,9 +8,6 @@ DROPBOX_KEY = os.environ.get('DROPBOX_KEY', '012345678901234')
GITHUB_PUBLIC_TOKEN = os.environ.get('GITHUB_PUBLIC_TOKEN', None) # 40 chars; null has lower limit
MAILCHIMP_API_KEY = os.environ.get('MAILCHIMP_API_KEY', '-us2') # [32chars]-xx#
MAILCHIMP_NEWS_ID = os.environ.get('MAILCHIMP_NEWS_ID', '0123456789')
MOBIGEN_PASSWORD = os.environ.get('MOBIGEN_PASSWORD', '012345678901234')
MOBIGEN_URL = os.environ.get('MOBIGEN_URL', '') # https://host/mobigen
MOBIGEN_USER_ID = os.environ.get('MOBIGEN_USER_ID', 'user')
STRIPE_PK = os.environ.get('STRIPE_PK', 'user')
STRIPE_SK = os.environ.get('STRIPE_SK', 'user')