PageRenderTime 152ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/subliminal/providers/podnapisi.py

https://github.com/abenea/subliminal
Python | 150 lines | 119 code | 12 blank | 19 comment | 32 complexity | ce1714ae1655d102bacfb4ce502a13c5 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import io
  4. import logging
  5. import re
  6. import xml.etree.ElementTree
  7. import zipfile
  8. import babelfish
  9. import bs4
  10. import guessit
  11. import requests
  12. from . import Provider
  13. from .. import __version__
  14. from ..exceptions import ProviderError
  15. from ..subtitle import Subtitle, fix_line_endings, compute_guess_matches
  16. from ..video import Episode, Movie
  17. logger = logging.getLogger(__name__)
  18. babelfish.language_converters.register('podnapisi = subliminal.converters.podnapisi:PodnapisiConverter')
  19. class PodnapisiSubtitle(Subtitle):
  20. provider_name = 'podnapisi'
  21. def __init__(self, language, id, releases, hearing_impaired, page_link, series=None, season=None, episode=None, # @ReservedAssignment
  22. title=None, year=None):
  23. super(PodnapisiSubtitle, self).__init__(language, hearing_impaired, page_link)
  24. self.id = id
  25. self.releases = releases
  26. self.hearing_impaired = hearing_impaired
  27. self.series = series
  28. self.season = season
  29. self.episode = episode
  30. self.title = title
  31. self.year = year
  32. def compute_matches(self, video):
  33. matches = set()
  34. # episode
  35. if isinstance(video, Episode):
  36. # series
  37. if video.series and self.series.lower() == video.series.lower():
  38. matches.add('series')
  39. # season
  40. if video.season and self.season == video.season:
  41. matches.add('season')
  42. # episode
  43. if video.episode and self.episode == video.episode:
  44. matches.add('episode')
  45. # guess
  46. for release in self.releases:
  47. matches |= compute_guess_matches(video, guessit.guess_episode_info(release + '.mkv'))
  48. # movie
  49. elif isinstance(video, Movie):
  50. # title
  51. if video.title and self.title.lower() == video.title.lower():
  52. matches.add('title')
  53. # guess
  54. for release in self.releases:
  55. matches |= compute_guess_matches(video, guessit.guess_movie_info(release + '.mkv'))
  56. # year
  57. if self.year == video.year:
  58. matches.add('year')
  59. return matches
  60. class PodnapisiProvider(Provider):
  61. languages = {babelfish.Language.frompodnapisi(l) for l in babelfish.language_converters['podnapisi'].codes}
  62. video_types = (Episode, Movie)
  63. server = 'http://simple.podnapisi.net'
  64. link_re = re.compile('^.*(?P<link>/ppodnapisi/predownload/i/\d+/k/.*$)')
  65. def initialize(self):
  66. self.session = requests.Session()
  67. self.session.headers = {'User-Agent': 'Subliminal/%s' % __version__.split('-')[0]}
  68. def terminate(self):
  69. self.session.close()
  70. def get(self, url, params=None, is_xml=True):
  71. """Make a GET request on `url` with the given parameters
  72. :param string url: part of the URL to reach with the leading slash
  73. :param dict params: params of the request
  74. :param bool xml: whether the response content is XML or not
  75. :return: the response
  76. :rtype: :class:`xml.etree.ElementTree.Element` or :class:`bs4.BeautifulSoup`
  77. """
  78. r = self.session.get(self.server + '/ppodnapisi' + url, params=params, timeout=10)
  79. if r.status_code != 200:
  80. raise ProviderError('Request failed with status code %d' % r.status_code)
  81. if is_xml:
  82. return xml.etree.ElementTree.fromstring(r.content)
  83. else:
  84. return bs4.BeautifulSoup(r.content, ['permissive'])
  85. def query(self, language, series=None, season=None, episode=None, title=None, year=None):
  86. params = {'sXML': 1, 'sJ': language.podnapisi}
  87. if series and season and episode:
  88. params['sK'] = series
  89. params['sTS'] = season
  90. params['sTE'] = episode
  91. elif title:
  92. params['sK'] = title
  93. else:
  94. raise ValueError('Missing parameters series and season and episode or title')
  95. if year:
  96. params['sY'] = year
  97. logger.debug('Searching episode %r', params)
  98. subtitles = []
  99. while True:
  100. root = self.get('/search', params)
  101. if not int(root.find('pagination/results').text):
  102. logger.debug('No subtitle found')
  103. break
  104. if series and season and episode:
  105. subtitles.extend([PodnapisiSubtitle(language, int(s.find('id').text),
  106. s.find('release').text.split() if s.find('release').text else [],
  107. 'n' in (s.find('flags').text or ''), s.find('url').text,
  108. series=series, season=season, episode=episode,
  109. year=s.find('year').text)
  110. for s in root.findall('subtitle')])
  111. elif title:
  112. subtitles.extend([PodnapisiSubtitle(language, int(s.find('id').text),
  113. s.find('release').text.split() if s.find('release').text else [],
  114. 'n' in (s.find('flags').text or ''), s.find('url').text,
  115. title=title, year=s.find('year').text)
  116. for s in root.findall('subtitle')])
  117. if int(root.find('pagination/current').text) >= int(root.find('pagination/count').text):
  118. break
  119. params['page'] = int(root.find('pagination/current').text) + 1
  120. return subtitles
  121. def list_subtitles(self, video, languages):
  122. if isinstance(video, Episode):
  123. return [s for l in languages for s in self.query(l, series=video.series, season=video.season,
  124. episode=video.episode, year=video.year)]
  125. elif isinstance(video, Movie):
  126. return [s for l in languages for s in self.query(l, title=video.title, year=video.year)]
  127. def download_subtitle(self, subtitle):
  128. r = self.session.get(subtitle.page_link + '/download', timeout=10)
  129. if r.status_code != 200:
  130. raise ProviderError('Request failed with status code %d' % r.status_code)
  131. with zipfile.ZipFile(io.BytesIO(r.content)) as zf:
  132. if len(zf.namelist()) > 1:
  133. raise ProviderError('More than one file to unzip')
  134. subtitle.content = fix_line_endings(zf.read(zf.namelist()[0]))