home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Calibre / calibre-0.8.18.msi / file_280 / zeitde_sub.recipe < prev    next >
Text File  |  2011-09-09  |  13KB  |  215 lines

  1. #!/usr/bin/env  python
  2. # -*- coding: utf-8 mode: python -*-
  3.  
  4. __license__   = 'GPL v3'
  5. __copyright__ = '2010, Steffen Siebert <calibre at steffensiebert.de>'
  6. __docformat__ = 'restructuredtext de'
  7. __version__   = '1.5'
  8.  
  9. """
  10. Die Zeit EPUB
  11. """
  12.  
  13. import os, zipfile, re, cStringIO
  14. from calibre.web.feeds.news import BasicNewsRecipe
  15. from calibre.ptempfile import PersistentTemporaryFile
  16. from calibre import walk
  17. from urlparse import urlparse
  18. from contextlib import closing
  19. from calibre.utils.magick.draw import save_cover_data_to
  20.  
  21. class ZeitEPUBAbo(BasicNewsRecipe):
  22.  
  23.     title = u'Die Zeit'
  24.     description = u'Das EPUB Abo der Zeit (needs subscription)'
  25.     language = 'de'
  26.     lang = 'de-DE'
  27.  
  28.     __author__ = 'Steffen Siebert, revised by Tobias Isenberg (with some code by Kovid Goyal)'
  29.     needs_subscription = True
  30.  
  31.     conversion_options = {
  32.         'no_default_epub_cover' : True,
  33.         # fixing the wrong left margin
  34.         'mobi_ignore_margins' : True,
  35.         'keep_ligatures' : True,
  36.     }
  37.  
  38.     preprocess_regexps    = [
  39.         # filtering for correct dashes ("Gedankenstrich" and "bis")
  40.         (re.compile(u' (-|\u2212)(?=[ ,])'), lambda match: u' \u2013'),
  41.         (re.compile(r'(?<=\d)-(?=\d)'), lambda match: u'\u2013'), # number-number
  42.         (re.compile(u'(?<=\d,)-(?= ?\u20AC)'), lambda match: u'\u2013'), # ,- Euro
  43.         # fix the number dash number dash for the title image that was broken by the previous line
  44.         (re.compile(u'(?<=\d\d\d\d)\u2013(?=\d?\d\.png)'), lambda match: '-'),
  45.         # filtering for certain dash cases
  46.         (re.compile(r'Bild - Zeitung'), lambda match: 'Bild-Zeitung'), # the obvious
  47.         (re.compile(r'EMail'), lambda match: 'E-Mail'), # the obvious
  48.         (re.compile(r'SBahn'), lambda match: 'S-Bahn'), # the obvious
  49.         (re.compile(r'UBoot'), lambda match: 'U-Boot'), # the obvious
  50.         (re.compile(r'T Shirt'), lambda match: 'T-Shirt'), # the obvious
  51.         (re.compile(r'TShirt'), lambda match: 'T-Shirt'), # the obvious
  52.         # the next two lines not only fix errors but also create new ones. this is due to additional errors in
  53.         # the typesetting such as missing commas or wrongly placed dashes. but more is fixed than broken.
  54.         (re.compile(r'(?<!und|der|\w\w,) -(?=\w)'), lambda match: '-'), # space too much before a connecting dash
  55.         (re.compile(r'(?<=\w)- (?!und\b|oder\b|wie\b|aber\b|auch\b|sondern\b|bis\b|&|&\s|bzw\.|auf\b|eher\b)'), lambda match: '-'), # space too much after a connecting dash
  56.         # filtering for missing spaces before the month in long dates
  57.         (re.compile(u'(?<=\d)\.(?=(Januar|Februar|M\u00E4rz|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember))'), lambda match: '. '),
  58.         # filtering for other missing spaces
  59.         (re.compile(r'Stuttgart21'), lambda match: 'Stuttgart 21'), # the obvious
  60.         (re.compile(u'(?<=\d)(?=\u20AC)'), lambda match: u'\u2013'), # Zahl[no space]Euro
  61.         (re.compile(r':(?=[^\d\s</])'), lambda match: ': '), # missing space after colon
  62.         (re.compile(u'\u00AB(?=[^\-\.:;,\?!<\)\s])'), lambda match: u'\u00AB '), # missing space after closing quotation
  63.         (re.compile(u'(?<=[^\s\(>])\u00BB'), lambda match: u' \u00BB'), # missing space before opening quotation
  64.         (re.compile(r'(?<=[a-z])(?=(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII|XIII|XIV|XV|XVI|XVII|XVIII|XIX|XX)\.)'), lambda match: ' '), # missing space before Roman numeral
  65.         (re.compile(r'(?<=(I|V|X)\.)(?=[\w])'), lambda match: ' '), # missing space after Roman numeral
  66.         (re.compile(r'(?<=(II|IV|VI|IX|XI|XV|XX)\.)(?=[\w])'), lambda match: ' '), # missing space after Roman numeral
  67.         (re.compile(r'(?<=(III|VII|XII|XIV|XVI|XIX)\.)(?=[\w])'), lambda match: ' '), # missing space after Roman numeral
  68.         (re.compile(r'(?<=(VIII|XIII|XVII)\.)(?=[\w])'), lambda match: ' '), # missing space after Roman numeral
  69.         (re.compile(r'(?<=(XVIII)\.)(?=[\w])'), lambda match: ' '), # missing space after Roman numeral
  70.         (re.compile(r'(?<=[A-Za-z├ä├û├£├ñ├╢├╝]),(?=[A-Za-z├ä├û├£├ñ├╢├╝])'), lambda match: ', '), # missing space after comma
  71.         (re.compile(r'(?<=[a-z├ñ├╢├╝])\.(?=[A-Z├ä├û├£][A-Za-z├ä├û├£├ñ├╢├╝])'), lambda match: '. '), # missing space after full-stop
  72.         (re.compile(r'(?<=[uU]\.) (?=a\.)'), lambda match: u'\u2008'), # fix abbreviation that was potentially broken previously
  73.         (re.compile(r'(?<=[iI]\.) (?=A\.)'), lambda match: u'\u2008'), # fix abbreviation that was potentially broken previously
  74.         (re.compile(r'(?<=[zZ]\.) (?=B\.)'), lambda match: u'\u2008'), # fix abbreviation that was potentially broken previously
  75.         (re.compile(r'(?<=\w\.) (?=[A-Z][a-z]*@)'), lambda match: ''), # fix e-mail address that was potentially broken previously
  76.         (re.compile(r'(?<=\d)[Pp]rozent'), lambda match: ' Prozent'),
  77.         (re.compile(r'\.\.\.\.+'), lambda match: '...'), # too many dots (....)
  78.         (re.compile(r'(?<=[^\s])\.\.\.'), lambda match: ' ...'), # spaces before ...
  79.         (re.compile(r'\.\.\.(?=[^\s])'), lambda match: '... '), # spaces after ...
  80.         (re.compile(r'(?<=[\[\(]) \.\.\. (?=[\]\)])'), lambda match: '...'), # fix special cases of ... in brackets
  81.         (re.compile(u'(?<=[\u00BB\u203A]) \.\.\.'), lambda match: '...'), # fix special cases of ... after a quotation mark
  82.         (re.compile(u'\.\.\. (?=[\u00AB\u2039,])'), lambda match: '...'), # fix special cases of ... before a quotation mark or comma
  83.         # fix missing spaces between numbers and any sort of units, possibly with dot
  84.         (re.compile(r'(?<=\d)(?=(Femto|Piko|Nano|Mikro|Milli|Zenti|Dezi|Hekto|Kilo|Mega|Giga|Tera|Peta|Tausend|Trilli|Kubik|Quadrat|Meter|Uhr|Jahr|Schuljahr|Seite))'), lambda match: ' '),
  85.         (re.compile(r'(?<=\d\.)(?=(Femto|Piko|Nano|Mikro|Milli|Zenti|Dezi|Hekto|Kilo|Mega|Giga|Tera|Peta|Tausend|Trilli|Kubik|Quadrat|Meter|Uhr|Jahr|Schuljahr|Seite))'), lambda match: ' '),
  86.         # fix wrong spaces
  87.         (re.compile(r'(?<=<p class="absatz">[A-Z├ä├û├£]) (?=[a-z├ñ├╢├╝\-])'), lambda match: ''), # at beginning of paragraphs
  88.         (re.compile(u' \u00AB'), lambda match: u'\u00AB '), # before closing quotation
  89.         (re.compile(u'\u00BB '), lambda match: u' \u00BB'), # after opening quotation
  90.         # filtering for spaces in large numbers for better readability
  91.         (re.compile(r'(?<=\d\d)(?=\d\d\d[ ,\.;\)<\?!-])'), lambda match: u'\u2008'), # end of the number with some character following
  92.         (re.compile(r'(?<=\d\d)(?=\d\d\d. )'), lambda match: u'\u2008'), # end of the number with full-stop following, then space is necessary (avoid file names)
  93.         (re.compile(u'(?<=\d)(?=\d\d\d\u2008)'), lambda match: u'\u2008'), # next level
  94.         (re.compile(u'(?<=\d)(?=\d\d\d\u2008)'), lambda match: u'\u2008'), # next level
  95.         (re.compile(u'(?<=\d)(?=\d\d\d\u2008)'), lambda match: u'\u2008'), # next level
  96.         (re.compile(u'(?<=\d)(?=\d\d\d\u2008)'), lambda match: u'\u2008'), # next level
  97.         # filtering for unicode characters that are missing on the Kindle,
  98.         # try to replace them with meaningful work-arounds
  99.         (re.compile(u'\u2080'), lambda match: '<span style="font-size: 40%;">0</span>'), # subscript-0
  100.         (re.compile(u'\u2081'), lambda match: '<span style="font-size: 40%;">1</span>'), # subscript-1
  101.         (re.compile(u'\u2082'), lambda match: '<span style="font-size: 40%;">2</span>'), # subscript-2
  102.         (re.compile(u'\u2083'), lambda match: '<span style="font-size: 40%;">3</span>'), # subscript-3
  103.         (re.compile(u'\u2084'), lambda match: '<span style="font-size: 40%;">4</span>'), # subscript-4
  104.         (re.compile(u'\u2085'), lambda match: '<span style="font-size: 40%;">5</span>'), # subscript-5
  105.         (re.compile(u'\u2086'), lambda match: '<span style="font-size: 40%;">6</span>'), # subscript-6
  106.         (re.compile(u'\u2087'), lambda match: '<span style="font-size: 40%;">7</span>'), # subscript-7
  107.         (re.compile(u'\u2088'), lambda match: '<span style="font-size: 40%;">8</span>'), # subscript-8
  108.         (re.compile(u'\u2089'), lambda match: '<span style="font-size: 40%;">9</span>'), # subscript-9
  109.         # always chance CO2
  110.         (re.compile(r'CO2'), lambda match: 'CO<span style="font-size: 40%;">2</span>'), # CO2
  111.         # remove *** paragraphs
  112.         (re.compile(r'<p class="absatz">\*\*\*</p>'), lambda match: ''),
  113.         # better layout for the top line of each article
  114.         (re.compile(u'(?<=DIE ZEIT N\u00B0 \d /) (?=\d\d)'), lambda match: ' 20'), # proper year in edition number
  115.         (re.compile(u'(?<=DIE ZEIT N\u00B0 \d\d /) (?=\d\d)'), lambda match: ' 20'), # proper year in edition number
  116.         (re.compile(u'(?<=>)(?=DIE ZEIT N\u00B0 \d\d / 20\d\d)'), lambda match: u' \u2014 '), # m-dash between category and DIE ZEIT
  117.     ]
  118.  
  119.     def build_index(self):
  120.         domain = "https://premium.zeit.de"
  121.         url = domain + "/abo/zeit_digital"
  122.         browser = self.get_browser()
  123.  
  124.         # new login process
  125.         response = browser.open(url)
  126.         browser.select_form(nr=2)
  127.         browser.form['name']=self.username
  128.         browser.form['pass']=self.password
  129.         browser.submit()
  130.         # now find the correct file, we will still use the ePub file
  131.         epublink = browser.find_link(text_regex=re.compile('.*Ausgabe als Datei im ePub-Format.*'))
  132.         response = browser.follow_link(epublink)
  133.         self.report_progress(1,_('next step'))
  134.  
  135.         tmp = PersistentTemporaryFile(suffix='.epub')
  136.         self.report_progress(0,_('downloading epub'))
  137.         tmp.write(response.read())
  138.         tmp.close()
  139.  
  140.         zfile = zipfile.ZipFile(tmp.name, 'r')
  141.         self.report_progress(0,_('extracting epub'))
  142.  
  143.         zfile.extractall(self.output_dir)
  144.  
  145.         tmp.close()
  146.  
  147.         index = os.path.join(self.output_dir, 'content.opf')
  148.  
  149.         self.report_progress(1,_('epub downloaded and extracted'))
  150.  
  151.         # doing regular expression filtering
  152.         for path in walk('.'):
  153.             (shortname, extension) = os.path.splitext(path)
  154.             if extension.lower() in ('.html', '.htm', '.xhtml'):
  155.                 with open(path, 'r+b') as f:
  156.                     raw = f.read()
  157.                     raw = raw.decode('utf-8')
  158.                     for pat, func in self.preprocess_regexps:
  159.                         raw = pat.sub(func, raw)
  160.                     f.seek(0)
  161.                     f.truncate()
  162.                     f.write(raw.encode('utf-8'))
  163.  
  164.         # adding real cover
  165.         self.report_progress(0,_('trying to download cover image (titlepage)'))
  166.         self.download_cover()
  167.         self.conversion_options["cover"] = self.cover_path
  168.  
  169.         return index
  170.  
  171.     # getting url of the cover
  172.     def get_cover_url(self):
  173.         self.log.warning('Downloading cover')
  174.         try:
  175.             self.log.warning('Trying PDF-based cover')
  176.             domain = "https://premium.zeit.de"
  177.             url = domain + "/abo/zeit_digital"
  178.             browser = self.get_browser()
  179.  
  180.             # new login process
  181.             browser.open(url)
  182.             browser.select_form(nr=2)
  183.             browser.form['name']=self.username
  184.             browser.form['pass']=self.password
  185.             browser.submit()
  186.             # actual cover search
  187.             pdflink = browser.find_link(url_regex=re.compile('system/files/epaper/DZ/pdf/DZ_ePaper*'))
  188.             cover_url = urlparse(pdflink.base_url)[0]+'://'+urlparse(pdflink.base_url)[1]+''+(urlparse(pdflink.url)[2]).replace('ePaper_','').replace('.pdf','_001.pdf')
  189.             self.log.warning('PDF link found:')
  190.             self.log.warning(cover_url)
  191.             # download the cover (has to be here due to new login process)
  192.             with closing(browser.open(cover_url)) as r:
  193.                 cdata = r.read()
  194.             from calibre.ebooks.metadata.pdf import get_metadata
  195.             stream = cStringIO.StringIO(cdata)
  196.             cdata = None
  197.             mi = get_metadata(stream)
  198.             if mi.cover_data and mi.cover_data[1]:
  199.                 cdata = mi.cover_data[1]
  200.  
  201.             cpath = os.path.join(self.output_dir, 'cover.jpg')
  202.             save_cover_data_to(cdata, cpath)
  203.             cover_url = cpath
  204.  
  205.         except:
  206.             self.log.warning('Trying low-res cover')
  207.             try:
  208.                 inhalt = self.index_to_soup('http://www.zeit.de/inhalt')
  209.                 cover_url = inhalt.find('div', attrs={'class':'singlearchive clearfix'}).img['src'].replace('icon_','')
  210.             except:
  211.                 self.log.warning('Using static old low-res cover')
  212.                 cover_url = 'http://images.zeit.de/bilder/titelseiten_zeit/1946/001_001.jpg'
  213.         return cover_url
  214.  
  215.