|
@ -22,6 +22,7 @@ class AnimeOnDemandIE(InfoExtractor): |
|
|
_APPLY_HTML5_URL = 'https://www.anime-on-demand.de/html5apply' |
|
|
_APPLY_HTML5_URL = 'https://www.anime-on-demand.de/html5apply' |
|
|
_NETRC_MACHINE = 'animeondemand' |
|
|
_NETRC_MACHINE = 'animeondemand' |
|
|
_TESTS = [{ |
|
|
_TESTS = [{ |
|
|
|
|
|
# jap, OmU |
|
|
'url': 'https://www.anime-on-demand.de/anime/161', |
|
|
'url': 'https://www.anime-on-demand.de/anime/161', |
|
|
'info_dict': { |
|
|
'info_dict': { |
|
|
'id': '161', |
|
|
'id': '161', |
|
@ -30,17 +31,21 @@ class AnimeOnDemandIE(InfoExtractor): |
|
|
}, |
|
|
}, |
|
|
'playlist_mincount': 4, |
|
|
'playlist_mincount': 4, |
|
|
}, { |
|
|
}, { |
|
|
# Film wording is used instead of Episode |
|
|
|
|
|
|
|
|
# Film wording is used instead of Episode, ger/jap, Dub/OmU |
|
|
'url': 'https://www.anime-on-demand.de/anime/39', |
|
|
'url': 'https://www.anime-on-demand.de/anime/39', |
|
|
'only_matching': True, |
|
|
'only_matching': True, |
|
|
}, { |
|
|
}, { |
|
|
# Episodes without titles |
|
|
|
|
|
|
|
|
# Episodes without titles, jap, OmU |
|
|
'url': 'https://www.anime-on-demand.de/anime/162', |
|
|
'url': 'https://www.anime-on-demand.de/anime/162', |
|
|
'only_matching': True, |
|
|
'only_matching': True, |
|
|
}, { |
|
|
}, { |
|
|
# ger/jap, Dub/OmU, account required |
|
|
# ger/jap, Dub/OmU, account required |
|
|
'url': 'https://www.anime-on-demand.de/anime/169', |
|
|
'url': 'https://www.anime-on-demand.de/anime/169', |
|
|
'only_matching': True, |
|
|
'only_matching': True, |
|
|
|
|
|
}, { |
|
|
|
|
|
# Full length film, non-series, ger/jap, Dub/OmU, account required |
|
|
|
|
|
'url': 'https://www.anime-on-demand.de/anime/185', |
|
|
|
|
|
'only_matching': True, |
|
|
}] |
|
|
}] |
|
|
|
|
|
|
|
|
def _login(self): |
|
|
def _login(self): |
|
@ -110,35 +115,12 @@ class AnimeOnDemandIE(InfoExtractor): |
|
|
|
|
|
|
|
|
entries = [] |
|
|
entries = [] |
|
|
|
|
|
|
|
|
for num, episode_html in enumerate(re.findall( |
|
|
|
|
|
r'(?s)<h3[^>]+class="episodebox-title".+?>Episodeninhalt<', webpage), 1): |
|
|
|
|
|
episodebox_title = self._search_regex( |
|
|
|
|
|
(r'class="episodebox-title"[^>]+title=(["\'])(?P<title>.+?)\1', |
|
|
|
|
|
r'class="episodebox-title"[^>]+>(?P<title>.+?)<'), |
|
|
|
|
|
episode_html, 'episodebox title', default=None, group='title') |
|
|
|
|
|
if not episodebox_title: |
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
episode_number = int(self._search_regex( |
|
|
|
|
|
r'(?:Episode|Film)\s*(\d+)', |
|
|
|
|
|
episodebox_title, 'episode number', default=num)) |
|
|
|
|
|
episode_title = self._search_regex( |
|
|
|
|
|
r'(?:Episode|Film)\s*\d+\s*-\s*(.+)', |
|
|
|
|
|
episodebox_title, 'episode title', default=None) |
|
|
|
|
|
|
|
|
|
|
|
video_id = 'episode-%d' % episode_number |
|
|
|
|
|
|
|
|
|
|
|
common_info = { |
|
|
|
|
|
'id': video_id, |
|
|
|
|
|
'series': anime_title, |
|
|
|
|
|
'episode': episode_title, |
|
|
|
|
|
'episode_number': episode_number, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def extract_info(html, video_id): |
|
|
|
|
|
title, description = [None] * 2 |
|
|
formats = [] |
|
|
formats = [] |
|
|
|
|
|
|
|
|
for input_ in re.findall( |
|
|
for input_ in re.findall( |
|
|
r'<input[^>]+class=["\'].*?streamstarter_html5[^>]+>', episode_html): |
|
|
|
|
|
|
|
|
r'<input[^>]+class=["\'].*?streamstarter_html5[^>]+>', html): |
|
|
attributes = extract_attributes(input_) |
|
|
attributes = extract_attributes(input_) |
|
|
playlist_urls = [] |
|
|
playlist_urls = [] |
|
|
for playlist_key in ('data-playlist', 'data-otherplaylist'): |
|
|
for playlist_key in ('data-playlist', 'data-otherplaylist'): |
|
@ -215,28 +197,74 @@ class AnimeOnDemandIE(InfoExtractor): |
|
|
}) |
|
|
}) |
|
|
formats.extend(file_formats) |
|
|
formats.extend(file_formats) |
|
|
|
|
|
|
|
|
if formats: |
|
|
|
|
|
self._sort_formats(formats) |
|
|
|
|
|
|
|
|
return { |
|
|
|
|
|
'title': title, |
|
|
|
|
|
'description': description, |
|
|
|
|
|
'formats': formats, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
def extract_entries(html, video_id, common_info): |
|
|
|
|
|
info = extract_info(html, video_id) |
|
|
|
|
|
|
|
|
|
|
|
if info['formats']: |
|
|
|
|
|
self._sort_formats(info['formats']) |
|
|
f = common_info.copy() |
|
|
f = common_info.copy() |
|
|
f.update({ |
|
|
|
|
|
'title': title, |
|
|
|
|
|
'description': description, |
|
|
|
|
|
'formats': formats, |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
f.update(info) |
|
|
entries.append(f) |
|
|
entries.append(f) |
|
|
|
|
|
|
|
|
# Extract teaser only when full episode is not available |
|
|
|
|
|
if not formats: |
|
|
|
|
|
|
|
|
# Extract teaser/trailer only when full episode is not available |
|
|
|
|
|
if not info['formats']: |
|
|
m = re.search( |
|
|
m = re.search( |
|
|
r'data-dialog-header=(["\'])(?P<title>.+?)\1[^>]+href=(["\'])(?P<href>.+?)\3[^>]*>Teaser<', |
|
|
|
|
|
episode_html) |
|
|
|
|
|
|
|
|
r'data-dialog-header=(["\'])(?P<title>.+?)\1[^>]+href=(["\'])(?P<href>.+?)\3[^>]*>(?P<kind>Teaser|Trailer)<', |
|
|
|
|
|
html) |
|
|
if m: |
|
|
if m: |
|
|
f = common_info.copy() |
|
|
f = common_info.copy() |
|
|
f.update({ |
|
|
f.update({ |
|
|
'id': '%s-teaser' % f['id'], |
|
|
|
|
|
|
|
|
'id': '%s-%s' % (f['id'], m.group('kind').lower()), |
|
|
'title': m.group('title'), |
|
|
'title': m.group('title'), |
|
|
'url': compat_urlparse.urljoin(url, m.group('href')), |
|
|
'url': compat_urlparse.urljoin(url, m.group('href')), |
|
|
}) |
|
|
}) |
|
|
entries.append(f) |
|
|
entries.append(f) |
|
|
|
|
|
|
|
|
|
|
|
def extract_episodes(html): |
|
|
|
|
|
for num, episode_html in enumerate(re.findall( |
|
|
|
|
|
r'(?s)<h3[^>]+class="episodebox-title".+?>Episodeninhalt<', html), 1): |
|
|
|
|
|
episodebox_title = self._search_regex( |
|
|
|
|
|
(r'class="episodebox-title"[^>]+title=(["\'])(?P<title>.+?)\1', |
|
|
|
|
|
r'class="episodebox-title"[^>]+>(?P<title>.+?)<'), |
|
|
|
|
|
episode_html, 'episodebox title', default=None, group='title') |
|
|
|
|
|
if not episodebox_title: |
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
episode_number = int(self._search_regex( |
|
|
|
|
|
r'(?:Episode|Film)\s*(\d+)', |
|
|
|
|
|
episodebox_title, 'episode number', default=num)) |
|
|
|
|
|
episode_title = self._search_regex( |
|
|
|
|
|
r'(?:Episode|Film)\s*\d+\s*-\s*(.+)', |
|
|
|
|
|
episodebox_title, 'episode title', default=None) |
|
|
|
|
|
|
|
|
|
|
|
video_id = 'episode-%d' % episode_number |
|
|
|
|
|
|
|
|
|
|
|
common_info = { |
|
|
|
|
|
'id': video_id, |
|
|
|
|
|
'series': anime_title, |
|
|
|
|
|
'episode': episode_title, |
|
|
|
|
|
'episode_number': episode_number, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
extract_entries(episode_html, video_id, common_info) |
|
|
|
|
|
|
|
|
|
|
|
def extract_film(html, video_id): |
|
|
|
|
|
common_info = { |
|
|
|
|
|
'id': anime_id, |
|
|
|
|
|
'title': anime_title, |
|
|
|
|
|
'description': anime_description, |
|
|
|
|
|
} |
|
|
|
|
|
extract_entries(html, video_id, common_info) |
|
|
|
|
|
|
|
|
|
|
|
extract_episodes(webpage) |
|
|
|
|
|
|
|
|
|
|
|
if not entries: |
|
|
|
|
|
extract_film(webpage, anime_id) |
|
|
|
|
|
|
|
|
return self.playlist_result(entries, anime_id, anime_title, anime_description) |
|
|
return self.playlist_result(entries, anime_id, anime_title, anime_description) |