Skip to content

Commit

Permalink
Merge pull request #2 from harmtemolder/master
Browse files Browse the repository at this point in the history
Updated code to match new style search result page
  • Loading branch information
harmtemolder authored Aug 19, 2020
2 parents ea42cf3 + a6c398a commit 192c4ea
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 133 deletions.
147 changes: 147 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# “Compiled”
*.zip

# Calibre
calibre

# PyCharm
.idea

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,19 @@ A Libgen Fiction store plugin for Calibre
## Installation

Download a release from [here](https://github.com/fallaciousreasoning/CalibreLibgenStore/releases) and follow the instructions to add the plugin to Calibre.

## Testing & development

While working on any of the scripts, run this to update the plugin in Calibre and start it in debug mode:

```shell
calibre-customize -b . && calibre-debug -g
```

## Build a release

Run this to zip all PY files together:

```shell
./zip.sh
```
138 changes: 74 additions & 64 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,102 +1,112 @@
# -*- coding: utf-8 -*-

from __future__ import (unicode_literals, division, absolute_import, print_function)

from calibre.customize import StoreBase
from calibre.devices.usbms.driver import debug_print
from calibre.gui2 import open_url
from calibre.gui2.store import StorePlugin
from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store.web_store_dialog import WebStoreDialog
from PyQt5.Qt import QUrl

from .libgen_client import LibgenFictionClient

store_version = 5 # Needed for dynamic plugin loading

__license__ = 'MIT'
__copyright__ = 'Fallacious Reasoning'
__docformat__ = 'restructuredtext en'

#####################################################################
# Plug-in base class
#####################################################################

from calibre.customize import InterfaceActionBase

PLUGIN_NAME = 'Libgen Fiction'
PLUGIN_DESCRIPTION = 'Adds a Libfen Fiction search provider to Calibre'
PLUGIN_VERSION_TUPLE = (0, 1, 0)
PLUGIN_VERSION = '.'.join([str(x) for x in PLUGIN_VERSION_TUPLE])
PLUGIN_DESCRIPTION = 'Adds a Libgen Fiction search provider to Calibre'
PLUGIN_AUTHORS = "Fallacious Reasoning (https://github.com/fallaciousreasoning/CalibreLibgenStore)"
PLUGIN_VERSION = (0, 2, 0)

#####################################################################

import base64
import mimetypes
import re
import urllib
import urllib2
from contextlib import closing

from lxml import etree

from .libgen_client import LibgenFictionClient
class LibgenStore(StorePlugin):
def genesis(self):
'''
Initialize the Libgen Client
'''
debug_print('Libgen Fiction::__init__.py:LibgenStore:genesis')

from calibre import browser, url_slash_cleaner
from calibre.constants import __appname__, __version__
from calibre.gui2.store.basic_config import BasicStoreConfig
from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store import StorePlugin
self.libgen = LibgenFictionClient()

from calibre.customize import StoreBase
def search(self, query, max_results=10, timeout=60):
'''
Searches LibGen for Books. Since the mirror links are not direct
downloads, it should not provide these as `s.downloads`.
'''

debug_print('Libgen Fiction::__init__.py:LibgenStore:search:query =',
query)

web_url = 'http://libgen.io/'
libgen = LibgenFictionClient()
libgen_results = self.libgen.search(query)

def search(query, max_results=10, timeout=60):
libgen_results = libgen.search(query)
for result in libgen_results.results[:min(max_results, len(libgen_results.results))]:
s = SearchResult()
for result in libgen_results.results[:min(max_results, len(libgen_results.results))]:
debug_print('Libgen Fiction::__init__.py:LibgenStore:search:'
'result.title =',
result.title)

s.title = result.title
s.author = result.author
s.series = result.series
s.language = result.language
for mirror in result.mirrors[0:1]: # Calibre only shows 1 anyway
debug_print('Libgen Fiction::__init__.py:LibgenStore:search:'
'result.mirror.url =', mirror.url)

for download in result.downloads:
s.downloads[download.format] = download.url
s = SearchResult()

s.formats = ', '.join(s.downloads.keys())
s.drm = SearchResult.DRM_UNLOCKED
s.cover_url = result.image_url
s.store_name = PLUGIN_NAME
s.cover_url = result.image_url
s.title = '{} ({}, {}{})'.format(
result.title, result.language, mirror.size, mirror.unit)
s.author = result.authors
s.price = '0.00'
s.detail_item = result.md5
s.drm = SearchResult.DRM_UNLOCKED
s.formats = mirror.format
s.plugin_author = PLUGIN_AUTHORS

# don't show results with no downloads
if not s.formats:
continue
debug_print('Libgen Fiction::__init__.py:LibgenStore:search:s =',
s)

yield s
yield s


class LibgenStore(StorePlugin):
def search(self, query, max_results=10, timeout=60):
def open(self, parent=None, detail_item=None, external=False):
'''
Searches LibGen for Books
Open the specified item in the external, or Calibre's browser
'''
for result in search(query, max_results, timeout):
yield result

if __name__ == '__main__':
import sys
debug_print('Libgen Fiction::__init__.py:LibgenStore:open:locals() =',
locals())

detail_url = (
self.libgen.get_detail_url(detail_item)
if detail_item
else self.libgen.base_url
)

debug_print('Libgen Fiction::__init__.py:LibgenStore:open:detail_url =',
detail_url)

query = ' '.join(sys.argv[1:]) if len(sys.argv) > 1 else "Stormlight Archive"
for result in search(' '.join(sys.argv[1:])):
print('=========================================================================================================\nTitle: {0}\nAuthor: {1}\nSeries: {2}\nLanguage: {3}\nDownloads: {4}'.format(result.title, result.author, result.series, result.language, len(result.downloads)))
if external or self.config.get('open_external', False):
open_url(QUrl(detail_url))
else:
d = WebStoreDialog(
self.gui, self.libgen.base_url, parent, detail_url)
d.setWindowTitle(self.name)
d.set_tags(self.config.get('tags', ''))
d.exec_()

class LibgenStoreWrapper(StoreBase):
name = PLUGIN_NAME
description = PLUGIN_DESCRIPTION
supported_platforms = ['windows', 'osx', 'linux']
author = PLUGIN_AUTHORS
version = PLUGIN_VERSION_TUPLE
version = PLUGIN_VERSION
minimum_calibre_version = (1, 0, 0)
affiliate = False
drm_free_only = True

def load_actual_plugin(self, gui):
'''
This method must return the actual interface action plugin object.
'''
#mod, cls = self.actual_plugin.split(':')
store = LibgenStore(gui, self.name)
self.actual_plugin_object = store#getattr(importlib.import_module(mod), cls)(gui, self.name)
return self.actual_plugin_object
self.actual_plugin_object = LibgenStore(gui, self.name)
return self.actual_plugin_object
Loading

0 comments on commit 192c4ea

Please sign in to comment.