Skip to content

Commit

Permalink
Feature/vueify home (#5345)
Browse files Browse the repository at this point in the history
* first run at banner

Signed-off-by: Alexis Tyler <[email protected]>

* fix column sorter

Signed-off-by: Alexis Tyler <[email protected]>

* remap stats into showList

Signed-off-by: Alexis Tyler <[email protected]>

* fix show size being undefined

Signed-off-by: Alexis Tyler <[email protected]>

* replaced downloadStats with progress bar component

Signed-off-by: Alexis Tyler <[email protected]>

* removed jQuery from progress-bar, moved banner to own file and started on simple layout

Signed-off-by: Alexis Tyler <[email protected]>

* load banner directly in home layout

Signed-off-by: Alexis Tyler <[email protected]>

* fix allowed & preferred qualities

Signed-off-by: Alexis Tyler <[email protected]>

* switch simple home layout to vue

Signed-off-by: Alexis Tyler <[email protected]>

* fix series listType with tabs

Signed-off-by: Alexis Tyler <[email protected]>

* skip mako when rendering vue layouts

Signed-off-by: Alexis Tyler <[email protected]>

* Tried fixing the build.
Still a number of errors in it, because of the move to ./helpers.

* fix imports/exports

Signed-off-by: Alexis Tyler <[email protected]>

* Fixed some issues caused by conflicts.
* Moved the logic to merge shows and stats, to the shows module. Stats are pulled in through the rootState.

* Moved from home.mako to SFC home.vue.
* Fixed some foobars.

* Added basic (minimal) vue-good-table to simple.vue.

* Add fields to vue-good-table.
* Moved xemNumbering from detailed to show generic.

* Calculate local time in apiv2 stats.
Return in epAirsNext and epAirsPrev

* Add prev_airdate to series.py
* Needed for the home pages.

* Moved the date formatting to store/modules/layout.js
* Removed the date formatting from display-show.vue
* Used the date formatting in home/simple.vue
* Removed airs key from shows.stats. As it's now directly on the Show.

* Add banner.vue

* Attempt at trying to add small.vue.
* not working

* For some weird reason <component> does not accept "small" for the :is property.
* That's why i'm renaming it on the fly to "smallposter".
* Add placeholder for poster.vue.

* Started vueifying home/poster.vue
* Added package vue-isotope
* Added package vanilla-lazyload

* Home poster layout:
* Add package vue-images-loaded
* Fix multiple ui bugs
* Added sorting

* Sorting asc/desc order.

* Fix filtering by text

* Remove reload of page.

* Fixed link to displayShow

* Typo. (ready for some testing)

* yarn dev

* Fix showStats if show does not have any episodes.

* Fixed issue with merging showStats

* Fix updating vue-isotope when sliding the size slide bar.

* Fixed scaling images with slidebar.

* Added Action "ADD_SHOWS" to load shows in bulk.
Much faster, as it commits entire array.

* Added mutation ADD_SHOWS to mutation-types.js

* Fixed loading poster size.
* Improved performance loading images.
* Added lazy loading to poster layout.

* Move the loading of the shows to app.js.
This is needed to prevent duplicate loading.

* Use v-model in stead.

* Fix poster sort UI components.
* Save sorting to backend.
* Fix updating layout when loading page.

* Fix home layout for split shows into Anime / Series.
* Don't display Series/Anime when only shows in one category.
* First version of tabbed layout, using vue-nav-tabs
* Fix display-show.vue, after going from display-show.vue to home.vue, it's still showing the summaryBackground css.

* Re-enable dragging of the show list-types.
* Added a new component vue-draggable

* Fix display-show.vue and show-header.vue css styling.
* Change to scoped, to prevent the styling from leaking to other pages.

* Fix styling for the home table layouts.

* Add column filters to simple.vue layout.

* Only provide key for getting cookies.

* Shorten preset, to make it more uniform.

* Fix light/dark themes

* Add backstretch component with random image to home

* Move selectedRootIndex config property to layout route.
Add filtering posters by location (rootDir)

* rearange home and poster UI controls.
* Made filter by name, layout universal.
* Moved the Poster layout specific controls to poster.vue.

* yarn dev

* Improve prev_episode / next_episode calculating.

* Improve formatting

* Improve calculating columns Active and Xem.

* Fix the table layouts, sorting.
* Pack new version of vue-good-table.

* Fix duplicate loading of shows.js
* Fixed layout of input selects, with multiple screen widths.

* Improved dark/light css styling

* Replace styling the summaryBackground banner and the checkboxControlsBackground with Vue refs.
* This was jquery before. But we need to get rid of jquery.
* Fix History.mako links to displayShow shows as blue link highlighted.

* Fix vgt-tabs styling
* Fix vgt-dropdown menu background color.
* Don't show Series/Anime when using tabs.

* Add date or status to poster layout.

* Fix poster layout sorting.

* Fix test_config tests.

* eslint and css-lint cleanup.

* Bump vue-good-table version

* Added new npm packages:
* vue-nav-tabs
* vueisotope
* vue-images-loaded
* vanilla-lazyload

* Increase width between indexer images.
* Center network logo

* Fix missing class tvShow.

* Dark styling on the vue-nav-tabs and vue-good-table

* Fixed arrange posters on tab change.

* Move poster size slider jquery to lock in component.

* Fixing pytest

* Fix pytest

* Fix lint and lint-css errors.

* Fixed test_config layout test.

* recreate yarn.lock

* yarn cache clean && yarn dev

* Fix some of the css-lint errors.

* Use testing-library/jest-dom to replace deprecated isVisible()

* Update snapshots

* Added test for lazy loading in asset.spec.js

* Fix pep8 warnings

* Fix error in pytest running on py2.

* rebundle

* Improve show loading.
Make sure all api/v2/series.py calls are handled throug the Promise.all() method.
* Added component load-progress-bar.vue.

* Fix bug non-vue routes not loading anymore.
* Removed dark style for the load-progress-bar.

* Move shared vue code for the show-list table layouts, to a dedicated mixin.
* Created a new root mixins folder.

* import error

* Improve date sorting for small, simple and banner.

* implemented review comments

* Save column sorting for simple.vue, smallposter.vue and banner.vue layouts in cookie.

* Fix getBoundClientRect error.
* Prob. in some race conditions.

* Fix storing he pagination per page count.
* Should be stored into cookies again.

* Make sure the load-progress-bar is full width on smaller devices.

Co-authored-by: P0psicles <[email protected]>
Co-authored-by: Dario <[email protected]>
  • Loading branch information
3 people authored May 21, 2020
1 parent 46f639e commit 0b346d6
Show file tree
Hide file tree
Showing 66 changed files with 6,146 additions and 7,846 deletions.
1 change: 1 addition & 0 deletions medusa/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2134,6 +2134,7 @@ def load_shows_from_db():
for sql_show in sql_results:
try:
cur_show = Series(sql_show['indexer'], sql_show['indexer_id'])
cur_show.prev_episode()
cur_show.next_episode()
app.showList.append(cur_show)
except Exception as error:
Expand Down
10 changes: 5 additions & 5 deletions medusa/server/api/v2/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ class ConfigHandler(BaseRequestHandler):
#: patch mapping
patches = {
# Main
'selectedRootIndex': IntegerField(app, 'SELECTED_ROOT'),
'rootDirs': ListField(app, 'ROOT_DIRS'),

'showDefaults.status': EnumField(app, 'STATUS_DEFAULT', (SKIPPED, WANTED, IGNORED), int),
Expand Down Expand Up @@ -474,6 +473,7 @@ class ConfigHandler(BaseRequestHandler):
'layout.backlogOverview.status': StringField(app, 'BACKLOG_STATUS'),
'layout.timeStyle': StringField(app, 'TIME_PRESET_W_SECONDS'),
'layout.dateStyle': StringField(app, 'DATE_PRESET'),
'layout.selectedRootIndex': IntegerField(app, 'SELECTED_ROOT'),
}

def get(self, identifier, path_param=None):
Expand Down Expand Up @@ -615,8 +615,6 @@ def data_main():
section_data['logs']['subliminalLog'] = bool(app.SUBLIMINAL_LOG)
section_data['logs']['privacyLevel'] = app.PRIVACY_LEVEL

section_data['selectedRootIndex'] = int_default(app.SELECTED_ROOT, -1) # All paths

# Added for config - main, needs refactoring in the structure.
section_data['launchBrowser'] = bool(app.LAUNCH_BROWSER)
section_data['defaultPage'] = app.DEFAULT_PAGE
Expand Down Expand Up @@ -1174,7 +1172,7 @@ def data_layout():

section_data['wide'] = bool(app.LAYOUT_WIDE)

section_data['posterSortdir'] = int(app.POSTER_SORTDIR)
section_data['posterSortdir'] = int(app.POSTER_SORTDIR or 0)
section_data['themeName'] = app.THEME_NAME
section_data['animeSplitHomeInTabs'] = bool(app.ANIME_SPLIT_HOME_IN_TABS)
section_data['animeSplitHome'] = bool(app.ANIME_SPLIT_HOME)
Expand All @@ -1192,11 +1190,13 @@ def data_layout():
section_data['comingEps'] = {}
section_data['comingEps']['displayPaused'] = bool(app.COMING_EPS_DISPLAY_PAUSED)
section_data['comingEps']['sort'] = app.COMING_EPS_SORT
section_data['comingEps']['missedRange'] = int(app.COMING_EPS_MISSED_RANGE)
section_data['comingEps']['missedRange'] = int(app.COMING_EPS_MISSED_RANGE or 0)
section_data['comingEps']['layout'] = app.COMING_EPS_LAYOUT

section_data['backlogOverview'] = {}
section_data['backlogOverview']['status'] = app.BACKLOG_STATUS
section_data['backlogOverview']['period'] = app.BACKLOG_PERIOD

section_data['selectedRootIndex'] = int_default(app.SELECTED_ROOT, -1) # All paths

return section_data
1 change: 1 addition & 0 deletions medusa/server/api/v2/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def filter_series(current):
s.to_json(detailed=detailed, episodes=episodes)
for s in Series.find_series(predicate=filter_series)
]

return self._paginate(data, sort='title')

identifier = SeriesIdentifier.from_slug(series_slug)
Expand Down
42 changes: 25 additions & 17 deletions medusa/server/api/v2/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
UNAIRED,
WANTED
)
from medusa.network_timezones import parse_date_time
from medusa.server.api.v2.base import BaseRequestHandler
from medusa.show.show import Show

Expand Down Expand Up @@ -66,46 +67,49 @@ def query_in(items):
return '({0})'.format(','.join(map(str, items)))

query = dedent("""\
SELECT indexer AS indexerId, showid AS seriesId,
SELECT tv_eps.indexer AS indexerId, tv_eps.showid AS seriesId,
SUM(
season > 0 AND
episode > 0 AND
airdate > 1 AND
status IN {status_quality}
tv_eps.status IN {status_quality}
) AS epSnatched,
SUM(
season > 0 AND
episode > 0 AND
airdate > 1 AND
status IN {status_download}
tv_eps.status IN {status_download}
) AS epDownloaded,
SUM(
season > 0 AND
episode > 0 AND
airdate > 1 AND (
(airdate <= {today} AND status IN {status_pre_today}) OR
status IN {status_both}
(airdate <= {today} AND tv_eps.status IN {status_pre_today}) OR
tv_eps.status IN {status_both}
)
) AS epTotal,
(SELECT airdate FROM tv_episodes
WHERE showid=tv_eps.showid AND
indexer=tv_eps.indexer AND
WHERE tv_episodes.showid=tv_eps.showid AND
tv_episodes.indexer=tv_eps.indexer AND
airdate >= {today} AND
(status = {unaired} OR status = {wanted})
(tv_eps.status = {unaired} OR tv_eps.status = {wanted})
ORDER BY airdate ASC
LIMIT 1
) AS epAirsNext,
(SELECT airdate FROM tv_episodes
WHERE showid=tv_eps.showid AND
indexer=tv_eps.indexer AND
airdate > 1 AND
status <> {unaired}
WHERE tv_episodes.showid=tv_eps.showid AND
tv_episodes.indexer=tv_eps.indexer AND
airdate > {today} AND
tv_eps.status <> {unaired}
ORDER BY airdate DESC
LIMIT 1
) AS epAirsPrev,
SUM(file_size) AS seriesSize
FROM tv_episodes tv_eps
GROUP BY showid, indexer
SUM(file_size) AS seriesSize,
tv_shows.airs as airs,
tv_shows.network as network
FROM tv_episodes tv_eps, tv_shows
WHERE tv_eps.showid = tv_shows.indexer_id AND tv_eps.indexer = tv_shows.indexer
GROUP BY tv_eps.showid, tv_eps.indexer;
""").format(
status_quality=query_in(snatched),
status_download=query_in(downloaded),
Expand All @@ -121,12 +125,16 @@ def query_in(items):
sql_result = main_db_con.select(query)

stats_data = {}
stats_data['seriesStat'] = []
stats_data['stats'] = []
stats_data['maxDownloadCount'] = 1000
for cur_result in sql_result:
stats_data['seriesStat'].append(cur_result)
stats_data['stats'].append(cur_result)
if cur_result['epTotal'] > stats_data['maxDownloadCount']:
stats_data['maxDownloadCount'] = cur_result['epTotal']
if cur_result['epAirsNext']:
cur_result['epAirsNext'] = parse_date_time(cur_result['epAirsNext'], cur_result['airs'], cur_result['network'])
if cur_result['epAirsPrev']:
cur_result['epAirsPrev'] = parse_date_time(cur_result['epAirsPrev'], cur_result['airs'], cur_result['network'])

stats_data['maxDownloadCount'] *= 100
return stats_data
40 changes: 6 additions & 34 deletions medusa/server/web/home/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,41 +121,13 @@ def _genericMessage(self, subject, message):
return t.render(message=message, subject=subject, title='')

def index(self):
t = PageTemplate(rh=self, filename='home.mako')
selected_root = int(app.SELECTED_ROOT)
shows_dir = None
if selected_root is not None and app.ROOT_DIRS:
backend_pieces = app.ROOT_DIRS
backend_dirs = backend_pieces[1:]
try:
shows_dir = backend_dirs[selected_root] if selected_root != -1 else None
except IndexError:
# If user have a root selected in /home and remove the root folder a IndexError is raised
shows_dir = None
app.SELECTED_ROOT = -1

series = []
if app.ANIME_SPLIT_HOME:
anime = []
for show in app.showList:
if shows_dir and not show._location.startswith(shows_dir):
continue
if show.is_anime:
anime.append(show)
else:
series.append(show)

show_lists = [[order, {'Series': series, 'Anime': anime}[order]] for order in app.SHOW_LIST_ORDER]
else:
for show in app.showList:
if shows_dir and not show._location.startswith(shows_dir):
continue
series.append(show)
show_lists = [['Series', series]]
"""
Render the home page.
stats = self.show_statistics()
return t.render(show_lists=show_lists, show_stat=stats[0],
max_download_count=stats[1], controller='home', action='index')
[Converted to VueRouter]
"""
t = PageTemplate(rh=self, filename='index.mako')
return t.render()

@staticmethod
def show_statistics():
Expand Down
63 changes: 57 additions & 6 deletions medusa/tv/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ def __init__(self, indexer, indexerid, lang='', quality=None,
self.default_ep_status = SKIPPED
self._location = ''
self.episodes = {}
self.prev_aired = ''
self.next_aired = ''
self.release_groups = None
self.exceptions = set()
Expand Down Expand Up @@ -555,9 +556,17 @@ def last_update_indexer(self):
epoch_date = update_date - datetime.date.fromtimestamp(0)
return int(epoch_date.total_seconds())

@property
def prev_airdate(self):
"""Return last aired episode airdate."""
return (
sbdatetime.convert_to_setting(network_timezones.parse_date_time(self.prev_aired, self.airs, self.network))
if try_int(self.prev_aired, 1) > MILLIS_YEAR_1900 else None
)

@property
def next_airdate(self):
"""Return next airdate."""
"""Return next aired episode airdate."""
return (
sbdatetime.convert_to_setting(network_timezones.parse_date_time(self.next_aired, self.airs, self.network))
if try_int(self.next_aired, 1) > MILLIS_YEAR_1900 else None
Expand Down Expand Up @@ -623,7 +632,7 @@ def aliases_to_json(self):
@property
def xem_numbering(self):
"""Return series episode xem numbering."""
return get_xem_numbering_for_show(self)
return get_xem_numbering_for_show(self, refresh_data=False)

@property
def xem_absolute_numbering(self):
Expand Down Expand Up @@ -1612,6 +1621,46 @@ def load_imdb_info(self):
log.debug(u'{id}: Obtained info from IMDb: {imdb_info}',
{'id': self.series_id, 'imdb_info': self.imdb_info})

def prev_episode(self):
"""Return the last aired episode air date.
:return:
:rtype: datetime.date
"""
log.debug(u'{id}: Finding the episode which aired last', {'id': self.series_id})

main_db_con = db.DBConnection()
sql_results = main_db_con.select(
'SELECT '
' airdate,'
' season,'
' episode '
'FROM '
' tv_episodes '
'WHERE '
' indexer = ?'
' AND showid = ? '
' AND airdate < ? '
' AND status <> ? '
'ORDER BY'
' airdate '
'DESC LIMIT 1',
[self.indexer, self.series_id, datetime.date.today().toordinal(), UNAIRED])

if sql_results is None or len(sql_results) == 0:
log.debug(u'{id}: Could not find a previous aired episode', {'id': self.series_id})
self.prev_aired = u''
else:
log.debug(
u'{id}: Found previous aired episode number {ep}', {
'id': self.series_id,
'ep': episode_num(sql_results[0]['season'], sql_results[0]['episode'])
}
)
self.prev_aired = sql_results[0]['airdate']

return self.prev_aired

def next_episode(self):
"""Return the next episode air date.
Expand All @@ -1635,14 +1684,13 @@ def next_episode(self):
' indexer = ?'
' AND showid = ? '
' AND airdate >= ? '
' AND status IN (?,?) '
'ORDER BY'
' airdate '
'ASC LIMIT 1',
[self.indexer, self.series_id, datetime.date.today().toordinal(), UNAIRED, WANTED])
[self.indexer, self.series_id, datetime.date.today().toordinal()])

if sql_results is None or len(sql_results) == 0:
log.debug(u'{id}: No episode found... need to implement a show status',
log.debug(u'{id}: Could not find a next episode',
{'id': self.series_id})
self.next_aired = u''
else:
Expand Down Expand Up @@ -2056,6 +2104,7 @@ def to_json(self, detailed=False, episodes=False):
data['imdbInfo'] = {to_camel_case(k): v for k, v in viewitems(self.imdb_info)}
data['year'] = {}
data['year']['start'] = self.imdb_year or self.start_year
data['prevAirDate'] = self.prev_airdate.isoformat() if self.prev_airdate else None
data['nextAirDate'] = self.next_airdate.isoformat() if self.next_airdate else None
data['lastUpdate'] = datetime.date.fromordinal(self._last_update_indexer).isoformat()
data['runtime'] = self.imdb_runtime or self.runtime
Expand Down Expand Up @@ -2097,6 +2146,9 @@ def to_json(self, detailed=False, episodes=False):
data['config']['release']['requiredWordsExclude'] = bool(self.rls_require_exclude)
data['config']['airdateOffset'] = self.airdate_offset

# Moved from detailed, as the home page, needs it to display the Xem icon.
data['xemNumbering'] = numbering_tuple_to_dict(self.xem_numbering)

# These are for now considered anime-only options
if self.is_anime:
bw_list = self.release_groups or BlackAndWhiteList(self)
Expand All @@ -2112,7 +2164,6 @@ def to_json(self, detailed=False, episodes=False):
if detailed:
data['size'] = self.size
data['showQueueStatus'] = self.show_queue_status
data['xemNumbering'] = numbering_tuple_to_dict(self.xem_numbering)
data['sceneAbsoluteNumbering'] = dict_to_array(self.scene_absolute_numbering, key='absolute', value='sceneAbsolute')
if self.is_scene:
data['xemAbsoluteNumbering'] = dict_to_array(self.xem_absolute_numbering, key='absolute', value='sceneAbsolute')
Expand Down
14 changes: 14 additions & 0 deletions tests/apiv2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
# coding=utf-8
"""Api v2 tests."""
from __future__ import unicode_literals

import os
import sys

import six

# Start event loop in python3
if six.PY3:
import asyncio

# We need to set the WindowsSelectorEventLoop event loop on python 3 (3.8 and higher) running on windows
if sys.platform == 'win32':
try:
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
except AttributeError: # Only available since Python 3.7.0
pass

sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../lib')))
sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../ext')))
sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
Loading

0 comments on commit 0b346d6

Please sign in to comment.