-
Notifications
You must be signed in to change notification settings - Fork 278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/show sub original release #7955
Changes from 9 commits
a199e80
c9a8c8b
6311ec0
bb84c45
26d1ad1
507e90b
1754d87
07f78c3
98c82f4
17ea616
2a53728
6ae5e57
7e9f32e
d6ce12c
75366df
8f32868
7586bec
5dc8cdf
3f1d499
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# coding=utf-8 | ||
"""Request handler for series and episodes.""" | ||
from __future__ import unicode_literals | ||
|
||
import logging | ||
|
||
from medusa import db | ||
|
||
from medusa.common import statusStrings | ||
from medusa.helper.exceptions import EpisodeDeletedException | ||
from medusa.logger.adapters.style import BraceAdapter | ||
from medusa.providers.generic_provider import GenericProvider | ||
from medusa.providers import get_provider_class | ||
from medusa.server.api.v2.base import BaseRequestHandler | ||
from medusa.server.api.v2.history import HistoryHandler | ||
from medusa.tv.episode import Episode, EpisodeNumber | ||
from medusa.tv.series import Series, SeriesIdentifier | ||
|
||
from os.path import basename | ||
|
||
log = BraceAdapter(logging.getLogger(__name__)) | ||
log.logger.addHandler(logging.NullHandler()) | ||
|
||
|
||
class EpisodeHistoryHandler(BaseRequestHandler): | ||
"""Episode history request handler.""" | ||
|
||
#: parent resource handler | ||
parent_handler = HistoryHandler | ||
#: resource name | ||
name = 'episode' | ||
#: identifier | ||
identifier = ('episode_slug', r'[\w-]+') | ||
#: path param | ||
path_param = ('path_param', r'\w+') | ||
#: allowed HTTP methods | ||
allowed_methods = ('GET', 'DELETE',) | ||
|
||
def get(self, series_slug, episode_slug, path_param): | ||
"""Query episode's history information. | ||
|
||
:param series_slug: series slug. E.g.: tvdb1234 | ||
:param episode_slug: episode slug. E.g.: s01e01 | ||
:param path_param: | ||
""" | ||
series_identifier = SeriesIdentifier.from_slug(series_slug) | ||
if not series_identifier: | ||
return self._bad_request('Invalid series slug') | ||
|
||
series = Series.find_by_identifier(series_identifier) | ||
if not series: | ||
return self._not_found('Series not found') | ||
|
||
if not episode_slug: | ||
return self._bad_request('Invalid episode slug') | ||
|
||
episode_number = EpisodeNumber.from_slug(episode_slug) | ||
if not episode_number: | ||
return self._not_found('Invalid episode number') | ||
|
||
episode = Episode.find_by_series_and_episode(series, episode_number) | ||
if not episode: | ||
return self._not_found('Episode not found') | ||
|
||
sql_base = ''' | ||
SELECT rowid, date, action, quality, | ||
provider, version, resource, size, proper_tags, | ||
indexer_id, showid, season, episode, manually_searched | ||
FROM history | ||
WHERE showid = ? AND indexer_id = ? AND season = ? AND episode = ? | ||
''' | ||
|
||
params = [series.series_id, series.indexer, episode.season, episode.episode] | ||
|
||
sql_base += ' ORDER BY date DESC' | ||
results = db.DBConnection().select(sql_base, params) | ||
|
||
def data_generator(): | ||
"""Read history data and normalize key/value pairs.""" | ||
for item in results: | ||
d = {} | ||
d['id'] = item['rowid'] | ||
|
||
if item['indexer_id'] and item['showid']: | ||
d['series'] = SeriesIdentifier.from_id(item['indexer_id'], item['showid']).slug | ||
|
||
d['status'] = item['action'] | ||
d['actionDate'] = item['date'] | ||
|
||
d['resource'] = basename(item['resource']) | ||
d['size'] = item['size'] | ||
d['properTags'] = item['proper_tags'] | ||
d['statusName'] = statusStrings.get(item['action']) | ||
d['season'] = item['season'] | ||
d['episode'] = item['episode'] | ||
d['manuallySearched'] = bool(item['manually_searched']) | ||
|
||
provider = get_provider_class(GenericProvider.make_id(item['provider'])) | ||
d['provider'] = {} | ||
if provider: | ||
d['provider']['id'] = provider.get_id() | ||
d['provider']['name'] = provider.name | ||
d['provider']['imageName'] = provider.image_name() | ||
|
||
yield d | ||
|
||
if not len(results): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think len() is needed here |
||
return self._not_found('History data not found for show {show} and episode {episode}'.format( | ||
show=series.identifier.slug, episode=episode.slug | ||
)) | ||
|
||
return self._ok(data=list(data_generator())) | ||
|
||
def delete(self, series_slug, episode_slug, **kwargs): | ||
"""Delete the episode.""" | ||
if not series_slug: | ||
return self._method_not_allowed('Deleting multiple series are not allowed') | ||
|
||
identifier = SeriesIdentifier.from_slug(series_slug) | ||
if not identifier: | ||
return self._bad_request('Invalid series identifier') | ||
|
||
series = Series.find_by_identifier(identifier) | ||
if not series: | ||
return self._not_found('Series not found') | ||
|
||
episode_number = EpisodeNumber.from_slug(episode_slug) | ||
if not episode_number: | ||
return self._bad_request('Invalid episode number') | ||
|
||
episode = Episode.find_by_series_and_episode(series, episode_number) | ||
if not episode: | ||
return self._not_found('Episode not found') | ||
|
||
try: | ||
episode.delete_episode() | ||
except EpisodeDeletedException: | ||
return self._no_content() | ||
else: | ||
return self._conflict('Unable to delete episode') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# coding=utf-8 | ||
"""Request handler for alias (scene exceptions).""" | ||
from __future__ import unicode_literals | ||
|
||
from medusa import db | ||
|
||
from medusa.server.api.v2.base import BaseRequestHandler | ||
from medusa.providers.generic_provider import GenericProvider | ||
from medusa.providers import get_provider_class | ||
from medusa.tv.series import SeriesIdentifier | ||
from os.path import basename | ||
from medusa.common import statusStrings | ||
|
||
|
||
class HistoryHandler(BaseRequestHandler): | ||
"""History request handler.""" | ||
|
||
#: resource name | ||
name = 'history' | ||
#: identifier | ||
identifier = ('series_slug', r'\w+') | ||
#: path param | ||
path_param = ('path_param', r'\w+') | ||
#: allowed HTTP methods | ||
allowed_methods = ('GET', 'POST', 'PUT', 'DELETE') | ||
|
||
def get(self, series_slug, path_param): | ||
"""Query history information.""" | ||
|
||
sql_base = ''' | ||
SELECT rowid, date, action, quality, | ||
provider, version, resource, size, | ||
indexer_id, showid, season, episode | ||
FROM history | ||
''' | ||
params = [] | ||
|
||
arg_page = self._get_page() | ||
arg_limit = self._get_limit(default=50) | ||
|
||
if series_slug is not None: | ||
series_identifier = SeriesIdentifier.from_slug(series_slug) | ||
if not series_identifier: | ||
return self._bad_request('Invalid series') | ||
|
||
sql_base += ' WHERE indexer_id = ? AND showid = ?' | ||
params += [series_identifier.indexer.id, series_identifier.id] | ||
|
||
sql_base += ' ORDER BY date DESC' | ||
results = db.DBConnection().select(sql_base, params) | ||
|
||
def data_generator(): | ||
"""Read log lines based on the specified criteria.""" | ||
start = arg_limit * (arg_page - 1) + 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you remove the +1 here you can remove the two -1 below There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, not sure. For ex. Page 1, with a Limit of 50. Will result in 1, page 2 will result in 51 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've changed it for now. |
||
|
||
for item in results[start - 1:start - 1 + arg_limit]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if there are less results than limit? Possible list index out of range? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that's possible, as it's slicing the results |
||
d = {} | ||
d['id'] = item['rowid'] | ||
|
||
if item['indexer_id'] and item['showid']: | ||
d['series'] = SeriesIdentifier.from_id(item['indexer_id'], item['showid']).slug | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here it's required. As for example for the history.mako page, we just want to get all history information. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But still indexer_id and showid must be in the DB? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ow yeah good point |
||
|
||
d['status'] = item['action'] | ||
d['actionDate'] = item['date'] | ||
|
||
d['resource'] = basename(item['resource']) | ||
d['size'] = item['size'] | ||
d['statusName'] = statusStrings.get(item['action']) | ||
d['season'] = item['season'] | ||
d['episode'] = item['episode'] | ||
d['manuallySearched'] = bool(item['manually_searched']) | ||
|
||
provider = get_provider_class(GenericProvider.make_id(item['provider'])) | ||
d['provider'] = {} | ||
if provider: | ||
d['provider']['id'] = provider.get_id() | ||
d['provider']['name'] = provider.name | ||
d['provider']['imageName'] = provider.image_name() | ||
|
||
yield d | ||
|
||
if not len(results): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above |
||
return self._not_found('History data not found') | ||
|
||
return self._paginate(data_generator=data_generator) | ||
|
||
def delete(self, identifier, **kwargs): | ||
"""Delete a history record.""" | ||
identifier = self._parse(identifier) | ||
if not identifier: | ||
return self._bad_request('Invalid history id') | ||
|
||
cache_db_con = db.DBConnection('cache.db') | ||
last_changes = cache_db_con.connection.total_changes | ||
cache_db_con.action('DELETE FROM history WHERE row_id = ?', [identifier]) | ||
if cache_db_con.connection.total_changes - last_changes <= 0: | ||
return self._not_found('History row not found') | ||
|
||
return self._no_content() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can these really be missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For an episode it can't.
But i've been overthinking the decistion to create the episode_history.py route. (
apiv2/history/tvdb12345/episode/s01e01
). But maybe I should just use a parameter to specify an episode?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's consistent with the rest of the routes. But since they can't be missing for episode, we should raise an exception or leave the check out completely?