Skip to content

Commit

Permalink
Feature/show sub original release (#7955)
Browse files Browse the repository at this point in the history
* Add history api route. (python apiv2 changes)
This was already prepared for another PR, where it will be used by the snatch-selection (vue) component and the history (vue) component.
For this addition to the subtitle-search.vue component. Only one of the actions in history.js is used.

* Add the last release name from the history table, to the subtitle-search.vue manual search table (if available)

* Fix a small front end (racing condition) bug.

* Only show release names when status in Snatched or Downloaded

* Fix lint errors

* Update api-description

* Added "provider" to api-description.yml

* Updated changelog

* Review comments.

* Re-arange subtitle-search.vue to make it more test friendly
* Fixed jest tests
* Updated jest snapshot

* Fix getting proper_tags & manually_searched

* make sure the pagination response is valid.

* Disabling the history tests.

* Use the provider field, as a generic string field.

* Fix flake warnings

* flake

Co-authored-by: Dario <medariox@users.noreply.github.com>
  • Loading branch information
p0psicles and medariox authored Apr 19, 2020
1 parent 90e3cba commit 1a8dfb5
Show file tree
Hide file tree
Showing 18 changed files with 2,361 additions and 1,707 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#### Improvements
- Add show names with dashes to guessit expected titles ([#7918](https://github.com/pymedusa/Medusa/pull/7918))
- Provider YggTorrents: Add 'saison' as a season pack search keyword ([#7920](https://github.com/pymedusa/Medusa/pull/7920))
- Show Snatched or Downloaded release name when manually picking a subtitle ([#7955](https://github.com/pymedusa/Medusa/pull/7955))

#### Fixes
- Fixed root dirs not always shown on Home page ([#7921](https://github.com/pymedusa/Medusa/pull/7921))
Expand Down
156 changes: 154 additions & 2 deletions dredd/api-description.yml
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ paths:
The Episode endpoint returns information about the Episodes from a given Series.
parameters:
- $ref: '#/parameters/series-id'
name: seriesid
- name: season
in: query
required: false
Expand Down Expand Up @@ -995,6 +994,91 @@ paths:
400:
$ref: '#/responses/error'
x-disabled: true
/history:
get:
summary: Return history entries to a specific show
description: |
The history endpoint returns logged activities stored in the history table, like episodes snatched and downloaded. Or downloaded subtitles for an episode.
parameters:
- $ref: '#/parameters/page'
- $ref: '#/parameters/limit'
- $ref: '#/parameters/sort'
responses:
200:
$ref: '#/responses/pagination_history'
x-disabled: true
400:
$ref: '#/responses/error'
description: Invalid series id or pagination parameters
x-request:
path-params:
seriesid: asdf
x-disabled: true
404:
$ref: '#/responses/error'
description: Series not found
x-request:
path-params:
seriesid: tvdb999999999
x-disabled: true
/history/{seriesid}:
get:
summary: Return history entries to a specific show
description: |
The history endpoint returns logged activities stored in the history table, like episodes snatched and downloaded. Or downloaded subtitles for an episode.
parameters:
- $ref: '#/parameters/series-id'
- $ref: '#/parameters/page'
- $ref: '#/parameters/limit'
- $ref: '#/parameters/sort'
responses:
200:
$ref: '#/responses/pagination_history'
x-disabled: true
400:
$ref: '#/responses/error'
description: Invalid series id or pagination parameters
x-request:
path-params:
seriesid: asdf
x-disabled: true
404:
$ref: '#/responses/error'
description: Series not found
x-request:
path-params:
seriesid: tvdb999999999
x-disabled: true
/history/{seriesid}/episode/{episodeid}:
get:
summary: Return history entries for a specific episode
description: |
The histories episode endpoint returns history entries for a specific episode
parameters:
- $ref: '#/parameters/series-id'
- $ref: '#/parameters/episode-id'
responses:
200:
description: Array of History entries
schema:
type: array
items:
$ref: '#/definitions/History'
x-disabled: true
400:
$ref: '#/responses/error'
description: Invalid series id or pagination parameters
x-request:
path-params:
seriesid: asdf
x-disabled: true
404:
$ref: '#/responses/error'
description: Series not found
x-request:
path-params:
seriesid: tvdb999999999
x-disabled: true

definitions:
Series:
Expand Down Expand Up @@ -2721,6 +2805,51 @@ definitions:
overview:
type: string
description: Episode status/quality overview string

History:
description: History object
type: object
properties:
id:
type: integer
format: int32
description: Internal id for the history row
series:
type: string
description: Series slug (if available)
status:
type: integer
format: int32
description: Status (numberic)
statusName:
type: string
description: Status description
actionDate:
type: integer
format: int32
description: Date of when the history entrie was stored
resource:
type: string
description: Description of what was stored
example:
- The release name for a statusName of "Downloaded" or "Snatched"
- The language of a subitle downloaded fo ra statusName of "Subtitled"
size:
type: integer
description: Snatched or Downloaded filesize
season:
type: integer
description: Season number
episode:
type: integer
description: Episode number
manuallySearched:
type: boolean
description: Specifies if an episode was snatched or downloaded through a manual search
provider:
type: string
description: Provider of the history record. For example, the provider id if origin from snatch/download.

parameters:
detailed:
name: detailed
Expand Down Expand Up @@ -2762,7 +2891,7 @@ parameters:
x-example: tvdb301824
type: string
episode-id:
name: episode-id
name: episodeid
in: path
required: true
description: The episode id to retrieve. E.g. s02e03, e34 or 2016-12-31
Expand Down Expand Up @@ -2925,3 +3054,26 @@ responses:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
pagination_history:
description: A paged array of history records
headers:
X-Pagination-Page:
type: integer
format: int32
description: The page number
X-Pagination-Limit:
type: integer
format: int32
description: The pagination limit
X-Pagination-Count:
type: integer
format: int32
description: The total items count
Link:
type: string
description: "The pagination links: next, last, first and previous"
schema:
type: array
items:
$ref: '#/definitions/History'

99 changes: 99 additions & 0 deletions medusa/server/api/v2/episode_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# coding=utf-8
"""Request handler for series and episodes."""
from __future__ import unicode_literals

import logging
from os.path import basename

from medusa import db
from medusa.common import statusStrings
from medusa.logger.adapters.style import BraceAdapter
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


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',)

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']
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'])
d['provider'] = item['provider']

yield d

if not results:
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()))
92 changes: 92 additions & 0 deletions medusa/server/api/v2/history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# coding=utf-8
"""Request handler for alias (scene exceptions)."""
from __future__ import unicode_literals

from os.path import basename

from medusa import db
from medusa.common import statusStrings
from medusa.server.api.v2.base import BaseRequestHandler
from medusa.tv.series import SeriesIdentifier


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):
"""
Get history records.
History records can be specified using a show slug.
"""
sql_base = """
SELECT rowid, date, action, quality,
provider, version, proper_tags, manually_searched,
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 and paginate history records."""
start = arg_limit * (arg_page - 1)

for item in results[start:start + arg_limit]:
d = {}
d['id'] = item['rowid']
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'])
d['provider'] = item['provider']

yield d

if not results:
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')

main_db_con = db.DBConnection()
last_changes = main_db_con.connection.total_changes
main_db_con.action('DELETE FROM history WHERE row_id = ?', [identifier])
if main_db_con.connection.total_changes - last_changes <= 0:
return self._not_found('History row not found')

return self._no_content()
Loading

0 comments on commit 1a8dfb5

Please sign in to comment.