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 |
|||
/data/ |
|||
/_env/ |
|||
/_source/ |
|||
.project |
|||
.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