Browse Source

Merge branch 'refs/heads/mikke89-charts-v2' into develop

pull/3080/head
Ruud 11 years ago
parent
commit
5f93b08c23
  1. 34
      couchpotato/core/media/movie/charts/__init__.py
  2. 60
      couchpotato/core/media/movie/charts/main.py
  3. 240
      couchpotato/core/media/movie/charts/static/charts.css
  4. 166
      couchpotato/core/media/movie/charts/static/charts.js
  5. 13
      couchpotato/core/media/movie/providers/automation/base.py
  6. 42
      couchpotato/core/media/movie/providers/automation/bluray.py
  7. 85
      couchpotato/core/media/movie/providers/automation/imdb.py
  8. 2
      couchpotato/core/media/movie/suggestion/static/suggest.css
  9. 9
      couchpotato/core/media/movie/suggestion/static/suggest.js
  10. 73
      couchpotato/static/scripts/page/home.js

34
couchpotato/core/media/movie/charts/__init__.py

@ -0,0 +1,34 @@
from .main import Charts
def autoload():
return Charts()
config = [{
'name': 'charts',
'groups': [
{
'label': 'Charts',
'description': 'Displays selected charts on the home page',
'type': 'list',
'name': 'charts_providers',
'tab': 'display',
'options': [
{
'name': 'max_items',
'default': 5,
'type': 'int',
'description': 'Maximum number of items displayed from each chart.',
},
{
'name': 'update_interval',
'default': 12,
'type': 'int',
'advanced': True,
'description': '(hours)',
},
],
},
],
}]

60
couchpotato/core/media/movie/charts/main.py

@ -0,0 +1,60 @@
import time
from couchpotato import tryInt
from couchpotato.core.logger import CPLog
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent,fireEvent
from couchpotato.core.plugins.base import Plugin
log = CPLog(__name__)
class Charts(Plugin):
update_in_progress = False
def __init__(self):
addApiView('charts.view', self.automationView)
addEvent('app.load', self.setCrons)
def setCrons(self):
fireEvent('schedule.interval', 'charts.update_cache', self.updateViewCache, hours = self.conf('update_interval', default = 12))
self.updateViewCache()
def automationView(self, force_update = False, **kwargs):
if force_update:
charts = self.updateViewCache()
else:
charts = self.getCache('charts_cached')
if not charts:
charts = self.updateViewCache()
return {
'success': True,
'count': len(charts),
'charts': charts
}
def updateViewCache(self):
if self.update_in_progress:
while self.update_in_progress:
time.sleep(1)
catched_charts = self.getCache('charts_cached')
if catched_charts:
return catched_charts
try:
self.update_in_progress = True
charts = fireEvent('automation.get_chart_list', merge = True)
self.setCache('charts_cached', charts, timeout = 7200 * tryInt(self.conf('update_interval', default = 12)))
except:
log.error('Failed refreshing charts')
self.update_in_progress = False
return charts

240
couchpotato/core/media/movie/charts/static/charts.css

@ -0,0 +1,240 @@
.charts {
clear: both;
}
.charts > h2 {
height: 40px;
}
.charts .chart {
display: inline-block;
width: 50%;
vertical-align: top;
}
.charts div.refresh {
margin-top: 10px;
clear:both;
text-align:center;
}
.charts p.no_charts_enabled {
padding: 0.7em 1em;
}
.charts div.refresh a {
display:block;
}
.charts .chart h3 a {
color: #fff;
}
.charts .chart .media_result {
display: inline-block;
width: 100%;
height: 150px;
}
@media all and (max-width: 960px) {
.charts .chart {
width: 50%;
}
}
@media all and (max-width: 600px) {
.charts .chart {
width: 100%;
}
}
.charts .chart .media_result .data {
left: 150px;
background: #4e5969;
border: none;
}
.charts .chart .media_result .data .info {
top: 10px;
left: 15px;
right: 15px;
bottom: 10px;
overflow: hidden;
}
.charts .chart .media_result .data .info h2 {
white-space: normal;
max-height: 120px;
font-size: 18px;
line-height: 18px;
}
.charts .chart .media_result .data .info .rating,
.charts .chart .media_result .data .info .genres,
.charts .chart .media_result .data .info .year {
position: static;
display: block;
padding: 0;
opacity: .6;
}
.charts .chart .media_result .data .info .year {
margin: 10px 0 0;
}
.charts .chart .media_result .data .info .rating {
font-size: 20px;
float: right;
margin-top: -20px;
}
.charts .chart .media_result .data .info .rating:before {
content: "\e031";
font-family: 'Elusive-Icons';
font-size: 14px;
margin: 0 5px 0 0;
vertical-align: bottom;
}
.charts .chart .media_result .data .info .genres {
font-size: 11px;
font-style: italic;
text-align: right;
}
.charts .chart .media_result .data .info .plot {
display: block;
font-size: 11px;
overflow: hidden;
text-align: justify;
height: 100%;
z-index: 2;
top: 64px;
position: absolute;
background: #4e5969;
cursor: pointer;
transition: all .4s ease-in-out;
padding: 0 3px 10px 0;
}
.charts .chart .media_result .data:before {
bottom: 0;
content: '';
display: block;
height: 10px;
right: 0;
left: 0;
bottom: 10px;
position: absolute;
background: linear-gradient(
0deg,
rgba(78, 89, 105, 1) 0%,
rgba(78, 89, 105, 0) 100%
);
z-index: 3;
pointer-events: none;
}
.charts .chart .media_result .data .info .plot.full {
top: 0;
overflow: auto;
}
.charts .chart .media_result .data {
cursor: default;
}
.charts .chart .media_result .options {
left: 150px;
}
.charts .chart .media_result .options select[name=title] { width: 100%; }
.charts .chart .media_result .options select[name=profile] { width: 100%; }
.charts .chart .media_result .options select[name=category] { width: 100%; }
.charts .chart .media_result .button {
position: absolute;
margin: 2px 0 0 0;
right: 15px;
bottom: 15px;
}
.charts .chart .media_result .thumbnail {
width: 100px;
position: absolute;
left: 50px;
}
.charts .chart .media_result div.chart_number {
color: white;
position: absolute;
top: 0;
padding: 10px;
font: bold 2em/1em Helvetica, Sans-Serif;
width: 50px;
height: 100%;
}
.charts .chart .media_result div.chart_number.chart_in_wanted {
background: rgb(0, 255, 40); /* fallback color */
background: rgba(0, 255, 40, 0.3);
}
.charts .chart .media_result div.chart_number.chart_in_library {
background: rgb(0, 202, 32); /* fallback color */
background: rgba(0, 202, 32, 0.3);
}
.charts .chart .media_result .actions {
position: absolute;
top: 10px;
right: 10px;
display: none;
width: 90px;
}
.charts .chart .media_result:hover .actions {
display: block;
}
.charts .chart .media_result:hover h2 .title {
opacity: 0;
}
.charts .chart .media_result .data.open .actions {
display: none;
}
.charts .chart .media_result .actions a {
margin-left: 10px;
vertical-align: middle;
}
.toggle_menu {
height: 50px;
}
.toggle_menu a {
display: block;
width: 50%;
float: left;
color: rgba(255,255,255,.6);
border-bottom: 1px solid rgba(255, 255, 255, 0.0666667);
}
.toggle_menu a:hover {
border-color: #047792;
border-width: 4px;
color: #fff;
}
.toggle_menu a.active {
border-bottom: 4px solid #04bce6;
color: #fff;
}
.toggle_menu a:last-child {
float: right;
}
.toggle_menu h2 {
height: 40px;
}

166
couchpotato/core/media/movie/charts/static/charts.js

@ -0,0 +1,166 @@
var Charts = new Class({
Implements: [Options, Events],
initialize: function(options){
var self = this;
self.setOptions(options);
self.create();
},
create: function(){
var self = this;
self.el_refreshing_text = new Element('span.refreshing', {
'text': 'Refreshing charts...'
});
self.el_refresh_link = new Element('a.refresh', {
'href': '#',
'text': 'Refresh charts',
'events': {
'click': function(e) {
e.preventDefault();
self.el.getChildren('div.chart').destroy();
self.el_refreshing_text.show();
self.el_refresh_link.hide();
self.api_request = Api.request('charts.view', {
'data': { 'force_update': 1 },
'onComplete': self.fill.bind(self)
});
}
}
}).hide();
self.el_refresh_container = new Element('div.refresh').grab(
self.el_refreshing_text
).grab(self.el_refresh_link);
self.el_no_charts_enabled = new Element('p.no_charts_enabled', {
'html': 'Hey, it looks like you have no charts enabled at the moment. If you\'d like some great movie suggestions you can go to <a href="' + App.createUrl('settings/display') + '">settings</a> and turn on some charts of your choice.'
}).hide();
self.el = new Element('div.charts').grab(
self.el_no_charts_enabled
).grab(self.el_refresh_container);
if( Cookie.read('suggestions_charts_menu_selected') === 'charts')
self.el.show();
else
self.el.hide();
self.api_request = Api.request('charts.view', {
'onComplete': self.fill.bind(self)
});
},
fill: function(json){
var self = this;
self.el_refreshing_text.hide();
self.el_refresh_link.show();
if(!json || json.count == 0){
self.el_no_charts_enabled.show();
self.el_refresh_link.show();
self.el_refreshing_text.hide();
}
else {
self.el_no_charts_enabled.hide();
json.charts.sort(function(a, b) {
return a.order - b.order;
});
Object.each(json.charts, function(chart){
var c = new Element('div.chart').grab(
new Element('h3').grab( new Element('a', {
'text': chart.name,
'href': chart.url
}))
);
var it = 1;
Object.each(chart.list, function(movie){
var m = new Block.Search.MovieItem(movie, {
'onAdded': function(){
self.afterAdded(m, movie)
}
});
var in_database_class = movie.in_wanted ? '.chart_in_wanted' : (movie.in_library ? '.chart_in_library' : '');
var in_database_title = movie.in_wanted ? 'Movie in wanted list' : (movie.in_library ? 'Movie in library' : '');
m.el.grab( new Element('div.chart_number' + in_database_class, { 'text': it++, 'title': in_database_title }));
m.data_container.grab(
new Element('div.actions').adopt(
new Element('a.add.icon2', {
'title': 'Add movie with your default quality',
'data-add': movie.imdb,
'events': {
'click': m.showOptions.bind(m)
}
}),
$(new MA.IMDB(m)),
$(new MA.Trailer(m, {
'height': 150
}))
)
);
m.data_container.removeEvents('click');
var plot = false;
if(m.info.plot && m.info.plot.length > 0)
plot = m.info.plot;
// Add rating
m.info_container.adopt(
m.rating = m.info.rating && m.info.rating.imdb && m.info.rating.imdb.length == 2 && parseFloat(m.info.rating.imdb[0]) > 0 ? new Element('span.rating', {
'text': parseFloat(m.info.rating.imdb[0]),
'title': parseInt(m.info.rating.imdb[1]) + ' votes'
}) : null,
m.genre = m.info.genres && m.info.genres.length > 0 ? new Element('span.genres', {
'text': m.info.genres.slice(0, 3).join(', ')
}) : null,
m.plot = plot ? new Element('span.plot', {
'text': plot,
'events': {
'click': function(){
this.toggleClass('full')
}
}
}) : null
)
$(m).inject(c);
});
$(c).inject(self.el_refresh_container, 'before');
});
}
self.fireEvent('loaded');
},
afterAdded: function(m, movie){
var self = this;
$(m).getElement('div.chart_number')
.addClass('chart_in_wanted')
.set('title', 'Movie in wanted list');
},
toElement: function(){
return this.el;
}
})

13
couchpotato/core/media/movie/providers/automation/base.py

@ -13,6 +13,7 @@ log = CPLog(__name__)
class Automation(AutomationBase):
enabled_option = 'automation_enabled'
chart_enabled_option = 'chart_display_enabled'
http_time_between_calls = 2
interval = 1800
@ -20,6 +21,7 @@ class Automation(AutomationBase):
def __init__(self):
addEvent('automation.get_movies', self._getMovies)
addEvent('automation.get_chart_list', self._getChartList)
def _getMovies(self):
@ -34,6 +36,13 @@ class Automation(AutomationBase):
return self.getIMDBids()
def _getChartList(self):
if not (self.conf(self.chart_enabled_option) or self.conf(self.chart_enabled_option) is None):
return
return self.getChartList()
def search(self, name, year = None, imdb_only = False):
prop_name = 'automation.cached.%s.%s' % (name, year)
@ -94,5 +103,9 @@ class Automation(AutomationBase):
def getIMDBids(self):
return []
def getChartList(self):
# Example return: [ {'name': 'Display name of list', 'url': 'http://example.com/', 'order': 1, 'list': []} ]
return
def canCheck(self):
return time.time() > self.last_checked + self.interval

42
couchpotato/core/media/movie/providers/automation/bluray.py

@ -14,6 +14,8 @@ class Bluray(Automation, RSS):
interval = 1800
rss_url = 'http://www.blu-ray.com/rss/newreleasesfeed.xml'
backlog_url = 'http://www.blu-ray.com/movies/movies.php?show=newreleases&page=%s'
display_url = 'http://www.blu-ray.com/movies/movies.php?show=newreleases'
chart_order = 1
def getIMDBids(self):
@ -77,6 +79,32 @@ class Bluray(Automation, RSS):
return movies
def getChartList(self):
# Nearly identical to 'getIMDBids', but we don't care about minimalMovie and return all movie data (not just id)
movie_list = {'name': 'Blu-ray.com - New Releases', 'url': self.display_url, 'order': self.chart_order, 'list': []}
max_items = int(self.conf('max_items', section='charts', default=5))
rss_movies = self.getRSSData(self.rss_url)
for movie in rss_movies:
name = self.getTextElement(movie, 'title').lower().split('blu-ray')[0].strip('(').rstrip()
year = self.getTextElement(movie, 'description').split('|')[1].strip('(').strip()
if not name.find('/') == -1: # make sure it is not a double movie release
continue
movie = self.search(name, year)
if movie:
movie_list['list'].append( movie )
if len(movie_list['list']) >= max_items:
break
if not movie_list['list']:
return
return [ movie_list ]
config = [{
'name': 'bluray',
'groups': [
@ -101,5 +129,19 @@ config = [{
},
],
},
{
'tab': 'display',
'list': 'charts_providers',
'name': 'bluray_charts_display',
'label': 'Blu-ray.com',
'description': 'Display <a href="http://www.blu-ray.com/movies/movies.php?show=newreleases">new releases</a> from Blu-ray.com',
'options': [
{
'name': 'chart_display_enabled',
'default': True,
'type': 'enabler',
},
],
},
],
}]

85
couchpotato/core/media/movie/providers/automation/imdb.py

@ -105,6 +105,16 @@ class IMDBAutomation(IMDBBase):
'top250': 'http://www.imdb.com/chart/top',
'boxoffice': 'http://www.imdb.com/chart/',
}
chart_names = {
'theater': 'IMDB - Movies in Theaters',
'top250': 'IMDB - Top 250 Movies',
'boxoffice': 'IMDB - Box Office',
}
chart_order = {
'theater': 2,
'top250': 4,
'boxoffice': 3,
}
first_table = ['boxoffice']
@ -144,6 +154,46 @@ class IMDBAutomation(IMDBBase):
return movies
def getChartList(self):
# Nearly identical to 'getIMDBids', but we don't care about minimalMovie and return all movie data (not just id)
movie_lists = []
max_items = int(self.conf('max_items', section='charts', default=5))
for url in self.chart_urls:
if self.conf('chart_display_%s' % url):
movie_list = {'name': self.chart_names[url], 'url': self.chart_urls[url], 'order': self.chart_order[url], 'list': []}
data = self.getHTMLData(self.chart_urls[url])
if data:
html = BeautifulSoup(data)
try:
result_div = html.find('div', attrs = {'id': 'main'})
try:
if url in self.first_table:
table = result_div.find('table')
result_div = table if table else result_div
except:
pass
imdb_ids = getImdb(str(result_div), multiple = True)
for imdb_id in imdb_ids[0:max_items]:
info = self.getInfo(imdb_id)
movie_list['list'].append(info)
if self.shuttingDown():
break
except:
log.error('Failed loading IMDB chart results from %s: %s', (url, traceback.format_exc()))
if movie_list['list']:
movie_lists.append(movie_list)
return movie_lists
config = [{
'name': 'imdb',
'groups': [
@ -206,5 +256,40 @@ config = [{
},
],
},
{
'tab': 'display',
'list': 'charts_providers',
'name': 'imdb_charts_display',
'label': 'IMDB',
'description': 'Display movies from IMDB Charts',
'options': [
{
'name': 'chart_display_enabled',
'default': True,
'type': 'enabler',
},
{
'name': 'chart_display_theater',
'type': 'bool',
'label': 'In Theaters',
'description': 'New Movies <a href="http://www.imdb.com/movies-in-theaters/">In-Theaters</a> chart',
'default': False,
},
{
'name': 'chart_display_top250',
'type': 'bool',
'label': 'TOP 250',
'description': 'IMDB <a href="http://www.imdb.com/chart/top/">TOP 250</a> chart',
'default': False,
},
{
'name': 'chart_display_boxoffice',
'type': 'bool',
'label': 'Box office TOP 10',
'description': 'IMDB Box office <a href="http://www.imdb.com/chart/">TOP 10</a> chart',
'default': True,
},
],
},
],
}]

2
couchpotato/core/media/movie/suggestion/static/suggest.css

@ -1,4 +1,6 @@
.suggestions {
clear: both;
padding-top: 10px;
}
.suggestions > h2 {

9
couchpotato/core/media/movie/suggestion/static/suggest.js

@ -42,11 +42,10 @@ var SuggestList = new Class({
}
}
}).grab(
new Element('h2', {
'text': 'You might like these'
})
);
});
var cookie_menu_select = Cookie.read('suggestions_charts_menu_selected');
if( cookie_menu_select === 'suggestions' || cookie_menu_select === null ) self.el.show(); else self.el.hide();
self.api_request = Api.request('suggestion.view', {
'onComplete': self.fill.bind(self)

73
couchpotato/static/scripts/page/home.js

@ -21,7 +21,9 @@ Page.Home = new Class({
self.chain.chain(
self.createAvailable.bind(self),
self.createSoon.bind(self),
self.createSuggestionsChartsMenu.bind(self),
self.createSuggestions.bind(self),
self.createCharts.bind(self),
self.createLate.bind(self)
);
@ -151,7 +153,78 @@ Page.Home = new Class({
$(self.suggestion_list).inject(self.el);
},
createCharts: function(){
var self = this;
// Charts
self.charts = new Charts({
'onLoaded': function(){
self.chain.callChain();
}
});
$(self.charts).inject(self.el);
},
createSuggestionsChartsMenu: function(){
var self = this;
self.el_toggle_menu_suggestions = new Element('a.toggle_suggestions.active', {
'href': '#',
'events': { 'click': function(e) {
e.preventDefault();
self.toggleSuggestionsCharts('suggestions');
}
}
}).grab( new Element('h2', {'text': 'Suggestions'}));
self.el_toggle_menu_charts = new Element('a.toggle_charts', {
'href': '#',
'events': { 'click': function(e) {
e.preventDefault();
self.toggleSuggestionsCharts('charts');
}
}
}).grab( new Element('h2', {'text': 'Charts'}));
self.el_toggle_menu = new Element('div.toggle_menu').grab(
self.el_toggle_menu_suggestions
).grab(
self.el_toggle_menu_charts
);
var menu_selected = Cookie.read('suggestions_charts_menu_selected');
if( menu_selected === null ) menu_selected = 'suggestions';
self.toggleSuggestionsCharts( menu_selected );
self.el_toggle_menu.inject(self.el);
self.chain.callChain();
},
toggleSuggestionsCharts: function(menu_id){
var self = this;
switch(menu_id) {
case 'suggestions':
if($(self.suggestion_list)) $(self.suggestion_list).show();
self.el_toggle_menu_suggestions.addClass('active');
if($(self.charts)) $(self.charts).hide();
self.el_toggle_menu_charts.removeClass('active');
break;
case 'charts':
if($(self.charts)) $(self.charts).show();
self.el_toggle_menu_charts.addClass('active');
if($(self.suggestion_list)) $(self.suggestion_list).hide();
self.el_toggle_menu_suggestions.removeClass('active');
break;
}
Cookie.write('suggestions_charts_menu_selected', menu_id, {'duration': 365});
},
createLate: function(){

Loading…
Cancel
Save