Skip to content

Instantly share code, notes, and snippets.

@danilvalov
Last active February 18, 2021 18:36
Show Gist options
  • Save danilvalov/720966d26e99f06aed41 to your computer and use it in GitHub Desktop.
Save danilvalov/720966d26e99f06aed41 to your computer and use it in GitHub Desktop.
FlexGet Kinopoisk plugin
templates:
movies:
set:
path: /tmp/mnt/94E8B2B1E8B290CA/Torrents/download/DLNA/Movies
transmission:
host: ************
port: ****
username: *************
password: *************
email:
from: **********@**********.ru
to: **********@**********.ru
title: FlexGet Notification
smtp_host: *************
smtp_port: 25
smtp_username: *************
smtp_password: '*************'
tasks:
nnm-club:
rss:
url: http://nnm-club.me/forum/rss2.php?f=218&t=1&uk=*************
other_fields: [description]
ascii: yes
accept_all: yes
manipulate:
- title:
replace:
regexp: '[^\x00-\x80]+'
format: ''
- kinopoisk_id:
from: description
replace:
regexp: '(.|\n)*kinopoisk.ru/rating/([0-9]+)(.|\n)*'
format: '\2'
kinopoisk:
min_score: 7.0
min_votes: 1000
template:
- movies
from __future__ import unicode_literals, division, absolute_import
import urllib2, xmltodict
import logging
from flexget import plugin
from flexget.event import event
from flexget.plugins.filter.seen import FilterSeen
log = logging.getLogger('kinopoisk')
class FilterKinopoisk(FilterSeen):
"""
Kinopoisk filter
Using:
Add option to config.yml:
kinopoisk:
min_score: 7.0 # ignore movies with score below 7.0, default: 0
min_votes: 1000 # ignore movies with votes below 1000, default: 0
matching: 'strict' # ignore entries without kinopoisk_id, values: 'strict' or 'loose', default: 'strict'
scope: 'local' # use global or local storage, values: 'global' or 'local', default: 'global'
Please, replace values of '7.0' and '1000' to your
"""
schema = {
'oneOf': [
{
'type': 'object',
'properties': {
'min_score': {'type': 'number'},
'min_votes': {'type': 'integer'},
'matching': {'type': 'string', 'enum': ['strict', 'loose']},
'scope': {'type': 'string', 'enum': ['global', 'local']}
}
}
]
}
def __init__(self):
# remember and filter by these fields
self.fields = ['kinopoisk_id']
self.keyword = 'kinopoisk'
def is_number(self, s):
try:
float(s)
return True
except ValueError:
return False
@plugin.priority(0) # Make filter run after other filters, but before exists_movies
def on_task_filter(self, task, config):
if config is False:
return
# Reject all entries without kinopoisk id
if config.get('matching') != 'loose':
for entry in task.entries:
if 'kinopoisk_id' not in entry or not self.is_number(entry['kinopoisk_id']):
log.info('Rejecting %s because of missing movie kinopoisk id' % entry['title'])
entry.reject('Missing movie kinopoisk id')
# call super
super(FilterKinopoisk, self).on_task_filter(task, config.get('scope', True))
# check that two copies of a movie have not been accepted this run
kinopoisk_ids = set()
for entry in task.accepted:
if not self.is_number(entry['kinopoisk_id']):
entry.reject('Kinopoisk ID not found')
continue
if entry['kinopoisk_id'] in kinopoisk_ids and self.is_number(entry['kinopoisk_id']):
entry.reject('Already accepted once in task')
continue
else:
kinopoisk_ids.add(entry['kinopoisk_id'])
try:
page = urllib2.urlopen('http://rating.kinopoisk.ru/' + entry['kinopoisk_id'] + '.xml')
except urllib2.HTTPError:
entry.reject('Rating is not found')
continue
doc = page.read()
page.close()
data = xmltodict.parse(doc)
score = float(data['rating']['kp_rating']['#text'])
votes = float(data['rating']['kp_rating']['@num_vote'])
if 'min_score' in config:
if score <= 0:
entry.reject('Score is zero')
continue
if score < config['min_score']:
entry.reject('Score (%s) below minimum (%s)' % (score, config['min_score']))
continue
if 'min_votes' in config:
if votes < config['min_votes']:
entry.reject('Votes (%s) below minimum (%s)' % (votes, config['min_votes']))
continue
log.debug('Accepting %s' % (entry['title']))
@event('plugin.register')
def register_plugin():
plugin.register(FilterKinopoisk, 'kinopoisk', api_ver=2)
@vi-nest
Copy link

vi-nest commented Feb 18, 2021

2018-04-22 21:21 CRITICAL task          nnm-club        BUG: Unhandled error in plugin kinopoisk: not well-formed (invalid token): line 1, column 0
Traceback (most recent call last):
  File "d:\program\python\python27\lib\site-packages\flexget\task.py", line 486, in __run_plugin
    return method(*args, **kwargs)
  File "d:\program\python\python27\lib\site-packages\flexget\event.py", line 23, in __call__
    return self.func(*args, **kwargs)
  File "C:\Users\222\flexget\plugins\kinopoisk.py", line 90, in on_task_filter
    data = xmltodict.parse(doc)
  File "d:\program\python\python27\lib\site-packages\xmltodict.py", line 330, in parse
    parser.Parse(xml_input, True)
ExpatError: not well-formed (invalid token): line 1, column 0

Как пофиксить? Спасибо.

Looks like sometimes it gives reponse in gzip format. It can be fixed like that:

doc = page.read()
page.close()
try:
    data = xmltodict.parse(doc)
except ExpatError:
    compressed_data = io.BytesIO(doc)
    for compressed_data_item in gzip.GzipFile(fileobj=compressed_data):
        data = xmltodict.parse(compressed_data_item)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment