268 changed files with 43240 additions and 21118 deletions
@ -0,0 +1,16 @@ |
|||||
|
# http://editorconfig.org |
||||
|
root = true |
||||
|
|
||||
|
[*] |
||||
|
indent_style = tab |
||||
|
indent_size = 4 |
||||
|
end_of_line = lf |
||||
|
charset = utf-8 |
||||
|
trim_trailing_whitespace = true |
||||
|
insert_final_newline = true |
||||
|
|
||||
|
[*.py] |
||||
|
indent_style = space |
||||
|
|
||||
|
[*.md] |
||||
|
trim_trailing_whitespace = false |
@ -1,5 +1,8 @@ |
|||||
*.pyc |
*.pyc |
||||
/data/ |
/data/ |
||||
|
/_env/ |
||||
/_source/ |
/_source/ |
||||
.project |
.project |
||||
.pydevproject |
.pydevproject |
||||
|
node_modules |
||||
|
.tmp |
||||
|
@ -0,0 +1,198 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
module.exports = function(grunt){ |
||||
|
require('jit-grunt')(grunt); |
||||
|
require('time-grunt')(grunt); |
||||
|
|
||||
|
grunt.loadNpmTasks('grunt-shell-spawn'); |
||||
|
|
||||
|
// Configurable paths
|
||||
|
var config = { |
||||
|
python: grunt.file.exists('./_env/bin/python') ? './_env/bin/python' : 'python', |
||||
|
tmp: '.tmp', |
||||
|
base: 'couchpotato', |
||||
|
css_dest: 'couchpotato/static/style/combined.min.css', |
||||
|
scripts_vendor_dest: 'couchpotato/static/scripts/combined.vendor.min.js', |
||||
|
scripts_base_dest: 'couchpotato/static/scripts/combined.base.min.js', |
||||
|
scripts_plugins_dest: 'couchpotato/static/scripts/combined.plugins.min.js' |
||||
|
}; |
||||
|
|
||||
|
var vendor_scripts_files = [ |
||||
|
'couchpotato/static/scripts/vendor/mootools.js', |
||||
|
'couchpotato/static/scripts/vendor/mootools_more.js', |
||||
|
'couchpotato/static/scripts/vendor/Array.stableSort.js', |
||||
|
'couchpotato/static/scripts/vendor/history.js', |
||||
|
'couchpotato/static/scripts/vendor/dynamics.js', |
||||
|
'couchpotato/static/scripts/vendor/fastclick.js' |
||||
|
]; |
||||
|
|
||||
|
var scripts_files = [ |
||||
|
'couchpotato/static/scripts/library/uniform.js', |
||||
|
'couchpotato/static/scripts/library/question.js', |
||||
|
'couchpotato/static/scripts/library/scrollspy.js', |
||||
|
'couchpotato/static/scripts/couchpotato.js', |
||||
|
'couchpotato/static/scripts/api.js', |
||||
|
'couchpotato/static/scripts/page.js', |
||||
|
'couchpotato/static/scripts/block.js', |
||||
|
'couchpotato/static/scripts/block/navigation.js', |
||||
|
'couchpotato/static/scripts/block/header.js', |
||||
|
'couchpotato/static/scripts/block/footer.js', |
||||
|
'couchpotato/static/scripts/block/menu.js', |
||||
|
'couchpotato/static/scripts/page/home.js', |
||||
|
'couchpotato/static/scripts/page/settings.js', |
||||
|
'couchpotato/static/scripts/page/about.js', |
||||
|
'couchpotato/static/scripts/page/login.js' |
||||
|
]; |
||||
|
|
||||
|
grunt.initConfig({ |
||||
|
|
||||
|
// Project settings
|
||||
|
config: config, |
||||
|
|
||||
|
// Make sure code styles are up to par and there are no obvious mistakes
|
||||
|
jshint: { |
||||
|
options: { |
||||
|
reporter: require('jshint-stylish'), |
||||
|
unused: false, |
||||
|
camelcase: false, |
||||
|
devel: true |
||||
|
}, |
||||
|
all: [ |
||||
|
'<%= config.base %>/{,**/}*.js', |
||||
|
'!<%= config.base %>/static/scripts/vendor/{,**/}*.js', |
||||
|
'!<%= config.base %>/static/scripts/combined.*.js' |
||||
|
] |
||||
|
}, |
||||
|
|
||||
|
// Compiles Sass to CSS and generates necessary files if requested
|
||||
|
sass: { |
||||
|
options: { |
||||
|
compass: true, |
||||
|
update: true, |
||||
|
sourcemap: 'none' |
||||
|
}, |
||||
|
server: { |
||||
|
files: [{ |
||||
|
expand: true, |
||||
|
cwd: '<%= config.base %>/', |
||||
|
src: ['**/*.scss'], |
||||
|
dest: '<%= config.tmp %>/styles/', |
||||
|
ext: '.css' |
||||
|
}] |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// Empties folders to start fresh
|
||||
|
clean: { |
||||
|
server: '.tmp' |
||||
|
}, |
||||
|
|
||||
|
// Add vendor prefixed styles
|
||||
|
autoprefixer: { |
||||
|
options: { |
||||
|
browsers: ['last 2 versions'], |
||||
|
remove: false, |
||||
|
cascade: false |
||||
|
}, |
||||
|
dist: { |
||||
|
files: [{ |
||||
|
expand: true, |
||||
|
cwd: '<%= config.tmp %>/styles/', |
||||
|
src: '{,**/}*.css', |
||||
|
dest: '<%= config.tmp %>/styles/' |
||||
|
}] |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
cssmin: { |
||||
|
dist: { |
||||
|
options: { |
||||
|
keepBreaks: true |
||||
|
}, |
||||
|
files: { |
||||
|
'<%= config.css_dest %>': ['<%= config.tmp %>/styles/**/*.css'] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
uglify: { |
||||
|
options: { |
||||
|
mangle: false, |
||||
|
compress: false, |
||||
|
beautify: true, |
||||
|
screwIE8: true |
||||
|
}, |
||||
|
vendor: { |
||||
|
files: { |
||||
|
'<%= config.scripts_vendor_dest %>': vendor_scripts_files |
||||
|
} |
||||
|
}, |
||||
|
base: { |
||||
|
files: { |
||||
|
'<%= config.scripts_base_dest %>': scripts_files |
||||
|
} |
||||
|
}, |
||||
|
plugins: { |
||||
|
files: { |
||||
|
'<%= config.scripts_plugins_dest %>': ['<%= config.base %>/core/**/*.js'] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
shell: { |
||||
|
runCouchPotato: { |
||||
|
command: '<%= config.python %> CouchPotato.py', |
||||
|
options: { |
||||
|
stdout: true, |
||||
|
stderr: true |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// COOL TASKS ==============================================================
|
||||
|
watch: { |
||||
|
scss: { |
||||
|
files: ['<%= config.base %>/**/*.{scss,sass}'], |
||||
|
tasks: ['sass:server', 'autoprefixer', 'cssmin'] |
||||
|
}, |
||||
|
js: { |
||||
|
files: [ |
||||
|
'<%= config.base %>/**/*.js', |
||||
|
'!<%= config.base %>/static/scripts/combined.*.js' |
||||
|
], |
||||
|
tasks: ['uglify:base', 'uglify:plugins', 'jshint'] |
||||
|
}, |
||||
|
livereload: { |
||||
|
options: { |
||||
|
livereload: 35729 |
||||
|
}, |
||||
|
files: [ |
||||
|
'<%= config.css_dest %>', |
||||
|
'<%= config.scripts_vendor_dest %>', |
||||
|
'<%= config.scripts_base_dest %>', |
||||
|
'<%= config.scripts_plugins_dest %>' |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
concurrent: { |
||||
|
options: { |
||||
|
logConcurrentOutput: true |
||||
|
}, |
||||
|
tasks: ['shell:runCouchPotato', 'watch'] |
||||
|
} |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
grunt.registerTask('default', [ |
||||
|
'clean:server', |
||||
|
'sass:server', |
||||
|
'autoprefixer', |
||||
|
'cssmin', |
||||
|
'uglify:vendor', |
||||
|
'uglify:base', |
||||
|
'uglify:plugins', |
||||
|
'concurrent' |
||||
|
]); |
||||
|
|
||||
|
}; |
@ -0,0 +1,44 @@ |
|||||
|
# First, require any additional compass plugins installed on your system. |
||||
|
# require 'zen-grids' |
||||
|
# require 'susy' |
||||
|
# require 'breakpoint' |
||||
|
|
||||
|
|
||||
|
# Toggle this between :development and :production when deploying the CSS to the |
||||
|
# live server. Development mode will retain comments and spacing from the |
||||
|
# original Sass source and adds line numbering comments for easier debugging. |
||||
|
environment = :development |
||||
|
# environment = :development |
||||
|
|
||||
|
# In development, we can turn on the FireSass-compatible debug_info. |
||||
|
firesass = false |
||||
|
# firesass = true |
||||
|
|
||||
|
|
||||
|
# Location of the your project's resources. |
||||
|
|
||||
|
|
||||
|
# Set this to the root of your project. All resource locations above are |
||||
|
# considered to be relative to this path. |
||||
|
http_path = "/" |
||||
|
|
||||
|
# To use relative paths to assets in your compiled CSS files, set this to true. |
||||
|
# relative_assets = true |
||||
|
|
||||
|
|
||||
|
## |
||||
|
## You probably don't need to edit anything below this. |
||||
|
## |
||||
|
sass_dir = "./couchpotato/static/style" |
||||
|
css_dir = "./couchpotato/static/style" |
||||
|
|
||||
|
# You can select your preferred output style here (can be overridden via the command line): |
||||
|
# output_style = :expanded or :nested or :compact or :compressed |
||||
|
output_style = (environment == :development) ? :expanded : :compressed |
||||
|
|
||||
|
# To disable debugging comments that display the original location of your selectors. Uncomment: |
||||
|
# line_comments = false |
||||
|
|
||||
|
# Pass options to sass. For development, we turn on the FireSass-compatible |
||||
|
# debug_info if the firesass config variable above is true. |
||||
|
sass_options = (environment == :development && firesass == true) ? {:debug_info => true} : {} |
@ -0,0 +1,133 @@ |
|||||
|
import traceback |
||||
|
|
||||
|
from bs4 import BeautifulSoup |
||||
|
from couchpotato.core.helpers.variable import tryInt |
||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media._base.providers.torrent.base import TorrentProvider |
||||
|
import six |
||||
|
|
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
|
||||
|
class Base(TorrentProvider): |
||||
|
|
||||
|
urls = { |
||||
|
'test': 'https://alpharatio.cc/', |
||||
|
'login': 'https://alpharatio.cc/login.php', |
||||
|
'login_check': 'https://alpharatio.cc/inbox.php', |
||||
|
'detail': 'https://alpharatio.cc/torrents.php?torrentid=%s', |
||||
|
'search': 'https://alpharatio.cc/torrents.php?action=advanced&searchstr=%s&scene=%s&filter_cat[%d]=1', |
||||
|
'download': 'https://alpharatio.cc/%s', |
||||
|
} |
||||
|
|
||||
|
http_time_between_calls = 1 # Seconds |
||||
|
|
||||
|
def _search(self, media, quality, results): |
||||
|
|
||||
|
url = self.urls['search'] % self.buildUrl(media, quality) |
||||
|
data = self.getHTMLData(url) |
||||
|
|
||||
|
if data: |
||||
|
html = BeautifulSoup(data) |
||||
|
|
||||
|
try: |
||||
|
result_table = html.find('table', attrs = {'id': 'torrent_table'}) |
||||
|
if not result_table: |
||||
|
return |
||||
|
|
||||
|
entries = result_table.find_all('tr', attrs = {'class': 'torrent'}) |
||||
|
for result in entries: |
||||
|
|
||||
|
link = result.find('a', attrs = {'dir': 'ltr'}) |
||||
|
url = result.find('a', attrs = {'title': 'Download'}) |
||||
|
tds = result.find_all('td') |
||||
|
size = tds[4].contents[0].strip('\n ') |
||||
|
|
||||
|
results.append({ |
||||
|
'id': link['href'].replace('torrents.php?id=', '').split('&')[0], |
||||
|
'name': link.contents[0], |
||||
|
'url': self.urls['download'] % url['href'], |
||||
|
'detail_url': self.urls['download'] % link['href'], |
||||
|
'size': self.parseSize(size), |
||||
|
'seeders': tryInt(tds[len(tds)-2].string), |
||||
|
'leechers': tryInt(tds[len(tds)-1].string), |
||||
|
}) |
||||
|
except: |
||||
|
log.error('Failed to parsing %s: %s', (self.getName(), traceback.format_exc())) |
||||
|
|
||||
|
|
||||
|
def getLoginParams(self): |
||||
|
return { |
||||
|
'username': self.conf('username'), |
||||
|
'password': self.conf('password'), |
||||
|
'keeplogged': '1', |
||||
|
'login': 'Login', |
||||
|
} |
||||
|
|
||||
|
def loginSuccess(self, output): |
||||
|
return 'logout.php' in output.lower() |
||||
|
|
||||
|
loginCheckSuccess = loginSuccess |
||||
|
|
||||
|
def getSceneOnly(self): |
||||
|
return '1' if self.conf('scene_only') else '' |
||||
|
|
||||
|
|
||||
|
config = [{ |
||||
|
'name': 'alpharatio', |
||||
|
'groups': [ |
||||
|
{ |
||||
|
'tab': 'searcher', |
||||
|
'list': 'torrent_providers', |
||||
|
'name': 'AlphaRatio', |
||||
|
'description': '<a href="http://alpharatio.cc/">AlphaRatio</a>', |
||||
|
'wizard': True, |
||||
|
'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACX0lEQVQ4jbWTX0hTURzHv+fu3umdV9GtOZ3pcllGBomJ9RCmkiWIEJUQET2EMqF86aFeegqLHgoio1ICScoieugPiBlFFmpROUjNIub+NKeba2rqvdvuPKeXDIcsgugHB378fj8+X37fcw5hjOFfgvtTc8o7mdveHWv0+YJ5iWb45SQWi2kc7olCnteoHCGUMqbpejBkO99rPDlW5rjV3FjZkmXU+3SiKK8EkOUVxj2+9bZOe8ebhZxSRTCIQmAES1oLQADKp4EIc8gRFr3t+/SNe0oLelatYM0zO56dqS3fmh4eXkoxIrWvAwXegLta8bymYyak9lyGR7d57eHHtOt7aNaQ0AORU8OEqlg0HURTnXi96cCaK0AYEW0l+MAoQoIp48PHke0JAYwyBkYhameUQ3vz7lTt3NRdKH0ajxgqQMJzAMdBkRVdYgAAEA71G2Z6MnOyvSmSJB/bFblN5DHEsosghf3zZduK+1fdQhyEcKitr+r0B2dMAyPOcmd02oxiC2jUjJaSwbPZpoLJhAA1Ci3hGURRlO0Of8nN9/MNUUXSkrQsFQ4meNORG6/G2O/jGXdZ044OKzg3z3r77TUre81tL1pxirLMWnsoMB00LtfjPLh67/OJH3xRMgiHb96JOCVbxbobRONBQNqScffJ6JE4E2VZFvv6BirbXpkboGcA4eGaDOV73G4LAFBKSWRhNsmqfnHCosG159Lxt++GdgC/XuLD3sH60/fdFxjJBNMDAAVZ8CNfVJxPLzbs/uqa2Lj/0stHkWSDFlwS4FIhRKei3a3VNeS//sa/iZ/B6hMIr7Fq4QAAAABJRU5ErkJggg==', |
||||
|
'options': [ |
||||
|
{ |
||||
|
'name': 'enabled', |
||||
|
'type': 'enabler', |
||||
|
'default': False, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'username', |
||||
|
'default': '', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'password', |
||||
|
'default': '', |
||||
|
'type': 'password', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'seed_ratio', |
||||
|
'label': 'Seed ratio', |
||||
|
'type': 'float', |
||||
|
'default': 1, |
||||
|
'description': 'Will not be (re)moved until this seed ratio is met.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'seed_time', |
||||
|
'label': 'Seed time', |
||||
|
'type': 'int', |
||||
|
'default': 40, |
||||
|
'description': 'Will not be (re)moved until this seed time (in hours) is met.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'scene_only', |
||||
|
'type': 'bool', |
||||
|
'default': False, |
||||
|
'description': 'Only allow scene releases.' |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'extra_score', |
||||
|
'advanced': True, |
||||
|
'label': 'Extra Score', |
||||
|
'type': 'int', |
||||
|
'default': 0, |
||||
|
'description': 'Starting score for each release found via this provider.', |
||||
|
} |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}] |
@ -0,0 +1,131 @@ |
|||||
|
import re |
||||
|
import json |
||||
|
import traceback |
||||
|
|
||||
|
from couchpotato.core.helpers.variable import tryInt, getIdentifier |
||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media._base.providers.torrent.base import TorrentProvider |
||||
|
|
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
|
||||
|
class Base(TorrentProvider): |
||||
|
|
||||
|
urls = { |
||||
|
'test': 'https://hd4free.xyz/', |
||||
|
'detail': 'https://hd4free.xyz/details.php?id=%s', |
||||
|
'search': 'https://hd4free.xyz/searchapi.php?apikey=%s&username=%s&imdbid=%s&internal=%s', |
||||
|
'download': 'https://hd4free.xyz/download.php?torrent=%s&torrent_pass=%s', |
||||
|
} |
||||
|
|
||||
|
http_time_between_calls = 1 # Seconds |
||||
|
|
||||
|
def _search(self, movie, quality, results): |
||||
|
data = self.getJsonData(self.urls['search'] % (self.conf('apikey'), self.conf('username'), getIdentifier(movie), self.conf('internal_only'))) |
||||
|
|
||||
|
if data: |
||||
|
try: |
||||
|
#for result in data[]: |
||||
|
for key, result in data.iteritems(): |
||||
|
if tryInt(result['total_results']) == 0: |
||||
|
return |
||||
|
torrentscore = self.conf('extra_score') |
||||
|
releasegroup = result['releasegroup'] |
||||
|
resolution = result['resolution'] |
||||
|
encoding = result['encoding'] |
||||
|
freeleech = tryInt(result['freeleech']) |
||||
|
seeders = tryInt(result['seeders']) |
||||
|
torrent_desc = '/ %s / %s / %s / %s seeders' % (releasegroup, resolution, encoding, seeders) |
||||
|
|
||||
|
if freeleech > 0 and self.conf('prefer_internal'): |
||||
|
torrent_desc += '/ Internal' |
||||
|
torrentscore += 200 |
||||
|
|
||||
|
if seeders == 0: |
||||
|
torrentscore = 0 |
||||
|
|
||||
|
name = result['release_name'] |
||||
|
year = tryInt(result['year']) |
||||
|
|
||||
|
results.append({ |
||||
|
'id': tryInt(result['torrentid']), |
||||
|
'name': re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) %s' % (name, year, torrent_desc)), |
||||
|
'url': self.urls['download'] % (result['torrentid'], result['torrentpass']), |
||||
|
'detail_url': self.urls['detail'] % result['torrentid'], |
||||
|
'size': tryInt(result['size']), |
||||
|
'seeders': tryInt(result['seeders']), |
||||
|
'leechers': tryInt(result['leechers']), |
||||
|
'age': tryInt(result['age']), |
||||
|
'score': torrentscore |
||||
|
}) |
||||
|
except: |
||||
|
log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc())) |
||||
|
config = [{ |
||||
|
'name': 'hd4free', |
||||
|
'groups': [ |
||||
|
{ |
||||
|
'tab': 'searcher', |
||||
|
'list': 'torrent_providers', |
||||
|
'name': 'HD4Free', |
||||
|
'wizard': True, |
||||
|
'description': '<a href="https://hd4free.xyz">HD4Free</a>', |
||||
|
'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABX1BMVEUF6nsH33cJ03EJ1XIJ1nMKzXIKz28Lym4MxGsMxWsMx2wNvmgNv2kNwGkNwWwOuGgOuWYOuWcOumcOu2cOvmgPtWQPtmUPt2UPt2YQr2IQsGIQsGMQsmMQs2QRqmARq2ARrmERrmISpV4SpmASp14SqF8ToFsToFwToVwTo10TpV0UnFoUn1sVllcVmFgWkFUWklYXjVQXjlMXkFUYh1EYilIYi1MZhlEafk0af04agE4agU4beEobeUsbe0wcdUkeaUQebUYfZEMfZ0QgX0AgYEAgYUEhWj4iVz0iWD0jTzkkSzcmQTMmQzQnPTInPjInPzIoNy8oOC8oODAoOTAoOjApMi0pNC4pNS4qLCoqLSsqLisqMCwrJygrKCgrKCkrKSkrKikrKiorKyosIyYsIycsJCcsJScsJigtHyUuGCIuGiMuGyMuHCMuHCQvEyAvFSEvFiEvFyE0ABU0ABY5lYz4AAAA3ElEQVR4AWNIQAMMiYmJCYkIkMCQnpKWkZ4KBGlARlpaLEOor194kI+Pj6+PT0CET0AYg46Alr22NDeHkBinnq6SkitDrolDgYtaapajdpGppoFfGkMhv2GxE0uuPwNfsk6mhHMOQ54isxmbUJKCtWx+tIZQcDpDtqSol7qIMqsRu3dIhJxxFkOBoF2JG5O7lSqjh5S/tkkWQ5SBTbqnfkymv2WGLa95YCSDhZiMvKIwj4GJCpesuDivK0N6VFRUYlRyfHJUchQQJDMkxsfHJcTHAxEIxMVj+BZDAACjwkqhYgsTAAAAAABJRU5ErkJggg==', |
||||
|
'options': [ |
||||
|
{ |
||||
|
'name': 'enabled', |
||||
|
'type': 'enabler', |
||||
|
'default': False, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'username', |
||||
|
'default': '', |
||||
|
'description': 'Enter your site username.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'apikey', |
||||
|
'default': '', |
||||
|
'label': 'API Key', |
||||
|
'description': 'Enter your site api key. This can be find on <a href="https://hd4free.xyz/usercp.php?action=security">Profile Security</a>', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'seed_ratio', |
||||
|
'label': 'Seed ratio', |
||||
|
'type': 'float', |
||||
|
'default': 0, |
||||
|
'description': 'Will not be (re)moved until this seed ratio is met. HD4Free minimum is 1:1.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'seed_time', |
||||
|
'label': 'Seed time', |
||||
|
'type': 'int', |
||||
|
'default': 0, |
||||
|
'description': 'Will not be (re)moved until this seed time (in hours) is met. HD4Free minimum is 72 hours.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'prefer_internal', |
||||
|
'advanced': True, |
||||
|
'type': 'bool', |
||||
|
'default': 1, |
||||
|
'description': 'Favors internal releases over non-internal releases.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'internal_only', |
||||
|
'advanced': True, |
||||
|
'label': 'Internal Only', |
||||
|
'type': 'bool', |
||||
|
'default': False, |
||||
|
'description': 'Only download releases marked as HD4Free internal', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'extra_score', |
||||
|
'advanced': True, |
||||
|
'label': 'Extra Score', |
||||
|
'type': 'int', |
||||
|
'default': 0, |
||||
|
'description': 'Starting score for each release found via this provider.', |
||||
|
} |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}] |
@ -0,0 +1,230 @@ |
|||||
|
import re |
||||
|
import traceback |
||||
|
import random |
||||
|
from datetime import datetime |
||||
|
|
||||
|
from couchpotato import fireEvent |
||||
|
from couchpotato.core.helpers.variable import tryInt, getIdentifier |
||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media._base.providers.torrent.base import TorrentMagnetProvider |
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
class Base(TorrentMagnetProvider): |
||||
|
|
||||
|
urls = { |
||||
|
'test': 'https://torrentapi.org/pubapi_v2.php?app_id=couchpotato', |
||||
|
'token': 'https://torrentapi.org/pubapi_v2.php?get_token=get_token&app_id=couchpotato', |
||||
|
'search': 'https://torrentapi.org/pubapi_v2.php?token=%s&mode=search&search_imdb=%s&min_seeders=%s&min_leechers' |
||||
|
'=%s&ranked=%s&category=movies&format=json_extended&app_id=couchpotato', |
||||
|
} |
||||
|
|
||||
|
http_time_between_calls = 2 # Seconds |
||||
|
_token = 0 |
||||
|
|
||||
|
def _search(self, movie, quality, results): |
||||
|
hasresults = 0 |
||||
|
curryear = datetime.now().year |
||||
|
movieid = getIdentifier(movie) |
||||
|
|
||||
|
try: |
||||
|
movieyear = movie['info']['year'] |
||||
|
except: |
||||
|
log.error('RARBG: Couldn\'t get movie year') |
||||
|
movieyear = 0 |
||||
|
|
||||
|
self.getToken() |
||||
|
|
||||
|
if (self._token != 0) and (movieyear == 0 or movieyear <= curryear): |
||||
|
data = self.getJsonData(self.urls['search'] % (self._token, movieid, self.conf('min_seeders'), |
||||
|
self.conf('min_leechers'), self.conf('ranked_only')), headers = self.getRequestHeaders()) |
||||
|
|
||||
|
if data: |
||||
|
if 'error_code' in data: |
||||
|
if data['error'] == 'No results found': |
||||
|
log.debug('RARBG: No results returned from Rarbg') |
||||
|
else: |
||||
|
if data['error_code'] == 10: |
||||
|
log.error(data['error'], movieid) |
||||
|
else: |
||||
|
log.error('RARBG: There is an error in the returned JSON: %s', data['error']) |
||||
|
else: |
||||
|
hasresults = 1 |
||||
|
|
||||
|
try: |
||||
|
if hasresults: |
||||
|
for result in data['torrent_results']: |
||||
|
name = result['title'] |
||||
|
titlesplit = re.split('-', name) |
||||
|
releasegroup = titlesplit[len(titlesplit)-1] |
||||
|
|
||||
|
xtrainfo = self.find_info(name) |
||||
|
encoding = xtrainfo[0] |
||||
|
resolution = xtrainfo[1] |
||||
|
# source = xtrainfo[2] |
||||
|
pubdate = result['pubdate'] # .strip(' +0000') |
||||
|
try: |
||||
|
pubdate = datetime.strptime(pubdate, '%Y-%m-%d %H:%M:%S +0000') |
||||
|
now = datetime.utcnow() |
||||
|
age = (now - pubdate).days |
||||
|
except ValueError: |
||||
|
log.debug('RARBG: Bad pubdate') |
||||
|
age = 0 |
||||
|
|
||||
|
torrentscore = self.conf('extra_score') |
||||
|
seeders = tryInt(result['seeders']) |
||||
|
torrent_desc = '/ %s / %s / %s / %s seeders' % (releasegroup, resolution, encoding, seeders) |
||||
|
|
||||
|
if seeders == 0: |
||||
|
torrentscore = 0 |
||||
|
|
||||
|
sliceyear = result['pubdate'][0:4] |
||||
|
year = tryInt(sliceyear) |
||||
|
|
||||
|
results.append({ |
||||
|
'id': random.randint(100, 9999), |
||||
|
'name': re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) %s' % (name, year, torrent_desc)), |
||||
|
'url': result['download'], |
||||
|
'detail_url': result['info_page'], |
||||
|
'size': tryInt(result['size']/1048576), # rarbg sends in bytes |
||||
|
'seeders': tryInt(result['seeders']), |
||||
|
'leechers': tryInt(result['leechers']), |
||||
|
'age': tryInt(age), |
||||
|
'score': torrentscore |
||||
|
}) |
||||
|
|
||||
|
except RuntimeError: |
||||
|
log.error('RARBG: Failed getting results from %s: %s', (self.getName(), traceback.format_exc())) |
||||
|
|
||||
|
def getToken(self): |
||||
|
tokendata = self.getJsonData(self.urls['token'], cache_timeout = 900, headers = self.getRequestHeaders()) |
||||
|
if tokendata: |
||||
|
try: |
||||
|
token = tokendata['token'] |
||||
|
if self._token != token: |
||||
|
log.debug('RARBG: GOT TOKEN: %s', token) |
||||
|
self._token = token |
||||
|
except: |
||||
|
log.error('RARBG: Failed getting token from Rarbg: %s', traceback.format_exc()) |
||||
|
self._token = 0 |
||||
|
|
||||
|
def getRequestHeaders(self): |
||||
|
return { |
||||
|
'User-Agent': fireEvent('app.version', single = True) |
||||
|
} |
||||
|
|
||||
|
@staticmethod |
||||
|
def find_info(filename): |
||||
|
# CODEC # |
||||
|
codec = 'x264' |
||||
|
v = re.search('(?i)(x265|h265|h\.265)', filename) |
||||
|
if v: |
||||
|
codec = 'x265' |
||||
|
|
||||
|
v = re.search('(?i)(xvid)', filename) |
||||
|
if v: |
||||
|
codec = 'xvid' |
||||
|
|
||||
|
# RESOLUTION # |
||||
|
resolution = 'SD' |
||||
|
a = re.search('(?i)(720p)', filename) |
||||
|
if a: |
||||
|
resolution = '720p' |
||||
|
|
||||
|
a = re.search('(?i)(1080p)', filename) |
||||
|
if a: |
||||
|
resolution = '1080p' |
||||
|
|
||||
|
a = re.search('(?i)(2160p)', filename) |
||||
|
if a: |
||||
|
resolution = '2160p' |
||||
|
|
||||
|
# SOURCE # |
||||
|
source = 'HD-Rip' |
||||
|
s = re.search('(?i)(WEB-DL|WEB_DL|WEB\.DL)', filename) |
||||
|
if s: |
||||
|
source = 'WEB-DL' |
||||
|
|
||||
|
s = re.search('(?i)(WEBRIP)', filename) |
||||
|
if s: |
||||
|
source = 'WEBRIP' |
||||
|
|
||||
|
s = re.search('(?i)(DVDR|DVDRip|DVD-Rip)', filename) |
||||
|
if s: |
||||
|
source = 'DVD-R' |
||||
|
|
||||
|
s = re.search('(?i)(BRRIP|BDRIP|BluRay)', filename) |
||||
|
if s: |
||||
|
source = 'BR-Rip' |
||||
|
|
||||
|
s = re.search('(?i)BluRay(.*)REMUX', filename) |
||||
|
if s: |
||||
|
source = 'BluRay-Remux' |
||||
|
|
||||
|
s = re.search('(?i)BluRay(.*)\.(AVC|VC-1)\.', filename) |
||||
|
if s: |
||||
|
source = 'BluRay-Full' |
||||
|
|
||||
|
return_info = [codec, resolution, source] |
||||
|
return return_info |
||||
|
|
||||
|
config = [{ |
||||
|
'name': 'rarbg', |
||||
|
'groups': [ |
||||
|
{ |
||||
|
'tab': 'searcher', |
||||
|
'list': 'torrent_providers', |
||||
|
'name': 'Rarbg', |
||||
|
'wizard': True, |
||||
|
'description': '<a href="https://rarbg.to/torrents.php">RARBG</a>', |
||||
|
'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB+UlEQVQ4jYXTP2hcRxDH8c8JJZjbYNy8V7gIr0qhg5AiFnETX' |
||||
|
'+PmVAtSmKDaUhUiFyGxjXFlp0hhHy5cqFd9lSGcU55cBU6EEMIj5dsmMewSjNGmOJ3852wysMyww37n94OdXimlh49xDR/hxGr' |
||||
|
'8hZ/xx0qnlHK5lPKk/H/8U0r5oZTyQSmltzzr+AKfT+ed8UFLeHNAH1UVbA2r88NBfQcX8O2yv74sUqKNWT+T01sy2+zpUbS/w' |
||||
|
'/awvo7H+O0NQEA/LPKlQWXrSgUmR9HxcZQwmbZGw/pc4MsVAIT+IjcNw80aTjaaem1vPCNlGakj1C6uWFiqeDtyTvoyqAKhBn+' |
||||
|
'+E7CkxC6Zzjop57XpUSenpIuMhpXAc/zyHkAicRSjw6fHZ1ewPdqwszWAB2hXACln8+NWSlld9zX9YN7GhajQXz5+joPXR66de' |
||||
|
'U1J27Zi7FzaqE0OdmwNGzF2Ymzt3j+E8/gJH64AFlozKS4+Be7tjwyaIKVsOpnavX0II9x8ByDLKco5SwvjL0MI/z64tyOcwsf' |
||||
|
'jQw8PJvAdvsb6GSBlxI7UyTnD37i7OWhe3NrflvOit3djbDKdwR181SulXMXdrkubbdvKaOpK09S/4jP8iG9m8zmJjCoEg0HzO' |
||||
|
'77vna7zp7ju1TqfYIyZxT7dwCd4eWr7BR7h2X8S6gShJlbKYQAAAABJRU5ErkJggg==', |
||||
|
'options': [ |
||||
|
{ |
||||
|
'name': 'enabled', |
||||
|
'type': 'enabler', |
||||
|
'default': False, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'ranked_only', |
||||
|
'advanced': True, |
||||
|
'label': 'Ranked Only', |
||||
|
'type': 'int', |
||||
|
'default': 1, |
||||
|
'description': 'Only ranked torrents (internal), scene releases, rarbg releases. ' |
||||
|
'Enter 1 (true) or 0 (false)', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'min_seeders', |
||||
|
'advanced': True, |
||||
|
'label': 'Minimum Seeders', |
||||
|
'type': 'int', |
||||
|
'default': 10, |
||||
|
'description': 'Minium amount of seeders the release must have.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'min_leechers', |
||||
|
'advanced': True, |
||||
|
'label': 'Minimum leechers', |
||||
|
'type': 'int', |
||||
|
'default': 0, |
||||
|
'description': 'Minium amount of leechers the release must have.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'extra_score', |
||||
|
'advanced': True, |
||||
|
'label': 'Extra Score', |
||||
|
'type': 'int', |
||||
|
'default': 0, |
||||
|
'description': 'Starting score for each release found via this provider.', |
||||
|
} |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}] |
@ -0,0 +1,138 @@ |
|||||
|
import traceback |
||||
|
|
||||
|
from bs4 import BeautifulSoup |
||||
|
from couchpotato.core.helpers.encoding import tryUrlencode, toUnicode |
||||
|
from couchpotato.core.helpers.variable import tryInt |
||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media._base.providers.torrent.base import TorrentProvider |
||||
|
|
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
|
||||
|
class Base(TorrentProvider): |
||||
|
|
||||
|
urls = { |
||||
|
'test': 'https://www.scenetime.com/', |
||||
|
'login': 'https://www.scenetime.com/takelogin.php', |
||||
|
'login_check': 'https://www.scenetime.com/inbox.php', |
||||
|
'detail': 'https://www.scenetime.com/details.php?id=%s', |
||||
|
'search': 'https://www.scenetime.com/browse.php?search=%s&cat=%d', |
||||
|
'download': 'https://www.scenetime.com/download.php/%s/%s', |
||||
|
} |
||||
|
|
||||
|
cat_ids = [ |
||||
|
([59], ['720p', '1080p']), |
||||
|
([81], ['brrip']), |
||||
|
([102], ['bd50']), |
||||
|
([3], ['dvdrip']), |
||||
|
] |
||||
|
|
||||
|
http_time_between_calls = 1 # Seconds |
||||
|
cat_backup_id = None |
||||
|
|
||||
|
def _searchOnTitle(self, title, movie, quality, results): |
||||
|
|
||||
|
url = self.urls['search'] % (tryUrlencode('%s %s' % (title.replace(':', ''), movie['info']['year'])), self.getCatId(quality)[0]) |
||||
|
data = self.getHTMLData(url) |
||||
|
|
||||
|
if data: |
||||
|
html = BeautifulSoup(data) |
||||
|
|
||||
|
try: |
||||
|
result_table = html.find(attrs = {'id': 'torrenttable'}) |
||||
|
|
||||
|
if not result_table: |
||||
|
log.error('failed to generate result_table') |
||||
|
return |
||||
|
|
||||
|
entries = result_table.find_all('tr') |
||||
|
|
||||
|
for result in entries[1:]: |
||||
|
cells = result.find_all('td') |
||||
|
link = result.find('a', attrs = {'class': 'index'}) |
||||
|
torrent_id = link['href'].replace('download.php/','').split('/')[0] |
||||
|
torrent_file = link['href'].replace('download.php/','').split('/')[1] |
||||
|
size = self.parseSize(cells[5].contents[0] + cells[5].contents[2]) |
||||
|
name_row = cells[1].contents[0] |
||||
|
name = name_row.getText() |
||||
|
seeders_row = cells[6].contents[0] |
||||
|
seeders = seeders_row.getText() |
||||
|
|
||||
|
|
||||
|
results.append({ |
||||
|
'id': torrent_id, |
||||
|
'name': name, |
||||
|
'url': self.urls['download'] % (torrent_id,torrent_file), |
||||
|
'detail_url': self.urls['detail'] % torrent_id, |
||||
|
'size': size, |
||||
|
'seeders': seeders, |
||||
|
}) |
||||
|
|
||||
|
except: |
||||
|
log.error('Failed to parsing %s: %s', (self.getName(), traceback.format_exc())) |
||||
|
|
||||
|
def getLoginParams(self): |
||||
|
return { |
||||
|
'login': 'submit', |
||||
|
'username': self.conf('username'), |
||||
|
'password': self.conf('password'), |
||||
|
} |
||||
|
|
||||
|
def loginSuccess(self, output): |
||||
|
return 'logout.php' in output.lower() or 'Welcome' in output.lower() |
||||
|
|
||||
|
loginCheckSuccess = loginSuccess |
||||
|
|
||||
|
|
||||
|
config = [{ |
||||
|
'name': 'scenetime', |
||||
|
'groups': [ |
||||
|
{ |
||||
|
'tab': 'searcher', |
||||
|
'list': 'torrent_providers', |
||||
|
'name': 'SceneTime', |
||||
|
'description': '<a href="https://www.scenetime.com">SceneTime</a>', |
||||
|
'wizard': True, |
||||
|
'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAIwSURBVDhPZZFbSBRRGMePs7Mzjma7+9AWWxpeYrXLkrcIfUwIpIeK3tO1hWhfltKwhyJMFIqgCz2EpdHWRun2oGG02O2hlYyypY21CygrlbhRIYHizO6/mdk5szPtB785hzm//zeXj7Q89q4I4QaQBx6ZHQY84Efq4Rrbg4rxVmx61AJ2pFY/twzvhP1hU4ZwIQ8K7mw1wdzdhrrxQ7g8E0Q09R6flubw+mcM7tHWPJcwt91ghuTQUDWYW8rejbrRA3i1OA0xLYGWJO8bxw6q50YIc70CRoQbNbj2MQgpkwsrpTYI7ze5CoS5UgYjpTd3YWphWg1l1CuwLC4jufQNtaG9JleBWM67YKR6oBlzf+bVoPIOUiaNwVgIzcF9sF3aknMvZFfCnnNCp9eJqqsNSKQ+qw2USssNzrzoh9Dnynmaq6yEPe2AkfX9lXjy5akWz9ZkcgqVFz0mj0KsJ0tgROh2oCfSJ3/3ihaHPA0Rh+/7UNhtN7kKhAsI+J+a3u2If49r8WxFZiawtsuR5xLumBUU3s/B2bkOm0+V4V3yrTwFOgcg8SMBe8CmuxTC+SygFB3l8TzxDLOpWYiSqEWzFf0ahc2/RncphPcSUIqPWPFhPqZFcrUqraLzXkA+Z3WXQvh2eaNR3MHmNVB+YPjNMMqPb9Q9I6YGRR0WTMQj6hOV+f/++wuDLwfg7iqH4GVMQQrh28w3Nvgd2H22Hk09jag6UYoSH4/C9gKTo9NG8A8MPUM4DJp74gAAAABJRU5ErkJggg==', |
||||
|
'options': [ |
||||
|
{ |
||||
|
'name': 'enabled', |
||||
|
'type': 'enabler', |
||||
|
'default': False, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'username', |
||||
|
'default': '', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'password', |
||||
|
'default': '', |
||||
|
'type': 'password', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'seed_ratio', |
||||
|
'label': 'Seed ratio', |
||||
|
'type': 'float', |
||||
|
'default': 1, |
||||
|
'description': 'Will not be (re)moved until this seed ratio is met.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'seed_time', |
||||
|
'label': 'Seed time', |
||||
|
'type': 'int', |
||||
|
'default': 40, |
||||
|
'description': 'Will not be (re)moved until this seed time (in hours) is met.', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'extra_score', |
||||
|
'advanced': True, |
||||
|
'label': 'Extra Score', |
||||
|
'type': 'int', |
||||
|
'default': 20, |
||||
|
'description': 'Starting score for each release found via this provider.', |
||||
|
} |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}] |
@ -1,277 +0,0 @@ |
|||||
.search_form { |
|
||||
display: inline-block; |
|
||||
vertical-align: middle; |
|
||||
position: absolute; |
|
||||
right: 105px; |
|
||||
top: 0; |
|
||||
text-align: right; |
|
||||
height: 100%; |
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1); |
|
||||
z-index: 20; |
|
||||
border: 0 solid transparent; |
|
||||
border-bottom-width: 4px; |
|
||||
} |
|
||||
.search_form:hover { |
|
||||
border-color: #047792; |
|
||||
} |
|
||||
|
|
||||
@media all and (max-width: 480px) { |
|
||||
.search_form { |
|
||||
right: 44px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.search_form.focused, |
|
||||
.search_form.shown { |
|
||||
border-color: #04bce6; |
|
||||
} |
|
||||
|
|
||||
.search_form .input { |
|
||||
height: 100%; |
|
||||
overflow: hidden; |
|
||||
width: 45px; |
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1); |
|
||||
} |
|
||||
|
|
||||
.search_form.focused .input, |
|
||||
.search_form.shown .input { |
|
||||
width: 380px; |
|
||||
background: #4e5969; |
|
||||
} |
|
||||
|
|
||||
.search_form .input input { |
|
||||
border-radius: 0; |
|
||||
display: block; |
|
||||
border: 0; |
|
||||
background: none; |
|
||||
color: #FFF; |
|
||||
font-size: 25px; |
|
||||
height: 100%; |
|
||||
width: 100%; |
|
||||
opacity: 0; |
|
||||
padding: 0 40px 0 10px; |
|
||||
transition: all .4s ease-in-out .2s; |
|
||||
} |
|
||||
.search_form.focused .input input, |
|
||||
.search_form.shown .input input { |
|
||||
opacity: 1; |
|
||||
} |
|
||||
|
|
||||
.search_form input::-ms-clear { |
|
||||
width : 0; |
|
||||
height: 0; |
|
||||
} |
|
||||
|
|
||||
@media all and (max-width: 480px) { |
|
||||
.search_form .input input { |
|
||||
font-size: 15px; |
|
||||
} |
|
||||
|
|
||||
.search_form.focused .input, |
|
||||
.search_form.shown .input { |
|
||||
width: 277px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.search_form .input a { |
|
||||
position: absolute; |
|
||||
top: 0; |
|
||||
right: 0; |
|
||||
width: 44px; |
|
||||
height: 100%; |
|
||||
cursor: pointer; |
|
||||
vertical-align: middle; |
|
||||
text-align: center; |
|
||||
line-height: 66px; |
|
||||
font-size: 15px; |
|
||||
color: #FFF; |
|
||||
} |
|
||||
|
|
||||
.search_form .input a:after { |
|
||||
content: "\e03e"; |
|
||||
} |
|
||||
|
|
||||
.search_form.shown.filled .input a:after { |
|
||||
content: "\e04e"; |
|
||||
} |
|
||||
|
|
||||
@media all and (max-width: 480px) { |
|
||||
.search_form .input a { |
|
||||
line-height: 44px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.search_form .results_container { |
|
||||
text-align: left; |
|
||||
position: absolute; |
|
||||
background: #5c697b; |
|
||||
margin: 4px 0 0; |
|
||||
width: 470px; |
|
||||
min-height: 50px; |
|
||||
box-shadow: 0 20px 20px -10px rgba(0,0,0,0.55); |
|
||||
display: none; |
|
||||
} |
|
||||
@media all and (max-width: 480px) { |
|
||||
.search_form .results_container { |
|
||||
width: 320px; |
|
||||
} |
|
||||
} |
|
||||
.search_form.focused.filled .results_container, |
|
||||
.search_form.shown.filled .results_container { |
|
||||
display: block; |
|
||||
} |
|
||||
|
|
||||
.search_form .results { |
|
||||
max-height: 570px; |
|
||||
overflow-x: hidden; |
|
||||
} |
|
||||
|
|
||||
.media_result { |
|
||||
overflow: hidden; |
|
||||
height: 50px; |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
.media_result .options { |
|
||||
position: absolute; |
|
||||
height: 100%; |
|
||||
top: 0; |
|
||||
left: 30px; |
|
||||
right: 0; |
|
||||
padding: 13px; |
|
||||
border: 1px solid transparent; |
|
||||
border-width: 1px 0; |
|
||||
border-radius: 0; |
|
||||
box-shadow: inset 0 1px 8px rgba(0,0,0,0.25); |
|
||||
} |
|
||||
.media_result .options > .in_library_wanted { |
|
||||
margin-top: -7px; |
|
||||
} |
|
||||
|
|
||||
.media_result .options > div { |
|
||||
border: 0; |
|
||||
} |
|
||||
|
|
||||
.media_result .options .thumbnail { |
|
||||
vertical-align: middle; |
|
||||
} |
|
||||
|
|
||||
.media_result .options select { |
|
||||
vertical-align: middle; |
|
||||
display: inline-block; |
|
||||
margin-right: 10px; |
|
||||
} |
|
||||
.media_result .options select[name=title] { width: 170px; } |
|
||||
.media_result .options select[name=profile] { width: 90px; } |
|
||||
.media_result .options select[name=category] { width: 80px; } |
|
||||
|
|
||||
@media all and (max-width: 480px) { |
|
||||
|
|
||||
.media_result .options select[name=title] { width: 90px; } |
|
||||
.media_result .options select[name=profile] { width: 50px; } |
|
||||
.media_result .options select[name=category] { width: 50px; } |
|
||||
|
|
||||
} |
|
||||
|
|
||||
.media_result .options .button { |
|
||||
vertical-align: middle; |
|
||||
display: inline-block; |
|
||||
} |
|
||||
|
|
||||
.media_result .options .message { |
|
||||
height: 100%; |
|
||||
font-size: 20px; |
|
||||
color: #fff; |
|
||||
line-height: 20px; |
|
||||
} |
|
||||
|
|
||||
.media_result .data { |
|
||||
position: absolute; |
|
||||
height: 100%; |
|
||||
top: 0; |
|
||||
left: 30px; |
|
||||
right: 0; |
|
||||
background: #5c697b; |
|
||||
cursor: pointer; |
|
||||
border-top: 1px solid rgba(255,255,255, 0.08); |
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1); |
|
||||
} |
|
||||
.media_result .data.open { |
|
||||
left: 100% !important; |
|
||||
} |
|
||||
|
|
||||
.media_result:last-child .data { border-bottom: 0; } |
|
||||
|
|
||||
.media_result .in_wanted, .media_result .in_library { |
|
||||
position: absolute; |
|
||||
bottom: 2px; |
|
||||
left: 14px; |
|
||||
font-size: 11px; |
|
||||
} |
|
||||
|
|
||||
.media_result .thumbnail { |
|
||||
width: 34px; |
|
||||
min-height: 100%; |
|
||||
display: block; |
|
||||
margin: 0; |
|
||||
vertical-align: top; |
|
||||
} |
|
||||
|
|
||||
.media_result .info { |
|
||||
position: absolute; |
|
||||
top: 20%; |
|
||||
left: 15px; |
|
||||
right: 7px; |
|
||||
vertical-align: middle; |
|
||||
} |
|
||||
|
|
||||
.media_result .info h2 { |
|
||||
margin: 0; |
|
||||
font-weight: normal; |
|
||||
font-size: 20px; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
.search_form .info h2 { |
|
||||
position: absolute; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.media_result .info h2 .title { |
|
||||
display: block; |
|
||||
margin: 0; |
|
||||
text-overflow: ellipsis; |
|
||||
overflow: hidden; |
|
||||
white-space: nowrap; |
|
||||
} |
|
||||
|
|
||||
.search_form .info h2 .title { |
|
||||
position: absolute; |
|
||||
width: 88%; |
|
||||
} |
|
||||
|
|
||||
.media_result .info h2 .year { |
|
||||
padding: 0 5px; |
|
||||
text-align: center; |
|
||||
position: absolute; |
|
||||
width: 12%; |
|
||||
right: 0; |
|
||||
} |
|
||||
|
|
||||
@media all and (max-width: 480px) { |
|
||||
|
|
||||
.search_form .info h2 .year { |
|
||||
font-size: 12px; |
|
||||
margin-top: 7px; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
.search_form .mask, |
|
||||
.media_result .mask { |
|
||||
position: absolute; |
|
||||
height: 100%; |
|
||||
width: 100%; |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
} |
|
@ -0,0 +1,503 @@ |
|||||
|
@import "_mixins"; |
||||
|
|
||||
|
.search_form { |
||||
|
display: inline-block; |
||||
|
z-index: 11; |
||||
|
width: 44px; |
||||
|
position: relative; |
||||
|
|
||||
|
* { |
||||
|
transform: translateZ(0); |
||||
|
} |
||||
|
|
||||
|
.icon-search { |
||||
|
position: absolute; |
||||
|
z-index: 2; |
||||
|
top: 50%; |
||||
|
left: 0; |
||||
|
height: 100%; |
||||
|
text-align: center; |
||||
|
color: #FFF; |
||||
|
font-size: 20px; |
||||
|
transform: translateY(-50%); |
||||
|
} |
||||
|
|
||||
|
.wrapper { |
||||
|
position: absolute; |
||||
|
left: 44px; |
||||
|
bottom: 0; |
||||
|
background: $primary_color; |
||||
|
border-radius: $border_radius 0 0 $border_radius; |
||||
|
display: none; |
||||
|
box-shadow: 0 0 15px 2px rgba(0,0,0,.15); |
||||
|
|
||||
|
&:before { |
||||
|
transform: rotate(45deg); |
||||
|
content: ''; |
||||
|
display: block; |
||||
|
position: absolute; |
||||
|
height: 10px; |
||||
|
width: 10px; |
||||
|
background: $primary_color; |
||||
|
left: -6px; |
||||
|
bottom: 16px; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
background: $background_color; |
||||
|
border-radius: $border_radius 0 0 $border_radius; |
||||
|
position: relative; |
||||
|
left: 4px; |
||||
|
height: 44px; |
||||
|
overflow: hidden; |
||||
|
width: 100%; |
||||
|
|
||||
|
input { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
height: 100%; |
||||
|
width: 100%; |
||||
|
z-index: 1; |
||||
|
|
||||
|
&::-ms-clear { |
||||
|
width : 0; |
||||
|
height: 0; |
||||
|
} |
||||
|
|
||||
|
&:focus { |
||||
|
background: rgba($theme_off, .2); |
||||
|
|
||||
|
&::-webkit-input-placeholder { |
||||
|
color: $text_color; |
||||
|
opacity: .7; |
||||
|
} |
||||
|
&::-moz-placeholder { |
||||
|
color: $text_color; |
||||
|
opacity: .7; |
||||
|
} |
||||
|
&:-ms-input-placeholder { |
||||
|
color: $text_color; |
||||
|
opacity: .7; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.filled { |
||||
|
&.focused .icon-search:before, |
||||
|
.page.home & .icon-search:before { |
||||
|
content: '\e80e'; |
||||
|
} |
||||
|
|
||||
|
.input input { |
||||
|
background: rgba($theme_off, .4); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.focused, |
||||
|
&.shown, |
||||
|
.page.home & { |
||||
|
border-color: #04bce6; |
||||
|
|
||||
|
.wrapper { |
||||
|
display: block; |
||||
|
width: 380px; |
||||
|
transform-origin: 0 90%; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
width: 260px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
|
||||
|
input { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.results_container { |
||||
|
min-height: 50px; |
||||
|
text-align: left; |
||||
|
position: relative; |
||||
|
left: 4px; |
||||
|
display: none; |
||||
|
background: $background_color; |
||||
|
border-radius: $border_radius 0 0 0; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
.results { |
||||
|
max-height: 280px; |
||||
|
overflow-x: hidden; |
||||
|
|
||||
|
.media_result { |
||||
|
overflow: hidden; |
||||
|
height: 50px; |
||||
|
position: relative; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
|
||||
|
.options { |
||||
|
position: absolute; |
||||
|
height: 100%; |
||||
|
top: 0; |
||||
|
left: 30px; |
||||
|
right: 0; |
||||
|
background: rgba(0,0,0,.3); |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
left: 0; |
||||
|
} |
||||
|
|
||||
|
> .in_library_wanted { |
||||
|
margin-top: -7px; |
||||
|
} |
||||
|
|
||||
|
> div { |
||||
|
border: 0; |
||||
|
display: flex; |
||||
|
padding: 10px; |
||||
|
align-items: stretch; |
||||
|
justify-content: space-between; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
padding: 3px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
select { |
||||
|
display: block; |
||||
|
height: 100%; |
||||
|
width: 100%; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
min-width: 0; |
||||
|
margin-right: 2px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
margin-right: 5px; |
||||
|
width: 210px; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
width: 140px; |
||||
|
margin-right: 2px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.profile, .category { |
||||
|
margin: 0 5px 0 0; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
margin-right: 2px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.add { |
||||
|
width: 42px; |
||||
|
flex: 1 auto; |
||||
|
|
||||
|
a { |
||||
|
color: #FFF; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.button { |
||||
|
display: block; |
||||
|
background: $primary_color; |
||||
|
text-align: center; |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
.message { |
||||
|
font-size: 20px; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.thumbnail { |
||||
|
width: 30px; |
||||
|
min-height: 100%; |
||||
|
display: block; |
||||
|
margin: 0; |
||||
|
vertical-align: top; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
display: none; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.data { |
||||
|
position: absolute; |
||||
|
height: 100%; |
||||
|
top: 0; |
||||
|
left: 30px; |
||||
|
right: 0; |
||||
|
cursor: pointer; |
||||
|
border-top: 1px solid rgba(255,255,255, 0.08); |
||||
|
transition: all .4s cubic-bezier(0.9,0,0.1,1); |
||||
|
transform: translateX(0); |
||||
|
background: $background_color; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
left: 0; |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
transform: translateX(2%); |
||||
|
} |
||||
|
|
||||
|
&.open { |
||||
|
transform: translateX(100%); |
||||
|
} |
||||
|
|
||||
|
.info { |
||||
|
position: absolute; |
||||
|
top: 20%; |
||||
|
left: 15px; |
||||
|
right: 7px; |
||||
|
vertical-align: middle; |
||||
|
|
||||
|
h2 { |
||||
|
margin: 0; |
||||
|
font-weight: 300; |
||||
|
font-size: 1.25em; |
||||
|
padding: 0; |
||||
|
position: absolute; |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
|
||||
|
.title { |
||||
|
display: inline-block; |
||||
|
margin: 0; |
||||
|
text-overflow: ellipsis; |
||||
|
overflow: hidden; |
||||
|
white-space: nowrap; |
||||
|
flex: 1 auto; |
||||
|
} |
||||
|
|
||||
|
.year { |
||||
|
opacity: .4; |
||||
|
padding: 0 5px; |
||||
|
width: auto; |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.in_wanted, |
||||
|
.in_library { |
||||
|
position: absolute; |
||||
|
top: 15px; |
||||
|
left: 0; |
||||
|
font-size: 11px; |
||||
|
color: $primary_color; |
||||
|
} |
||||
|
|
||||
|
&.in_library_wanted { |
||||
|
.title { |
||||
|
margin-top: -7px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&:hover .info h2 .year { |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
&:last-child .data { |
||||
|
border-bottom: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.focused.filled, |
||||
|
&.shown.filled { |
||||
|
.results_container { |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
border-radius: 0 0 0 $border_radius; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.page.home & { |
||||
|
$input_height: 66px; |
||||
|
$input_height_mobile: 44px; |
||||
|
|
||||
|
display: block; |
||||
|
padding: $padding; |
||||
|
width: 100%; |
||||
|
max-width: 500px; |
||||
|
margin: 0 auto; |
||||
|
height: $input_height + 2*$padding; |
||||
|
position: relative; |
||||
|
margin-top: $padding; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
margin-top: $padding/2; |
||||
|
height: $input_height_mobile + $padding; |
||||
|
} |
||||
|
|
||||
|
.icon-search { |
||||
|
display: block; |
||||
|
color: #000; |
||||
|
right: $padding; |
||||
|
top: $padding; |
||||
|
width: $input_height; |
||||
|
height: $input_height; |
||||
|
line-height: $input_height; |
||||
|
left: auto; |
||||
|
transform: none; |
||||
|
font-size: 2em; |
||||
|
opacity: .5; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
right: $padding/2; |
||||
|
width: $input_height_mobile; |
||||
|
height: $input_height_mobile; |
||||
|
line-height: $input_height_mobile; |
||||
|
right: $padding/2; |
||||
|
top: $padding/2; |
||||
|
font-size: 1.5em; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.wrapper { |
||||
|
border-radius: 0; |
||||
|
box-shadow: none; |
||||
|
bottom: auto; |
||||
|
top: $padding; |
||||
|
left: $padding; |
||||
|
right: $padding; |
||||
|
position: absolute; |
||||
|
width: auto; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
right: $padding/2; |
||||
|
top: $padding/2; |
||||
|
left: $padding/2; |
||||
|
} |
||||
|
|
||||
|
&:before { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
border-radius: 0; |
||||
|
left: 0; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
height: $input_height; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
height: $input_height_mobile; |
||||
|
} |
||||
|
|
||||
|
input { |
||||
|
box-shadow: 0; |
||||
|
font-size: 2em; |
||||
|
font-weight: 400; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
font-size: 1em; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.results_container { |
||||
|
min-height: $input_height; |
||||
|
position: absolute; |
||||
|
top: $input_height; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
border: 1px solid #b7b7b7; |
||||
|
border-top: 0; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
top: $input_height_mobile; |
||||
|
min-height: $input_height_mobile; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@include media-phablet-and-up { |
||||
|
.results { |
||||
|
max-height: 400px; |
||||
|
|
||||
|
.media_result { |
||||
|
height: $input_height; |
||||
|
|
||||
|
|
||||
|
@include media-phablet { |
||||
|
height: $input_height_mobile; |
||||
|
} |
||||
|
|
||||
|
.thumbnail { |
||||
|
width: 40px; |
||||
|
} |
||||
|
|
||||
|
.options { |
||||
|
left: 40px; |
||||
|
|
||||
|
.title { |
||||
|
margin-right: 5px; |
||||
|
width: 320px; |
||||
|
|
||||
|
@include media-phablet { |
||||
|
width: 140px; |
||||
|
margin-right: 2px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.data { |
||||
|
left: 40px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@include media-phablet { |
||||
|
.results { |
||||
|
.media_result { |
||||
|
height: $input_height_mobile; |
||||
|
|
||||
|
.options { |
||||
|
|
||||
|
.title { |
||||
|
|
||||
|
width: 140px; |
||||
|
margin-right: 2px; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.big_search { |
||||
|
background: $theme_off; |
||||
|
} |
@ -0,0 +1,154 @@ |
|||||
|
var MovieDetails = new Class({ |
||||
|
|
||||
|
Extends: BlockBase, |
||||
|
|
||||
|
sections: null, |
||||
|
buttons: null, |
||||
|
|
||||
|
initialize: function(parent, options){ |
||||
|
var self = this; |
||||
|
|
||||
|
self.sections = {}; |
||||
|
|
||||
|
var category = parent.get('category'), |
||||
|
profile = parent.profile; |
||||
|
|
||||
|
self.el = new Element('div',{ |
||||
|
'class': 'page active movie_details level_' + (options.level || 0) |
||||
|
}).adopt( |
||||
|
self.overlay = new Element('div.overlay', { |
||||
|
'events': { |
||||
|
'click': self.close.bind(self) |
||||
|
} |
||||
|
}).grab( |
||||
|
new Element('a.close.icon-left-arrow') |
||||
|
), |
||||
|
self.content = new Element('div.scroll_content').grab( |
||||
|
new Element('div.head').adopt( |
||||
|
new Element('h1').grab( |
||||
|
self.title_dropdown = new BlockMenu(self, { |
||||
|
'class': 'title', |
||||
|
'button_text': parent.getTitle() + (parent.get('year') ? ' (' + parent.get('year') + ')' : ''), |
||||
|
'button_class': 'icon-dropdown' |
||||
|
}) |
||||
|
), |
||||
|
self.buttons = new Element('div.buttons') |
||||
|
) |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
self.addSection('description', new Element('div', { |
||||
|
'text': parent.get('plot') |
||||
|
})); |
||||
|
|
||||
|
|
||||
|
// Title dropdown
|
||||
|
var titles = parent.get('info').titles; |
||||
|
$(self.title_dropdown).addEvents({ |
||||
|
'click:relay(li a)': function(e, el){ |
||||
|
(e).stopPropagation(); |
||||
|
|
||||
|
// Update category
|
||||
|
Api.request('movie.edit', { |
||||
|
'data': { |
||||
|
'id': parent.get('_id'), |
||||
|
'default_title': el.get('text') |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
$(self.title_dropdown).getElements('.icon-ok').removeClass('icon-ok'); |
||||
|
el.addClass('icon-ok'); |
||||
|
|
||||
|
self.title_dropdown.button.set('text', el.get('text') + (parent.get('year') ? ' (' + parent.get('year') + ')' : '')); |
||||
|
|
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
titles.each(function(t){ |
||||
|
self.title_dropdown.addLink(new Element('a', { |
||||
|
'text': t, |
||||
|
'class': parent.get('title') == t ? 'icon-ok' : '' |
||||
|
})); |
||||
|
}); |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
addSection: function(name, section_el){ |
||||
|
var self = this; |
||||
|
name = name.toLowerCase(); |
||||
|
|
||||
|
self.content.grab( |
||||
|
self.sections[name] = new Element('div', { |
||||
|
'class': 'section section_' + name |
||||
|
}).grab(section_el) |
||||
|
); |
||||
|
}, |
||||
|
|
||||
|
addButton: function(button){ |
||||
|
var self = this; |
||||
|
|
||||
|
self.buttons.grab(button); |
||||
|
}, |
||||
|
|
||||
|
open: function(){ |
||||
|
var self = this; |
||||
|
|
||||
|
self.el.addClass('show'); |
||||
|
|
||||
|
if(!App.mobile_screen){ |
||||
|
$(self.content).getElements('> .head, > .section').each(function(section, nr){ |
||||
|
dynamics.css(section, { |
||||
|
opacity: 0, |
||||
|
translateY: 100 |
||||
|
}); |
||||
|
|
||||
|
dynamics.animate(section, { |
||||
|
opacity: 1, |
||||
|
translateY: 0 |
||||
|
}, { |
||||
|
type: dynamics.spring, |
||||
|
frequency: 200, |
||||
|
friction: 300, |
||||
|
duration: 1200, |
||||
|
delay: 500 + (nr * 100) |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
close: function(){ |
||||
|
var self = this; |
||||
|
|
||||
|
var ended = function() { |
||||
|
self.el.dispose(); |
||||
|
self.overlay.removeEventListener('transitionend', ended); |
||||
|
}; |
||||
|
self.overlay.addEventListener('transitionend', ended, false); |
||||
|
|
||||
|
// animate out
|
||||
|
|
||||
|
if(!App.mobile_screen){ |
||||
|
$(self.content).getElements('> .head, > .section').reverse().each(function(section, nr){ |
||||
|
dynamics.animate(section, { |
||||
|
opacity: 0, |
||||
|
translateY: 100 |
||||
|
}, { |
||||
|
type: dynamics.spring, |
||||
|
frequency: 200, |
||||
|
friction: 300, |
||||
|
duration: 1200, |
||||
|
delay: (nr * 50) |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
dynamics.setTimeout(function(){ |
||||
|
self.el.removeClass('show'); |
||||
|
}, 200); |
||||
|
} |
||||
|
else { |
||||
|
self.el.removeClass('show'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
}); |
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,50 @@ |
|||||
|
Page.Movies = new Class({ |
||||
|
|
||||
|
Extends: PageBase, |
||||
|
|
||||
|
name: 'movies', |
||||
|
icon: 'movie', |
||||
|
sub_pages: ['Wanted', 'Manage'], |
||||
|
default_page: 'Wanted', |
||||
|
current_page: null, |
||||
|
|
||||
|
initialize: function(parent, options){ |
||||
|
var self = this; |
||||
|
self.parent(parent, options); |
||||
|
|
||||
|
self.navigation = new BlockNavigation(); |
||||
|
$(self.navigation).inject(self.content, 'top'); |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
defaultAction: function(action, params){ |
||||
|
var self = this; |
||||
|
|
||||
|
if(self.current_page){ |
||||
|
self.current_page.hide(); |
||||
|
|
||||
|
if(self.current_page.list && self.current_page.list.navigation) |
||||
|
self.current_page.list.navigation.dispose(); |
||||
|
} |
||||
|
|
||||
|
var route = new Route(); |
||||
|
route.parse(action); |
||||
|
|
||||
|
var page_name = route.getPage() != 'index' ? route.getPage().capitalize() : self.default_page; |
||||
|
|
||||
|
var page = self.sub_pages.filter(function(page){ |
||||
|
return page.name == page_name; |
||||
|
}).pick()['class']; |
||||
|
|
||||
|
page.open(route.getAction() || 'index', params); |
||||
|
page.show(); |
||||
|
|
||||
|
if(page.list && page.list.navigation) |
||||
|
page.list.navigation.inject(self.navigation); |
||||
|
|
||||
|
self.current_page = page; |
||||
|
self.navigation.activate(page_name.toLowerCase()); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
}); |
@ -0,0 +1,104 @@ |
|||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media.movie.providers.automation.base import Automation |
||||
|
|
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
autoload = 'Hummingbird' |
||||
|
|
||||
|
|
||||
|
class Hummingbird(Automation): |
||||
|
|
||||
|
def getIMDBids(self): |
||||
|
movies = [] |
||||
|
for movie in self.getWatchlist(): |
||||
|
imdb = self.search(movie[0], movie[1]) |
||||
|
if imdb: |
||||
|
movies.append(imdb['imdb']) |
||||
|
return movies |
||||
|
|
||||
|
def getWatchlist(self): |
||||
|
if not self.conf('automation_username'): |
||||
|
log.error('You need to fill in a username') |
||||
|
return [] |
||||
|
|
||||
|
url = "http://hummingbird.me/api/v1/users/%s/library" % self.conf('automation_username') |
||||
|
data = self.getJsonData(url) |
||||
|
|
||||
|
chosen_filter = { |
||||
|
'automation_list_current': 'currently-watching', |
||||
|
'automation_list_plan': 'plan-to-watch', |
||||
|
'automation_list_completed': 'completed', |
||||
|
'automation_list_hold': 'on-hold', |
||||
|
'automation_list_dropped': 'dropped', |
||||
|
} |
||||
|
|
||||
|
chosen_lists = [] |
||||
|
for x in chosen_filter: |
||||
|
if self.conf(x): |
||||
|
chosen_lists.append(chosen_filter[x]) |
||||
|
|
||||
|
entries = [] |
||||
|
for item in data: |
||||
|
if item['anime']['show_type'] != 'Movie' or item['status'] not in chosen_lists: |
||||
|
continue |
||||
|
title = item['anime']['title'] |
||||
|
year = item['anime']['started_airing'] |
||||
|
if year: |
||||
|
year = year[:4] |
||||
|
entries.append([title, year]) |
||||
|
return entries |
||||
|
|
||||
|
config = [{ |
||||
|
'name': 'hummingbird', |
||||
|
'groups': [ |
||||
|
{ |
||||
|
'tab': 'automation', |
||||
|
'list': 'watchlist_providers', |
||||
|
'name': 'hummingbird_automation', |
||||
|
'label': 'Hummingbird', |
||||
|
'description': 'Import movies from your Hummingbird.me lists', |
||||
|
'options': [ |
||||
|
{ |
||||
|
'name': 'automation_enabled', |
||||
|
'default': False, |
||||
|
'type': 'enabler', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'automation_username', |
||||
|
'label': 'Username', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'automation_list_current', |
||||
|
'type': 'bool', |
||||
|
'label': 'Currently Watching', |
||||
|
'default': False, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'automation_list_plan', |
||||
|
'type': 'bool', |
||||
|
'label': 'Plan to Watch', |
||||
|
'default': True, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'automation_list_completed', |
||||
|
'type': 'bool', |
||||
|
'label': 'Completed', |
||||
|
'default': False, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'automation_list_hold', |
||||
|
'type': 'bool', |
||||
|
'label': 'On Hold', |
||||
|
'default': False, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'automation_list_dropped', |
||||
|
'type': 'bool', |
||||
|
'label': 'Dropped', |
||||
|
'default': False, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}] |
@ -1,95 +0,0 @@ |
|||||
from xml.etree.ElementTree import QName |
|
||||
import datetime |
|
||||
import re |
|
||||
|
|
||||
from couchpotato.core.helpers.rss import RSS |
|
||||
from couchpotato.core.helpers.variable import tryInt, splitString |
|
||||
from couchpotato.core.logger import CPLog |
|
||||
from couchpotato.core.media.movie.providers.automation.base import Automation |
|
||||
|
|
||||
|
|
||||
log = CPLog(__name__) |
|
||||
|
|
||||
autoload = 'Rottentomatoes' |
|
||||
|
|
||||
|
|
||||
class Rottentomatoes(Automation, RSS): |
|
||||
|
|
||||
interval = 1800 |
|
||||
|
|
||||
def getIMDBids(self): |
|
||||
|
|
||||
movies = [] |
|
||||
|
|
||||
rotten_tomatoes_namespace = 'http://www.rottentomatoes.com/xmlns/rtmovie/' |
|
||||
urls = dict(zip(splitString(self.conf('automation_urls')), [tryInt(x) for x in splitString(self.conf('automation_urls_use'))])) |
|
||||
|
|
||||
for url in urls: |
|
||||
|
|
||||
if not urls[url]: |
|
||||
continue |
|
||||
|
|
||||
rss_movies = self.getRSSData(url) |
|
||||
rating_tag = str(QName(rotten_tomatoes_namespace, 'tomatometer_percent')) |
|
||||
|
|
||||
for movie in rss_movies: |
|
||||
|
|
||||
value = self.getTextElement(movie, "title") |
|
||||
result = re.search('(?<=%\s).*', value) |
|
||||
|
|
||||
if result: |
|
||||
|
|
||||
rating = tryInt(self.getTextElement(movie, rating_tag)) |
|
||||
name = result.group(0) |
|
||||
|
|
||||
print rating, tryInt(self.conf('tomatometer_percent')) |
|
||||
if rating < tryInt(self.conf('tomatometer_percent')): |
|
||||
log.info2('%s seems to be rotten...', name) |
|
||||
else: |
|
||||
log.info2('Found %s with fresh rating %s', (name, rating)) |
|
||||
year = datetime.datetime.now().strftime("%Y") |
|
||||
imdb = self.search(name, year) |
|
||||
|
|
||||
if imdb and self.isMinimalMovie(imdb): |
|
||||
movies.append(imdb['imdb']) |
|
||||
|
|
||||
return movies |
|
||||
|
|
||||
|
|
||||
config = [{ |
|
||||
'name': 'rottentomatoes', |
|
||||
'groups': [ |
|
||||
{ |
|
||||
'tab': 'automation', |
|
||||
'list': 'automation_providers', |
|
||||
'name': 'rottentomatoes_automation', |
|
||||
'label': 'Rottentomatoes', |
|
||||
'description': 'Imports movies from rottentomatoes rss feeds specified below.', |
|
||||
'options': [ |
|
||||
{ |
|
||||
'name': 'automation_enabled', |
|
||||
'default': False, |
|
||||
'type': 'enabler', |
|
||||
}, |
|
||||
{ |
|
||||
'name': 'automation_urls_use', |
|
||||
'label': 'Use', |
|
||||
'default': '1', |
|
||||
}, |
|
||||
{ |
|
||||
'name': 'automation_urls', |
|
||||
'label': 'url', |
|
||||
'type': 'combined', |
|
||||
'combine': ['automation_urls_use', 'automation_urls'], |
|
||||
'default': 'http://www.rottentomatoes.com/syndication/rss/in_theaters.xml', |
|
||||
}, |
|
||||
{ |
|
||||
'name': 'tomatometer_percent', |
|
||||
'default': '80', |
|
||||
'label': 'Tomatometer', |
|
||||
'description': 'Use as extra scoring requirement', |
|
||||
}, |
|
||||
], |
|
||||
}, |
|
||||
], |
|
||||
}] |
|
@ -1,83 +0,0 @@ |
|||||
import base64 |
|
||||
|
|
||||
from couchpotato.core.event import addEvent |
|
||||
from couchpotato.core.helpers.variable import sha1 |
|
||||
from couchpotato.core.logger import CPLog |
|
||||
from couchpotato.core.media.movie.providers.automation.base import Automation |
|
||||
|
|
||||
|
|
||||
log = CPLog(__name__) |
|
||||
|
|
||||
autoload = 'Trakt' |
|
||||
|
|
||||
|
|
||||
class Trakt(Automation): |
|
||||
|
|
||||
urls = { |
|
||||
'base': 'http://api.trakt.tv/', |
|
||||
'watchlist': 'user/watchlist/movies.json/%s/', |
|
||||
} |
|
||||
|
|
||||
def __init__(self): |
|
||||
super(Trakt, self).__init__() |
|
||||
|
|
||||
addEvent('setting.save.trakt.automation_password', self.sha1Password) |
|
||||
|
|
||||
def sha1Password(self, value): |
|
||||
return sha1(value) if value else '' |
|
||||
|
|
||||
def getIMDBids(self): |
|
||||
|
|
||||
movies = [] |
|
||||
for movie in self.getWatchlist(): |
|
||||
movies.append(movie.get('imdb_id')) |
|
||||
|
|
||||
return movies |
|
||||
|
|
||||
def getWatchlist(self): |
|
||||
method = (self.urls['watchlist'] % self.conf('automation_api_key')) + self.conf('automation_username') |
|
||||
return self.call(method) |
|
||||
|
|
||||
def call(self, method_url): |
|
||||
|
|
||||
headers = {} |
|
||||
if self.conf('automation_password'): |
|
||||
headers['Authorization'] = 'Basic %s' % base64.encodestring('%s:%s' % (self.conf('automation_username'), self.conf('automation_password')))[:-1] |
|
||||
|
|
||||
data = self.getJsonData(self.urls['base'] + method_url, headers = headers) |
|
||||
return data if data else [] |
|
||||
|
|
||||
|
|
||||
config = [{ |
|
||||
'name': 'trakt', |
|
||||
'groups': [ |
|
||||
{ |
|
||||
'tab': 'automation', |
|
||||
'list': 'watchlist_providers', |
|
||||
'name': 'trakt_automation', |
|
||||
'label': 'Trakt', |
|
||||
'description': 'import movies from your own watchlist', |
|
||||
'options': [ |
|
||||
{ |
|
||||
'name': 'automation_enabled', |
|
||||
'default': False, |
|
||||
'type': 'enabler', |
|
||||
}, |
|
||||
{ |
|
||||
'name': 'automation_api_key', |
|
||||
'label': 'Apikey', |
|
||||
}, |
|
||||
{ |
|
||||
'name': 'automation_username', |
|
||||
'label': 'Username', |
|
||||
}, |
|
||||
{ |
|
||||
'name': 'automation_password', |
|
||||
'label': 'Password', |
|
||||
'type': 'password', |
|
||||
'description': 'When you have "Protect my data" checked <a href="http://trakt.tv/settings/account">on trakt</a>.', |
|
||||
}, |
|
||||
], |
|
||||
}, |
|
||||
], |
|
||||
}] |
|
@ -0,0 +1,31 @@ |
|||||
|
from .main import Trakt |
||||
|
|
||||
|
|
||||
|
def autoload(): |
||||
|
return Trakt() |
||||
|
|
||||
|
|
||||
|
config = [{ |
||||
|
'name': 'trakt', |
||||
|
'groups': [ |
||||
|
{ |
||||
|
'tab': 'automation', |
||||
|
'list': 'watchlist_providers', |
||||
|
'name': 'trakt_automation', |
||||
|
'label': 'Trakt', |
||||
|
'description': 'Import movies from your own watchlist', |
||||
|
'options': [ |
||||
|
{ |
||||
|
'name': 'automation_enabled', |
||||
|
'default': False, |
||||
|
'type': 'enabler', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'automation_oauth_token', |
||||
|
'label': 'Auth Token', |
||||
|
'advanced': 1 |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}] |
@ -0,0 +1,76 @@ |
|||||
|
import json |
||||
|
|
||||
|
from couchpotato import Env |
||||
|
from couchpotato.api import addApiView |
||||
|
from couchpotato.core.helpers.variable import cleanHost |
||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media._base.providers.base import Provider |
||||
|
from couchpotato.core.media.movie.providers.automation.base import Automation |
||||
|
|
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
|
||||
|
class TraktBase(Provider): |
||||
|
|
||||
|
client_id = '8a54ed7b5e1b56d874642770ad2e8b73e2d09d6e993c3a92b1e89690bb1c9014' |
||||
|
api_url = 'https://api-v2launch.trakt.tv/' |
||||
|
|
||||
|
def call(self, method_url, post_data = None): |
||||
|
headers = { |
||||
|
'Content-Type': 'application/json', |
||||
|
'Authorization': 'Bearer %s' % self.conf('automation_oauth_token'), |
||||
|
'trakt-api-version': 2, |
||||
|
'trakt-api-key': self.client_id, |
||||
|
} |
||||
|
|
||||
|
if post_data: |
||||
|
post_data = json.dumps(post_data) |
||||
|
|
||||
|
data = self.getJsonData(self.api_url + method_url, data = post_data or {}, headers = headers) |
||||
|
return data if data else [] |
||||
|
|
||||
|
|
||||
|
class Trakt(Automation, TraktBase): |
||||
|
|
||||
|
urls = { |
||||
|
'watchlist': 'sync/watchlist/movies/', |
||||
|
'oauth': 'https://api.couchpota.to/authorize/trakt/', |
||||
|
} |
||||
|
|
||||
|
def __init__(self): |
||||
|
addApiView('automation.trakt.auth_url', self.getAuthorizationUrl) |
||||
|
addApiView('automation.trakt.credentials', self.getCredentials) |
||||
|
|
||||
|
super(Trakt, self).__init__() |
||||
|
|
||||
|
def getIMDBids(self): |
||||
|
movies = [] |
||||
|
for movie in self.getWatchlist(): |
||||
|
movies.append(movie.get('movie').get('ids').get('imdb')) |
||||
|
|
||||
|
return movies |
||||
|
|
||||
|
def getWatchlist(self): |
||||
|
return self.call(self.urls['watchlist']) |
||||
|
|
||||
|
def getAuthorizationUrl(self, host = None, **kwargs): |
||||
|
callback_url = cleanHost(host) + '%sautomation.trakt.credentials/' % (Env.get('api_base').lstrip('/')) |
||||
|
log.debug('callback_url is %s', callback_url) |
||||
|
|
||||
|
target_url = self.urls['oauth'] + "?target=" + callback_url |
||||
|
log.debug('target_url is %s', target_url) |
||||
|
|
||||
|
return { |
||||
|
'success': True, |
||||
|
'url': target_url, |
||||
|
} |
||||
|
|
||||
|
def getCredentials(self, **kwargs): |
||||
|
try: |
||||
|
oauth_token = kwargs.get('oauth') |
||||
|
except: |
||||
|
return 'redirect', Env.get('web_base') + 'settings/automation/' |
||||
|
log.debug('oauth_token is: %s', oauth_token) |
||||
|
self.conf('automation_oauth_token', value = oauth_token) |
||||
|
return 'redirect', Env.get('web_base') + 'settings/automation/' |
@ -0,0 +1,67 @@ |
|||||
|
var TraktAutomation = new Class({ |
||||
|
|
||||
|
initialize: function(){ |
||||
|
var self = this; |
||||
|
|
||||
|
App.addEvent('loadSettings', self.addRegisterButton.bind(self)); |
||||
|
}, |
||||
|
|
||||
|
addRegisterButton: function(){ |
||||
|
var self = this, |
||||
|
setting_page = App.getPage('Settings'); |
||||
|
|
||||
|
setting_page.addEvent('create', function(){ |
||||
|
|
||||
|
var fieldset = setting_page.tabs.automation.groups.trakt_automation, |
||||
|
l = window.location; |
||||
|
|
||||
|
var trakt_set = 0; |
||||
|
fieldset.getElements('input[type=text]').each(function(el){ |
||||
|
trakt_set += +(el.get('value') !== ''); |
||||
|
}); |
||||
|
|
||||
|
new Element('.ctrlHolder').adopt( |
||||
|
|
||||
|
// Unregister button
|
||||
|
(trakt_set > 0) ? |
||||
|
[ |
||||
|
self.unregister = new Element('a.button.red', { |
||||
|
'text': 'Unregister', |
||||
|
'events': { |
||||
|
'click': function(){ |
||||
|
fieldset.getElements('input[name*=oauth_token]').set('value', '').fireEvent('change'); |
||||
|
|
||||
|
self.unregister.destroy(); |
||||
|
self.unregister_or.destroy(); |
||||
|
} |
||||
|
} |
||||
|
}), |
||||
|
self.unregister_or = new Element('span[text=or]') |
||||
|
] |
||||
|
: null, |
||||
|
|
||||
|
// Register button
|
||||
|
new Element('a.button', { |
||||
|
'text': trakt_set > 0 ? 'Register a different account' : 'Register your trakt.tv account', |
||||
|
'events': { |
||||
|
'click': function(){ |
||||
|
Api.request('automation.trakt.auth_url', { |
||||
|
'data': { |
||||
|
'host': l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') |
||||
|
}, |
||||
|
'onComplete': function(json){ |
||||
|
window.location = json.url; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
).inject(fieldset); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
new TraktAutomation(); |
@ -0,0 +1,221 @@ |
|||||
|
from xml.etree.ElementTree import Element, SubElement, tostring |
||||
|
import os |
||||
|
import re |
||||
|
import traceback |
||||
|
import xml.dom.minidom |
||||
|
|
||||
|
from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData |
||||
|
from couchpotato.core.helpers.encoding import toUnicode |
||||
|
from couchpotato.core.helpers.variable import getTitle |
||||
|
from couchpotato.core.logger import CPLog |
||||
|
|
||||
|
autoload = 'WdtvLive' |
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
|
||||
|
class WdtvLive(MovieMetaData): |
||||
|
|
||||
|
def getThumbnailName(self, name, root, i): |
||||
|
return self.createMetaName('%s.jpg', name, root) |
||||
|
|
||||
|
def createMetaName(self, basename, name, root): |
||||
|
return os.path.join(root, basename.replace('%s', name)) |
||||
|
|
||||
|
def getNfoName(self, name, root, i): |
||||
|
return self.createMetaName('%s.xml', name, root) |
||||
|
|
||||
|
def getNfo(self, movie_info=None, data=None, i=0): |
||||
|
if not data: data = {} |
||||
|
if not movie_info: movie_info = {} |
||||
|
|
||||
|
nfoxml = Element('details') |
||||
|
|
||||
|
# Title |
||||
|
try: |
||||
|
el = SubElement(nfoxml, 'title') |
||||
|
el.text = toUnicode(getTitle(data)) |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
# IMDB id |
||||
|
try: |
||||
|
el = SubElement(nfoxml, 'id') |
||||
|
el.text = toUnicode(data['identifier']) |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
# Runtime |
||||
|
try: |
||||
|
runtime = SubElement(nfoxml, 'runtime') |
||||
|
runtime.text = '%s min' % movie_info.get('runtime') |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
# Other values |
||||
|
types = ['year', 'mpaa', 'originaltitle:original_title', 'outline', 'plot', 'tagline', 'premiered:released'] |
||||
|
for type in types: |
||||
|
|
||||
|
if ':' in type: |
||||
|
name, type = type.split(':') |
||||
|
else: |
||||
|
name = type |
||||
|
|
||||
|
try: |
||||
|
if movie_info.get(type): |
||||
|
el = SubElement(nfoxml, name) |
||||
|
el.text = toUnicode(movie_info.get(type, '')) |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
# Rating |
||||
|
for rating_type in ['imdb', 'rotten', 'tmdb']: |
||||
|
try: |
||||
|
r, v = movie_info['rating'][rating_type] |
||||
|
rating = SubElement(nfoxml, 'rating') |
||||
|
rating.text = str(r) |
||||
|
votes = SubElement(nfoxml, 'votes') |
||||
|
votes.text = str(v) |
||||
|
break |
||||
|
except: |
||||
|
log.debug('Failed adding rating info from %s: %s', (rating_type, traceback.format_exc())) |
||||
|
|
||||
|
# Genre |
||||
|
for genre in movie_info.get('genres', []): |
||||
|
genres = SubElement(nfoxml, 'genre') |
||||
|
genres.text = toUnicode(genre) |
||||
|
|
||||
|
# Actors |
||||
|
for actor_name in movie_info.get('actor_roles', {}): |
||||
|
role_name = movie_info['actor_roles'][actor_name] |
||||
|
|
||||
|
actor = SubElement(nfoxml, 'actor') |
||||
|
name = SubElement(actor, 'name') |
||||
|
name.text = toUnicode(actor_name) |
||||
|
if role_name: |
||||
|
role = SubElement(actor, 'role') |
||||
|
role.text = toUnicode(role_name) |
||||
|
if movie_info['images']['actors'].get(actor_name): |
||||
|
thumb = SubElement(actor, 'thumb') |
||||
|
thumb.text = toUnicode(movie_info['images']['actors'].get(actor_name)) |
||||
|
|
||||
|
# Directors |
||||
|
for director_name in movie_info.get('directors', []): |
||||
|
director = SubElement(nfoxml, 'director') |
||||
|
director.text = toUnicode(director_name) |
||||
|
|
||||
|
# Writers |
||||
|
for writer in movie_info.get('writers', []): |
||||
|
writers = SubElement(nfoxml, 'credits') |
||||
|
writers.text = toUnicode(writer) |
||||
|
|
||||
|
# Sets or collections |
||||
|
collection_name = movie_info.get('collection') |
||||
|
if collection_name: |
||||
|
collection = SubElement(nfoxml, 'set') |
||||
|
collection.text = toUnicode(collection_name) |
||||
|
sorttitle = SubElement(nfoxml, 'sorttitle') |
||||
|
sorttitle.text = '%s %s' % (toUnicode(collection_name), movie_info.get('year')) |
||||
|
|
||||
|
# Images |
||||
|
for image_url in movie_info['images']['poster_original']: |
||||
|
image = SubElement(nfoxml, 'thumb') |
||||
|
image.text = toUnicode(image_url) |
||||
|
|
||||
|
image_types = [ |
||||
|
('fanart', 'backdrop_original'), |
||||
|
('banner', 'banner'), |
||||
|
('discart', 'disc_art'), |
||||
|
('logo', 'logo'), |
||||
|
('clearart', 'clear_art'), |
||||
|
('landscape', 'landscape'), |
||||
|
('extrathumb', 'extra_thumbs'), |
||||
|
('extrafanart', 'extra_fanart'), |
||||
|
] |
||||
|
|
||||
|
for image_type in image_types: |
||||
|
sub, type = image_type |
||||
|
|
||||
|
sub_element = SubElement(nfoxml, sub) |
||||
|
for image_url in movie_info['images'][type]: |
||||
|
image = SubElement(sub_element, 'thumb') |
||||
|
image.text = toUnicode(image_url) |
||||
|
|
||||
|
# Add trailer if found |
||||
|
trailer_found = False |
||||
|
if data.get('renamed_files'): |
||||
|
for filename in data.get('renamed_files'): |
||||
|
if 'trailer' in filename: |
||||
|
trailer = SubElement(nfoxml, 'trailer') |
||||
|
trailer.text = toUnicode(filename) |
||||
|
trailer_found = True |
||||
|
if not trailer_found and data['files'].get('trailer'): |
||||
|
trailer = SubElement(nfoxml, 'trailer') |
||||
|
trailer.text = toUnicode(data['files']['trailer'][0]) |
||||
|
|
||||
|
# Add file metadata |
||||
|
fileinfo = SubElement(nfoxml, 'fileinfo') |
||||
|
streamdetails = SubElement(fileinfo, 'streamdetails') |
||||
|
|
||||
|
# Video data |
||||
|
if data['meta_data'].get('video'): |
||||
|
video = SubElement(streamdetails, 'video') |
||||
|
codec = SubElement(video, 'codec') |
||||
|
codec.text = toUnicode(data['meta_data']['video']) |
||||
|
aspect = SubElement(video, 'aspect') |
||||
|
aspect.text = str(data['meta_data']['aspect']) |
||||
|
width = SubElement(video, 'width') |
||||
|
width.text = str(data['meta_data']['resolution_width']) |
||||
|
height = SubElement(video, 'height') |
||||
|
height.text = str(data['meta_data']['resolution_height']) |
||||
|
|
||||
|
# Audio data |
||||
|
if data['meta_data'].get('audio'): |
||||
|
audio = SubElement(streamdetails, 'audio') |
||||
|
codec = SubElement(audio, 'codec') |
||||
|
codec.text = toUnicode(data['meta_data'].get('audio')) |
||||
|
channels = SubElement(audio, 'channels') |
||||
|
channels.text = toUnicode(data['meta_data'].get('audio_channels')) |
||||
|
|
||||
|
# Clean up the xml and return it |
||||
|
nfoxml = xml.dom.minidom.parseString(tostring(nfoxml)) |
||||
|
xml_string = nfoxml.toprettyxml(indent = ' ') |
||||
|
text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL) |
||||
|
xml_string = text_re.sub('>\g<1></', xml_string) |
||||
|
|
||||
|
return xml_string.encode('utf-8') |
||||
|
|
||||
|
|
||||
|
config = [{ |
||||
|
'name': 'wdtvlive', |
||||
|
'groups': [ |
||||
|
{ |
||||
|
'tab': 'renamer', |
||||
|
'subtab': 'metadata', |
||||
|
'name': 'wdtvlive_metadata', |
||||
|
'label': 'WDTV Live', |
||||
|
'description': 'Metadata for WDTV', |
||||
|
'options': [ |
||||
|
{ |
||||
|
'name': 'meta_enabled', |
||||
|
'default': False, |
||||
|
'type': 'enabler', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'meta_nfo', |
||||
|
'label': 'NFO', |
||||
|
'default': True, |
||||
|
'type': 'bool', |
||||
|
'description': 'Generate metadata xml', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'meta_thumbnail', |
||||
|
'label': 'Thumbnail', |
||||
|
'default': True, |
||||
|
'type': 'bool', |
||||
|
'description': 'Generate thumbnail jpg', |
||||
|
} |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}] |
@ -0,0 +1,32 @@ |
|||||
|
from couchpotato.core.event import fireEvent |
||||
|
from couchpotato.core.helpers.encoding import tryUrlencode |
||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media._base.providers.torrent.alpharatio import Base |
||||
|
from couchpotato.core.media.movie.providers.base import MovieProvider |
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
autoload = 'AlphaRatio' |
||||
|
|
||||
|
|
||||
|
class AlphaRatio(MovieProvider, Base): |
||||
|
|
||||
|
# AlphaRatio movie search categories |
||||
|
# 7: MoviesHD |
||||
|
# 9: MoviePackHD |
||||
|
# 6: MoviesSD |
||||
|
# 8: MovePackSD |
||||
|
|
||||
|
cat_ids = [ |
||||
|
([7, 9], ['bd50']), |
||||
|
([7, 9], ['720p', '1080p']), |
||||
|
([6, 8], ['dvdr']), |
||||
|
([6, 8], ['brrip', 'dvdrip']), |
||||
|
] |
||||
|
cat_backup_id = 6 |
||||
|
|
||||
|
def buildUrl(self, media, quality): |
||||
|
query = (tryUrlencode(fireEvent('library.query', media, single = True)), |
||||
|
self.getSceneOnly(), |
||||
|
self.getCatId(quality)[0]) |
||||
|
return query |
@ -0,0 +1,11 @@ |
|||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media._base.providers.torrent.hd4free import Base |
||||
|
from couchpotato.core.media.movie.providers.base import MovieProvider |
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
autoload = 'HD4Free' |
||||
|
|
||||
|
|
||||
|
class HD4Free(MovieProvider, Base): |
||||
|
pass |
@ -0,0 +1,11 @@ |
|||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media._base.providers.torrent.rarbg import Base |
||||
|
from couchpotato.core.media.movie.providers.base import MovieProvider |
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
autoload = 'Rarbg' |
||||
|
|
||||
|
|
||||
|
class Rarbg(MovieProvider, Base): |
||||
|
pass |
@ -0,0 +1,11 @@ |
|||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.media._base.providers.torrent.scenetime import Base |
||||
|
from couchpotato.core.media.movie.providers.base import MovieProvider |
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
autoload = 'SceneTime' |
||||
|
|
||||
|
|
||||
|
class SceneTime(MovieProvider, Base): |
||||
|
pass |
@ -1,8 +0,0 @@ |
|||||
from couchpotato.core.media._base.providers.userscript.base import UserscriptBase |
|
||||
|
|
||||
autoload = 'ShareThe' |
|
||||
|
|
||||
|
|
||||
class ShareThe(UserscriptBase): |
|
||||
|
|
||||
includes = ['http://*.sharethe.tv/movies/*', 'http://sharethe.tv/movies/*'] |
|
@ -1,8 +0,0 @@ |
|||||
from couchpotato.core.media._base.providers.userscript.base import UserscriptBase |
|
||||
|
|
||||
autoload = 'WHiWA' |
|
||||
|
|
||||
|
|
||||
class WHiWA(UserscriptBase): |
|
||||
|
|
||||
includes = ['http://whiwa.net/stats/movie/*'] |
|
@ -1,162 +0,0 @@ |
|||||
.suggestions { |
|
||||
clear: both; |
|
||||
padding-top: 10px; |
|
||||
margin-bottom: 30px; |
|
||||
} |
|
||||
|
|
||||
.suggestions > h2 { |
|
||||
height: 40px; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result { |
|
||||
display: inline-block; |
|
||||
width: 33.333%; |
|
||||
height: 150px; |
|
||||
} |
|
||||
|
|
||||
@media all and (max-width: 960px) { |
|
||||
.suggestions .media_result { |
|
||||
width: 50%; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media all and (max-width: 600px) { |
|
||||
.suggestions .media_result { |
|
||||
width: 100%; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .data { |
|
||||
left: 100px; |
|
||||
background: #4e5969; |
|
||||
border: none; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .data .info { |
|
||||
top: 10px; |
|
||||
left: 15px; |
|
||||
right: 15px; |
|
||||
bottom: 10px; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .data .info h2 { |
|
||||
white-space: normal; |
|
||||
max-height: 120px; |
|
||||
font-size: 18px; |
|
||||
line-height: 18px; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .data .info .rating, |
|
||||
.suggestions .media_result .data .info .genres, |
|
||||
.suggestions .media_result .data .info .year { |
|
||||
position: static; |
|
||||
display: block; |
|
||||
padding: 0; |
|
||||
opacity: .6; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .data .info .year { |
|
||||
margin: 10px 0 0; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .data .info .rating { |
|
||||
font-size: 20px; |
|
||||
float: right; |
|
||||
margin-top: -20px; |
|
||||
} |
|
||||
.suggestions .media_result .data .info .rating:before { |
|
||||
content: "\e031"; |
|
||||
font-family: 'Elusive-Icons'; |
|
||||
font-size: 14px; |
|
||||
margin: 0 5px 0 0; |
|
||||
vertical-align: bottom; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .data .info .genres { |
|
||||
font-size: 11px; |
|
||||
font-style: italic; |
|
||||
text-align: right; |
|
||||
} |
|
||||
|
|
||||
.suggestions .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; |
|
||||
} |
|
||||
.suggestions .media_result .data:before { |
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .data .info .plot.full { |
|
||||
top: 0; |
|
||||
overflow: auto; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .data { |
|
||||
cursor: default; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .options { |
|
||||
left: 100px; |
|
||||
} |
|
||||
.suggestions .media_result .options select[name=title] { width: 100%; } |
|
||||
.suggestions .media_result .options select[name=profile] { width: 100%; } |
|
||||
.suggestions .media_result .options select[name=category] { width: 100%; } |
|
||||
|
|
||||
.suggestions .media_result .button { |
|
||||
position: absolute; |
|
||||
margin: 2px 0 0 0; |
|
||||
right: 15px; |
|
||||
bottom: 15px; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
.suggestions .media_result .thumbnail { |
|
||||
width: 100px; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .actions { |
|
||||
position: absolute; |
|
||||
top: 10px; |
|
||||
right: 10px; |
|
||||
display: none; |
|
||||
width: 140px; |
|
||||
} |
|
||||
.suggestions .media_result:hover .actions { |
|
||||
display: block; |
|
||||
} |
|
||||
.suggestions .media_result:hover h2 .title { |
|
||||
opacity: 0; |
|
||||
} |
|
||||
.suggestions .media_result .data.open .actions { |
|
||||
display: none; |
|
||||
} |
|
||||
|
|
||||
.suggestions .media_result .actions a { |
|
||||
margin-left: 10px; |
|
||||
vertical-align: middle; |
|
||||
} |
|
||||
|
|
@ -1,173 +0,0 @@ |
|||||
var SuggestList = new Class({ |
|
||||
|
|
||||
Implements: [Options, Events], |
|
||||
|
|
||||
shown_once: false, |
|
||||
|
|
||||
initialize: function(options){ |
|
||||
var self = this; |
|
||||
self.setOptions(options); |
|
||||
|
|
||||
self.create(); |
|
||||
}, |
|
||||
|
|
||||
create: function(){ |
|
||||
var self = this; |
|
||||
|
|
||||
self.el = new Element('div.suggestions', { |
|
||||
'events': { |
|
||||
'click:relay(a.delete)': function(e, el){ |
|
||||
(e).stop(); |
|
||||
|
|
||||
$(el).getParent('.media_result').destroy(); |
|
||||
|
|
||||
Api.request('suggestion.ignore', { |
|
||||
'data': { |
|
||||
'imdb': el.get('data-ignore') |
|
||||
}, |
|
||||
'onComplete': self.fill.bind(self) |
|
||||
}); |
|
||||
|
|
||||
}, |
|
||||
'click:relay(a.eye-open)': function(e, el){ |
|
||||
(e).stop(); |
|
||||
|
|
||||
$(el).getParent('.media_result').destroy(); |
|
||||
|
|
||||
Api.request('suggestion.ignore', { |
|
||||
'data': { |
|
||||
'imdb': el.get('data-seen'), |
|
||||
'mark_seen': 1 |
|
||||
}, |
|
||||
'onComplete': self.fill.bind(self) |
|
||||
}); |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
var cookie_menu_select = Cookie.read('suggestions_charts_menu_selected') || 'suggestions'; |
|
||||
if( cookie_menu_select === 'suggestions') |
|
||||
self.show(); |
|
||||
else |
|
||||
self.hide(); |
|
||||
|
|
||||
self.fireEvent.delay(0, self, 'created'); |
|
||||
|
|
||||
}, |
|
||||
|
|
||||
fill: function(json){ |
|
||||
|
|
||||
var self = this; |
|
||||
|
|
||||
if(!json || json.count == 0){ |
|
||||
self.el.hide(); |
|
||||
} |
|
||||
else { |
|
||||
|
|
||||
Object.each(json.suggestions, function(movie){ |
|
||||
|
|
||||
var m = new Block.Search.MovieItem(movie, { |
|
||||
'onAdded': function(){ |
|
||||
self.afterAdded(m, movie) |
|
||||
} |
|
||||
}); |
|
||||
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 |
|
||||
})), |
|
||||
new Element('a.delete.icon2', { |
|
||||
'title': 'Don\'t suggest this movie again', |
|
||||
'data-ignore': movie.imdb |
|
||||
}), |
|
||||
new Element('a.eye-open.icon2', { |
|
||||
'title': 'Seen it, like it, don\'t add', |
|
||||
'data-seen': movie.imdb |
|
||||
}) |
|
||||
) |
|
||||
); |
|
||||
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(self.el); |
|
||||
|
|
||||
}); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
self.fireEvent('loaded'); |
|
||||
|
|
||||
}, |
|
||||
|
|
||||
afterAdded: function(m, movie){ |
|
||||
var self = this; |
|
||||
|
|
||||
setTimeout(function(){ |
|
||||
$(m).destroy(); |
|
||||
|
|
||||
Api.request('suggestion.ignore', { |
|
||||
'data': { |
|
||||
'imdb': movie.imdb, |
|
||||
'remove_only': true |
|
||||
}, |
|
||||
'onComplete': self.fill.bind(self) |
|
||||
}); |
|
||||
|
|
||||
}, 3000); |
|
||||
|
|
||||
}, |
|
||||
|
|
||||
show: function(){ |
|
||||
var self = this; |
|
||||
|
|
||||
self.el.show(); |
|
||||
|
|
||||
if(!self.shown_once){ |
|
||||
self.api_request = Api.request('suggestion.view', { |
|
||||
'onComplete': self.fill.bind(self) |
|
||||
}); |
|
||||
|
|
||||
self.shown_once = true; |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
hide: function(){ |
|
||||
this.el.hide(); |
|
||||
}, |
|
||||
|
|
||||
toElement: function(){ |
|
||||
return this.el; |
|
||||
} |
|
||||
|
|
||||
}); |
|
@ -0,0 +1,89 @@ |
|||||
|
import json |
||||
|
import urllib, urllib2 |
||||
|
|
||||
|
from couchpotato.core.helpers.variable import cleanHost |
||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.notifications.base import Notification |
||||
|
|
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
|
||||
|
autoload = 'Emby' |
||||
|
|
||||
|
|
||||
|
class Emby(Notification): |
||||
|
|
||||
|
def notify(self, message = '', data = None, listener = None): |
||||
|
host = self.conf('host') |
||||
|
apikey = self.conf('apikey') |
||||
|
|
||||
|
host = cleanHost(host) |
||||
|
url = '%semby/Library/Series/Updated' % (host) |
||||
|
values = {} |
||||
|
data = urllib.urlencode(values) |
||||
|
|
||||
|
try: |
||||
|
req = urllib2.Request(url, data) |
||||
|
req.add_header('X-MediaBrowser-Token', apikey) |
||||
|
|
||||
|
response = urllib2.urlopen(req) |
||||
|
result = response.read() |
||||
|
response.close() |
||||
|
return True |
||||
|
|
||||
|
except (urllib2.URLError, IOError), e: |
||||
|
return False |
||||
|
|
||||
|
def test(self, **kwargs): |
||||
|
host = self.conf('host') |
||||
|
apikey = self.conf('apikey') |
||||
|
message = self.test_message |
||||
|
|
||||
|
host = cleanHost(host) |
||||
|
url = '%semby/Notifications/Admin' % (host) |
||||
|
values = {'Name': 'CouchPotato', 'Description': message, 'ImageUrl': 'https://raw.githubusercontent.com/RuudBurger/CouchPotatoServer/master/couchpotato/static/images/notify.couch.small.png'} |
||||
|
data = json.dumps(values) |
||||
|
|
||||
|
try: |
||||
|
req = urllib2.Request(url, data) |
||||
|
req.add_header('X-MediaBrowser-Token', apikey) |
||||
|
req.add_header('Content-Type', 'application/json') |
||||
|
|
||||
|
response = urllib2.urlopen(req) |
||||
|
result = response.read() |
||||
|
response.close() |
||||
|
return { |
||||
|
'success': True |
||||
|
} |
||||
|
|
||||
|
except (urllib2.URLError, IOError), e: |
||||
|
return False |
||||
|
|
||||
|
|
||||
|
config = [{ |
||||
|
'name': 'emby', |
||||
|
'groups': [ |
||||
|
{ |
||||
|
'tab': 'notifications', |
||||
|
'list': 'notification_providers', |
||||
|
'name': 'emby', |
||||
|
'options': [ |
||||
|
{ |
||||
|
'name': 'enabled', |
||||
|
'default': 0, |
||||
|
'type': 'enabler', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'host', |
||||
|
'default': 'localhost:8096', |
||||
|
'description': 'IP:Port, default localhost:8096' |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'apikey', |
||||
|
'label': 'API Key', |
||||
|
'default': '', |
||||
|
}, |
||||
|
], |
||||
|
} |
||||
|
], |
||||
|
}] |
@ -0,0 +1,126 @@ |
|||||
|
import json |
||||
|
from couchpotato.core.logger import CPLog |
||||
|
from couchpotato.core.notifications.base import Notification |
||||
|
|
||||
|
log = CPLog(__name__) |
||||
|
autoload = 'Slack' |
||||
|
|
||||
|
|
||||
|
class Slack(Notification): |
||||
|
url = 'https://slack.com/api/chat.postMessage' |
||||
|
required_confs = ('token', 'channels',) |
||||
|
|
||||
|
def notify(self, message='', data=None, listener=None): |
||||
|
for key in self.required_confs: |
||||
|
if not self.conf(key): |
||||
|
log.warning('Slack notifications are enabled, but ' |
||||
|
'"{0}" is not specified.'.format(key)) |
||||
|
return False |
||||
|
|
||||
|
data = data or {} |
||||
|
message = message.strip() |
||||
|
|
||||
|
if self.conf('include_imdb') and 'identifier' in data: |
||||
|
template = ' http://www.imdb.com/title/{0[identifier]}/' |
||||
|
message += template.format(data) |
||||
|
|
||||
|
payload = { |
||||
|
'token': self.conf('token'), |
||||
|
'text': message, |
||||
|
'username': self.conf('bot_name'), |
||||
|
'unfurl_links': self.conf('include_imdb'), |
||||
|
'as_user': self.conf('as_user'), |
||||
|
'icon_url': self.conf('icon_url'), |
||||
|
'icon_emoji': self.conf('icon_emoji') |
||||
|
} |
||||
|
|
||||
|
channels = self.conf('channels').split(',') |
||||
|
for channel in channels: |
||||
|
payload['channel'] = channel.strip() |
||||
|
response = self.urlopen(self.url, data=payload) |
||||
|
response = json.loads(response) |
||||
|
if not response['ok']: |
||||
|
log.warning('Notification sending to Slack has failed. Error ' |
||||
|
'code: %s.', response['error']) |
||||
|
return False |
||||
|
return True |
||||
|
|
||||
|
|
||||
|
config = [{ |
||||
|
'name': 'slack', |
||||
|
'groups': [ |
||||
|
{ |
||||
|
'tab': 'notifications', |
||||
|
'list': 'notification_providers', |
||||
|
'name': 'slack', |
||||
|
'options': [ |
||||
|
{ |
||||
|
'name': 'enabled', |
||||
|
'default': 0, |
||||
|
'type': 'enabler', |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'token', |
||||
|
'description': ( |
||||
|
'Your Slack authentication token.', |
||||
|
'Can be created at https://api.slack.com/web' |
||||
|
) |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'channels', |
||||
|
'description': ( |
||||
|
'Channel to send notifications to.', |
||||
|
'Can be a public channel, private group or IM ' |
||||
|
'channel. Can be an encoded ID or a name ' |
||||
|
'(staring with a hashtag, e.g. #general). ' |
||||
|
'Separate with commas in order to notify multiple ' |
||||
|
'channels. It is however recommended to send ' |
||||
|
'notifications to only one channel due to ' |
||||
|
'the Slack API rate limits.' |
||||
|
) |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'include_imdb', |
||||
|
'default': True, |
||||
|
'type': 'bool', |
||||
|
'descrpition': 'Include a link to the movie page on IMDB.' |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'bot_name', |
||||
|
'description': 'Name of bot.', |
||||
|
'default': 'CouchPotato', |
||||
|
'advanced': True, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'as_user', |
||||
|
'description': 'Send message as the authentication token ' |
||||
|
' user.', |
||||
|
'default': False, |
||||
|
'type': 'bool', |
||||
|
'advanced': True |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'icon_url', |
||||
|
'description': 'URL to an image to use as the icon for ' |
||||
|
'notifications.', |
||||
|
'advanced': True, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'icon_emoji', |
||||
|
'description': ( |
||||
|
'Emoji to use as the icon for notifications.', |
||||
|
'Overrides icon_url' |
||||
|
), |
||||
|
'advanced': True, |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'on_snatch', |
||||
|
'default': 0, |
||||
|
'type': 'bool', |
||||
|
'advanced': True, |
||||
|
'description': 'Also send message when movie is snatched.', |
||||
|
}, |
||||
|
], |
||||
|
} |
||||
|
], |
||||
|
}] |
@ -1,199 +0,0 @@ |
|||||
.page.log .nav { |
|
||||
display: block; |
|
||||
text-align: center; |
|
||||
padding: 0 0 30px; |
|
||||
margin: 0; |
|
||||
font-size: 20px; |
|
||||
position: fixed; |
|
||||
width: 100%; |
|
||||
bottom: 0; |
|
||||
left: 0; |
|
||||
background: #4E5969; |
|
||||
z-index: 100; |
|
||||
} |
|
||||
|
|
||||
.page.log .nav li { |
|
||||
display: inline-block; |
|
||||
padding: 5px 10px; |
|
||||
margin: 0; |
|
||||
} |
|
||||
|
|
||||
.page.log .nav li.select, |
|
||||
.page.log .nav li.clear { |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
|
|
||||
.page.log .nav li:hover:not(.active):not(.filter) { |
|
||||
background: rgba(255, 255, 255, 0.1); |
|
||||
} |
|
||||
|
|
||||
.page.log .nav li.active { |
|
||||
font-weight: bold; |
|
||||
cursor: default; |
|
||||
background: rgba(255,255,255,.1); |
|
||||
} |
|
||||
|
|
||||
@media all and (max-width: 480px) { |
|
||||
.page.log .nav { |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
|
|
||||
.page.log .nav li { |
|
||||
padding: 5px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.page.log .nav li.hint { |
|
||||
text-align: center; |
|
||||
width: 400px; |
|
||||
left: 50%; |
|
||||
margin-left: -200px; |
|
||||
font-style: italic; |
|
||||
font-size: 11px; |
|
||||
position: absolute; |
|
||||
right: 20px; |
|
||||
opacity: .5; |
|
||||
bottom: 5px; |
|
||||
} |
|
||||
|
|
||||
.page.log .loading { |
|
||||
text-align: center; |
|
||||
font-size: 20px; |
|
||||
padding: 50px; |
|
||||
} |
|
||||
|
|
||||
.page.log .container { |
|
||||
padding: 30px 0 60px; |
|
||||
overflow: hidden; |
|
||||
line-height: 150%; |
|
||||
font-size: 11px; |
|
||||
color: #FFF; |
|
||||
} |
|
||||
|
|
||||
.page.log .container select { |
|
||||
vertical-align: top; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .time { |
|
||||
clear: both; |
|
||||
color: lightgrey; |
|
||||
font-size: 10px; |
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1); |
|
||||
position: relative; |
|
||||
overflow: hidden; |
|
||||
padding: 0 3px; |
|
||||
font-family: Lucida Console, Monaco, Nimbus Mono L, monospace, serif; |
|
||||
} |
|
||||
.page.log .container .time.highlight { |
|
||||
background: rgba(255, 255, 255, 0.1); |
|
||||
} |
|
||||
.page.log .container .time span { |
|
||||
padding: 5px 0 3px; |
|
||||
display: inline-block; |
|
||||
vertical-align: middle; |
|
||||
} |
|
||||
|
|
||||
.page.log[data-filter=INFO] .error, |
|
||||
.page.log[data-filter=INFO] .debug, |
|
||||
.page.log[data-filter=ERROR] .debug, |
|
||||
.page.log[data-filter=ERROR] .info, |
|
||||
.page.log[data-filter=DEBUG] .info, |
|
||||
.page.log[data-filter=DEBUG] .error { |
|
||||
display: none; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .type { |
|
||||
margin-left: 10px; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .message { |
|
||||
float: right; |
|
||||
width: 86%; |
|
||||
white-space: pre-wrap; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .error { color: #FFA4A4; } |
|
||||
.page.log .container .debug span { opacity: .6; } |
|
||||
|
|
||||
.do_report { |
|
||||
position: absolute; |
|
||||
padding: 10px; |
|
||||
} |
|
||||
|
|
||||
.page.log .report { |
|
||||
position: fixed; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
background: rgba(0,0,0,.7); |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
z-index: 99999; |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
|
|
||||
.page.log .report .button { |
|
||||
display: inline-block; |
|
||||
margin: 10px 0; |
|
||||
padding: 10px; |
|
||||
} |
|
||||
|
|
||||
.page.log .report .bug { |
|
||||
width: 800px; |
|
||||
height: 80%; |
|
||||
position: absolute; |
|
||||
left: 50%; |
|
||||
top: 50%; |
|
||||
margin: 0 0 0 -400px; |
|
||||
transform: translate(0, -50%); |
|
||||
} |
|
||||
|
|
||||
.page.log .report .bug textarea { |
|
||||
display: block; |
|
||||
width: 100%; |
|
||||
background: #FFF; |
|
||||
padding: 20px; |
|
||||
overflow: auto; |
|
||||
color: #666; |
|
||||
height: 70%; |
|
||||
font-size: 12px; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .time ::-webkit-selection { |
|
||||
background-color: #000; |
|
||||
color: #FFF; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .time ::-moz-selection { |
|
||||
background-color: #000; |
|
||||
color: #FFF; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .time ::-ms-selection { |
|
||||
background-color: #000; |
|
||||
color: #FFF; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .time.highlight ::selection { |
|
||||
background-color: transparent; |
|
||||
color: inherit; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .time.highlight ::-webkit-selection { |
|
||||
background-color: transparent; |
|
||||
color: inherit; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .time.highlight ::-moz-selection { |
|
||||
background-color: transparent; |
|
||||
color: inherit; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .time.highlight ::-ms-selection { |
|
||||
background-color: transparent; |
|
||||
color: inherit; |
|
||||
} |
|
||||
|
|
||||
.page.log .container .time.highlight ::selection { |
|
||||
background-color: transparent; |
|
||||
color: inherit; |
|
||||
} |
|
@ -0,0 +1,159 @@ |
|||||
|
@import "_mixins"; |
||||
|
|
||||
|
.page.log { |
||||
|
|
||||
|
.nav { |
||||
|
text-align: right; |
||||
|
padding: 0; |
||||
|
margin: 0; |
||||
|
|
||||
|
li { |
||||
|
display: inline-block; |
||||
|
padding: 5px 10px; |
||||
|
margin: 0; |
||||
|
|
||||
|
&.select, &.clear { |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
&:hover:not(.active):not(.filter) { |
||||
|
background: rgba(255, 255, 255, 0.1); |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
font-weight: bold; |
||||
|
cursor: default; |
||||
|
background: rgba(255,255,255,.1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.hint { |
||||
|
font-style: italic; |
||||
|
opacity: .5; |
||||
|
margin-top: 3px; |
||||
|
} |
||||
|
|
||||
|
.container { |
||||
|
padding: $padding; |
||||
|
overflow: hidden; |
||||
|
line-height: 150%; |
||||
|
|
||||
|
&.loading { |
||||
|
text-align: center; |
||||
|
font-size: 20px; |
||||
|
padding: 100px 50px; |
||||
|
} |
||||
|
|
||||
|
select { |
||||
|
vertical-align: top; |
||||
|
} |
||||
|
|
||||
|
.time { |
||||
|
clear: both; |
||||
|
font-size: .75em; |
||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1); |
||||
|
overflow: hidden; |
||||
|
padding: 0 3px; |
||||
|
font-family: Lucida Console, Monaco, Nimbus Mono L, monospace, serif; |
||||
|
display: flex; |
||||
|
|
||||
|
&.highlight { |
||||
|
background: $theme_off; |
||||
|
} |
||||
|
|
||||
|
span { |
||||
|
padding: 5px 0 3px; |
||||
|
display: inline-block; |
||||
|
vertical-align: middle; |
||||
|
width: 90px; |
||||
|
} |
||||
|
|
||||
|
::selection { |
||||
|
background-color: #000; |
||||
|
color: #FFF; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.type.type { |
||||
|
margin-left: 10px; |
||||
|
width: 40px; |
||||
|
} |
||||
|
|
||||
|
.message { |
||||
|
white-space: pre-wrap; |
||||
|
flex: 1 auto; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.error { color: #FFA4A4; } |
||||
|
.debug span { opacity: .6; } |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
[data-filter=INFO] .error, |
||||
|
[data-filter=INFO] .debug, |
||||
|
[data-filter=ERROR] .debug, |
||||
|
[data-filter=ERROR] .info, |
||||
|
[data-filter=DEBUG] .info, |
||||
|
[data-filter=DEBUG] .error { |
||||
|
display: none; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.report_popup.report_popup { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
top: 0; |
||||
|
z-index: 99999; |
||||
|
font-size: 14px; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
opacity: 1; |
||||
|
color: #FFF; |
||||
|
pointer-events: auto; |
||||
|
|
||||
|
.button { |
||||
|
margin: 10px 0; |
||||
|
padding: 10px; |
||||
|
color: $background_color; |
||||
|
background: $primary_color; |
||||
|
} |
||||
|
|
||||
|
.bug { |
||||
|
width: 80%; |
||||
|
height: 80%; |
||||
|
max-height: 800px; |
||||
|
max-width: 800px; |
||||
|
|
||||
|
display: flex; |
||||
|
flex-flow: column nowrap; |
||||
|
|
||||
|
> span { |
||||
|
margin: $padding/2 0 $padding 0; |
||||
|
} |
||||
|
|
||||
|
textarea { |
||||
|
display: block; |
||||
|
width: 100%; |
||||
|
background: #FFF; |
||||
|
padding: 20px; |
||||
|
overflow: auto; |
||||
|
color: #666; |
||||
|
height: 70%; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.do_report.do_report { |
||||
|
z-index: 10000; |
||||
|
position: absolute; |
||||
|
padding: 10px; |
||||
|
background: $primary_color; |
||||
|
color: #FFF; |
||||
|
} |
@ -1,197 +0,0 @@ |
|||||
.add_new_profile { |
|
||||
padding: 20px; |
|
||||
display: block; |
|
||||
text-align: center; |
|
||||
font-size: 20px; |
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2); |
|
||||
} |
|
||||
|
|
||||
.profile { |
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2); |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
.profile > .delete { |
|
||||
position: absolute; |
|
||||
padding: 16px; |
|
||||
right: 0; |
|
||||
cursor: pointer; |
|
||||
opacity: 0.6; |
|
||||
color: #fd5353; |
|
||||
} |
|
||||
.profile > .delete:hover { |
|
||||
opacity: 1; |
|
||||
} |
|
||||
|
|
||||
.profile .ctrlHolder:hover { |
|
||||
background: none; |
|
||||
} |
|
||||
|
|
||||
.profile .qualities { |
|
||||
min-height: 80px; |
|
||||
} |
|
||||
|
|
||||
.profile .formHint { |
|
||||
width: 210px !important; |
|
||||
vertical-align: top !important; |
|
||||
margin: 0 !important; |
|
||||
padding-left: 3px !important; |
|
||||
opacity: 0.1; |
|
||||
} |
|
||||
.profile:hover .formHint { |
|
||||
opacity: 1; |
|
||||
} |
|
||||
|
|
||||
.profile .wait_for { |
|
||||
padding-top: 0; |
|
||||
padding-bottom: 20px; |
|
||||
} |
|
||||
|
|
||||
.profile .wait_for input { |
|
||||
margin: 0 5px !important; |
|
||||
} |
|
||||
|
|
||||
.profile .wait_for .minimum_score_input { |
|
||||
width: 40px !important; |
|
||||
text-align: left; |
|
||||
} |
|
||||
|
|
||||
.profile .types { |
|
||||
padding: 0; |
|
||||
margin: 0 20px 0 -4px; |
|
||||
display: inline-block; |
|
||||
} |
|
||||
|
|
||||
.profile .types li { |
|
||||
padding: 3px 5px; |
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2); |
|
||||
list-style: none; |
|
||||
} |
|
||||
.profile .types li:last-child { border: 0; } |
|
||||
|
|
||||
.profile .types li > * { |
|
||||
display: inline-block; |
|
||||
vertical-align: middle; |
|
||||
line-height: 0; |
|
||||
margin-right: 10px; |
|
||||
} |
|
||||
|
|
||||
.profile .type .check { |
|
||||
margin-top: -1px; |
|
||||
} |
|
||||
|
|
||||
.profile .quality_type select { |
|
||||
width: 120px; |
|
||||
margin-left: -1px; |
|
||||
} |
|
||||
|
|
||||
.profile .types li.is_empty .check, |
|
||||
.profile .types li.is_empty .delete, |
|
||||
.profile .types li.is_empty .handle, |
|
||||
.profile .types li.is_empty .check_label { |
|
||||
visibility: hidden; |
|
||||
} |
|
||||
|
|
||||
.profile .types .type label { |
|
||||
display: inline-block; |
|
||||
width: auto; |
|
||||
float: none; |
|
||||
text-transform: uppercase; |
|
||||
font-size: 11px; |
|
||||
font-weight: normal; |
|
||||
margin-right: 20px; |
|
||||
text-shadow: none; |
|
||||
vertical-align: bottom; |
|
||||
padding: 0; |
|
||||
height: 17px; |
|
||||
} |
|
||||
.profile .types .type label .check { |
|
||||
margin-right: 5px; |
|
||||
} |
|
||||
.profile .types .type label .check_label { |
|
||||
display: inline-block; |
|
||||
vertical-align: top; |
|
||||
height: 16px; |
|
||||
line-height: 13px; |
|
||||
} |
|
||||
|
|
||||
.profile .types .type .threed { |
|
||||
display: none; |
|
||||
} |
|
||||
|
|
||||
.profile .types .type.allow_3d .threed { |
|
||||
display: inline-block; |
|
||||
} |
|
||||
|
|
||||
.profile .types .type .handle { |
|
||||
background: url('../../images/handle.png') center; |
|
||||
display: inline-block; |
|
||||
height: 20px; |
|
||||
width: 20px; |
|
||||
cursor: -moz-grab; |
|
||||
cursor: -webkit-grab; |
|
||||
cursor: grab; |
|
||||
margin: 0; |
|
||||
} |
|
||||
|
|
||||
.profile .types .type .delete { |
|
||||
height: 20px; |
|
||||
width: 20px; |
|
||||
line-height: 20px; |
|
||||
visibility: hidden; |
|
||||
cursor: pointer; |
|
||||
font-size: 13px; |
|
||||
color: #fd5353; |
|
||||
} |
|
||||
.profile .types .type:not(.allow_3d) .delete { |
|
||||
margin-left: 55px; |
|
||||
} |
|
||||
|
|
||||
.profile .types .type:hover:not(.is_empty) .delete { |
|
||||
visibility: visible; |
|
||||
} |
|
||||
|
|
||||
#profile_ordering { |
|
||||
|
|
||||
} |
|
||||
|
|
||||
#profile_ordering ul { |
|
||||
float: left; |
|
||||
margin: 0; |
|
||||
width: 275px; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
#profile_ordering li { |
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2); |
|
||||
padding: 0 5px; |
|
||||
} |
|
||||
#profile_ordering li:last-child { border: 0; } |
|
||||
|
|
||||
#profile_ordering li .check { |
|
||||
margin: 2px 10px 0 0; |
|
||||
vertical-align: top; |
|
||||
} |
|
||||
|
|
||||
#profile_ordering li > span { |
|
||||
display: inline-block; |
|
||||
height: 20px; |
|
||||
vertical-align: top; |
|
||||
line-height: 20px; |
|
||||
} |
|
||||
|
|
||||
#profile_ordering li .handle { |
|
||||
background: url('../../images/handle.png') center; |
|
||||
width: 20px; |
|
||||
float: right; |
|
||||
cursor: -webkit-grab; |
|
||||
cursor: -moz-grab; |
|
||||
cursor: grab; |
|
||||
} |
|
||||
|
|
||||
#profile_ordering .formHint { |
|
||||
clear: none; |
|
||||
float: right; |
|
||||
width: 250px; |
|
||||
margin: 0; |
|
||||
} |
|
@ -0,0 +1,150 @@ |
|||||
|
@import "_mixins"; |
||||
|
|
||||
|
.add_new_profile { |
||||
|
padding: 20px; |
||||
|
display: block; |
||||
|
text-align: center; |
||||
|
font-size: 20px; |
||||
|
border-bottom: 1px solid $theme_off; |
||||
|
} |
||||
|
|
||||
|
.profile { |
||||
|
margin-bottom: 20px; |
||||
|
|
||||
|
.quality_label input { |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.ctrlHolder { |
||||
|
|
||||
|
.types { |
||||
|
flex: 1 1 auto; |
||||
|
min-width: 360px; |
||||
|
|
||||
|
.type { |
||||
|
display: flex; |
||||
|
flex-row: row nowrap; |
||||
|
align-items: center; |
||||
|
padding: 2px 0; |
||||
|
|
||||
|
label { |
||||
|
min-width: 0; |
||||
|
margin-left: $padding/2; |
||||
|
|
||||
|
span { |
||||
|
font-size: .9em; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
input[type=checkbox] { |
||||
|
margin-right: 3px; |
||||
|
} |
||||
|
|
||||
|
.delete, .handle { |
||||
|
margin-left: $padding/4; |
||||
|
width: 20px; |
||||
|
font-size: 20px; |
||||
|
opacity: .1; |
||||
|
text-align: center; |
||||
|
cursor: pointer; |
||||
|
|
||||
|
&.handle { |
||||
|
cursor: move; |
||||
|
cursor: grab; |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.is_empty { |
||||
|
.delete, .handle { |
||||
|
display: none; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
&.wait_for.wait_for { |
||||
|
display: block; |
||||
|
|
||||
|
input { |
||||
|
min-width: 0; |
||||
|
width: 40px; |
||||
|
text-align: center; |
||||
|
margin: 0 2px; |
||||
|
} |
||||
|
|
||||
|
.advanced { |
||||
|
display: none; |
||||
|
color: $primary_color; |
||||
|
|
||||
|
.show_advanced & { |
||||
|
display: inline; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.formHint { |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#profile_ordering { |
||||
|
ul { |
||||
|
list-style: none; |
||||
|
margin: 0; |
||||
|
width: 275px; |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
li { |
||||
|
border-bottom: 1px solid $theme_off; |
||||
|
padding: 5px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
&:hover { |
||||
|
background: $theme_off; |
||||
|
} |
||||
|
|
||||
|
&:last-child { border: 0; } |
||||
|
|
||||
|
input[type=checkbox] { |
||||
|
margin: 2px 10px 0 0; |
||||
|
vertical-align: top; |
||||
|
} |
||||
|
|
||||
|
> span { |
||||
|
display: inline-block; |
||||
|
height: 20px; |
||||
|
vertical-align: top; |
||||
|
line-height: 20px; |
||||
|
|
||||
|
&.profile_label { |
||||
|
flex: 1 1 auto; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.handle { |
||||
|
font-size: 20px; |
||||
|
width: 20px; |
||||
|
float: right; |
||||
|
cursor: move; |
||||
|
cursor: grab; |
||||
|
opacity: .5; |
||||
|
text-align: center; |
||||
|
|
||||
|
&:hover { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.formHint { |
||||
|
} |
||||
|
} |
@ -1,26 +0,0 @@ |
|||||
.group_sizes { |
|
||||
|
|
||||
} |
|
||||
|
|
||||
.group_sizes .head { |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
|
|
||||
.group_sizes .ctrlHolder { |
|
||||
padding-top: 4px !important; |
|
||||
padding-bottom: 4px !important; |
|
||||
font-size: 12px; |
|
||||
} |
|
||||
|
|
||||
.group_sizes .label { |
|
||||
max-width: 120px; |
|
||||
} |
|
||||
|
|
||||
.group_sizes .min, .group_sizes .max { |
|
||||
text-align: center; |
|
||||
width: 50px; |
|
||||
max-width: 50px; |
|
||||
margin: 0 5px !important; |
|
||||
padding: 0 3px; |
|
||||
display: inline-block; |
|
||||
} |
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue