Browse Source

Merge branch 'refs/heads/develop'

pull/3560/head
Ruud 11 years ago
parent
commit
665478db13
  1. 7
      couchpotato/__init__.py
  2. 1
      couchpotato/core/downloaders/rtorrent_.py
  3. 35
      couchpotato/core/media/_base/media/main.py
  4. 2
      couchpotato/core/media/movie/_base/main.py
  5. 24
      couchpotato/core/media/movie/searcher.py
  6. 1
      couchpotato/core/media/movie/suggestion/main.py
  7. 4
      couchpotato/core/plugins/profile/main.py
  8. 5
      couchpotato/core/plugins/profile/static/profile.css
  9. 25
      couchpotato/core/plugins/profile/static/profile.js
  10. 17
      couchpotato/core/plugins/quality/main.py
  11. 33
      couchpotato/core/plugins/release/main.py
  12. 27
      couchpotato/core/plugins/renamer.py
  13. 4
      couchpotato/static/style/settings.css

7
couchpotato/__init__.py

@ -1,3 +1,7 @@
import os
import time
import traceback
from couchpotato.api import api_docs, api_docs_missing, api from couchpotato.api import api_docs, api_docs_missing, api
from couchpotato.core.event import fireEvent from couchpotato.core.event import fireEvent
from couchpotato.core.helpers.variable import md5, tryInt from couchpotato.core.helpers.variable import md5, tryInt
@ -5,9 +9,6 @@ from couchpotato.core.logger import CPLog
from couchpotato.environment import Env from couchpotato.environment import Env
from tornado import template from tornado import template
from tornado.web import RequestHandler, authenticated from tornado.web import RequestHandler, authenticated
import os
import time
import traceback
log = CPLog(__name__) log = CPLog(__name__)

1
couchpotato/core/downloaders/rtorrent_.py

@ -5,7 +5,6 @@ from urlparse import urlparse
import os import os
from couchpotato.core._base.downloader.main import DownloaderBase, ReleaseDownloadList from couchpotato.core._base.downloader.main import DownloaderBase, ReleaseDownloadList
from couchpotato.core.event import addEvent from couchpotato.core.event import addEvent
from couchpotato.core.helpers.encoding import sp from couchpotato.core.helpers.encoding import sp
from couchpotato.core.helpers.variable import cleanHost, splitString from couchpotato.core.helpers.variable import cleanHost, splitString

35
couchpotato/core/media/_base/media/main.py

@ -1,3 +1,6 @@
from datetime import timedelta
from operator import itemgetter
import time
import traceback import traceback
from string import ascii_lowercase from string import ascii_lowercase
@ -164,8 +167,15 @@ class MediaPlugin(MediaBase):
status = list(status if isinstance(status, (list, tuple)) else [status]) status = list(status if isinstance(status, (list, tuple)) else [status])
for s in status: for s in status:
for ms in db.get_many('media_status', s, with_doc = with_doc): for ms in db.get_many('media_status', s):
yield ms['doc'] if with_doc else ms if with_doc:
try:
doc = db.get('id', ms['_id'])
yield doc
except RecordNotFound:
log.debug('Record not found, skipping: %s', ms['_id'])
else:
yield ms
def withIdentifiers(self, identifiers, with_doc = False): def withIdentifiers(self, identifiers, with_doc = False):
@ -452,20 +462,20 @@ class MediaPlugin(MediaBase):
if not m['profile_id']: if not m['profile_id']:
m['status'] = 'done' m['status'] = 'done'
else: else:
move_to_wanted = True m['status'] = 'active'
try: try:
profile = db.get('id', m['profile_id']) profile = db.get('id', m['profile_id'])
media_releases = fireEvent('release.for_media', m['_id'], single = True) media_releases = fireEvent('release.for_media', m['_id'], single = True)
done_releases = [release for release in media_releases if release.get('status') == 'done']
for q_identifier in profile['qualities']: if done_releases:
index = profile['qualities'].index(q_identifier) # Only look at latest added release
release = sorted(done_releases, key = itemgetter('last_edit'), reverse = True)[0]
for release in media_releases:
if q_identifier == release['quality'] and (release.get('status') == 'done' and profile['finish'][index]):
move_to_wanted = False
m['status'] = 'active' if move_to_wanted else 'done' # Check if we are finished with the media
if fireEvent('quality.isfinish', {'identifier': release['quality'], 'is_3d': release.get('is_3d', False)}, profile, timedelta(seconds = time.time() - release['last_edit']).days, single = True):
m['status'] = 'done'
except RecordNotFound: except RecordNotFound:
log.debug('Failed restatus: %s', traceback.format_exc()) log.debug('Failed restatus: %s', traceback.format_exc())
@ -473,7 +483,10 @@ class MediaPlugin(MediaBase):
if previous_status != m['status']: if previous_status != m['status']:
db.update(m) db.update(m)
return True # Tag media as recent
self.tag(media_id, 'recent')
return m['status']
except: except:
log.error('Failed restatus: %s', traceback.format_exc()) log.error('Failed restatus: %s', traceback.format_exc())

2
couchpotato/core/media/movie/_base/main.py

@ -236,7 +236,7 @@ class MovieBase(MovieTypeBase):
db.update(m) db.update(m)
fireEvent('media.restatus', m['_id']) fireEvent('media.restatus', m['_id'], single = True)
m = db.get('id', media_id) m = db.get('id', media_id)

24
couchpotato/core/media/movie/searcher.py

@ -120,8 +120,19 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
if not movie['profile_id'] or (movie['status'] == 'done' and not manual): if not movie['profile_id'] or (movie['status'] == 'done' and not manual):
log.debug('Movie doesn\'t have a profile or already done, assuming in manage tab.') log.debug('Movie doesn\'t have a profile or already done, assuming in manage tab.')
fireEvent('media.restatus', movie['_id'], single = True)
return return
default_title = getTitle(movie)
if not default_title:
log.error('No proper info found for movie, removing it from library to stop it from causing more issues.')
fireEvent('media.delete', movie['_id'], single = True)
return
# Update media status and check if it is still not done (due to the stop searching after feature
if fireEvent('media.restatus', movie['_id'], single = True) == 'done':
log.debug('No better quality found, marking movie %s as done.', default_title)
pre_releases = fireEvent('quality.pre_releases', single = True) pre_releases = fireEvent('quality.pre_releases', single = True)
release_dates = fireEvent('movie.update_release_dates', movie['_id'], merge = True) release_dates = fireEvent('movie.update_release_dates', movie['_id'], merge = True)
@ -133,12 +144,6 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
ignore_eta = manual ignore_eta = manual
total_result_count = 0 total_result_count = 0
default_title = getTitle(movie)
if not default_title:
log.error('No proper info found for movie, removing it from library to cause it from having more issues.')
fireEvent('media.delete', movie['_id'], single = True)
return
fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title) fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title)
# Ignore eta once every 7 days # Ignore eta once every 7 days
@ -154,8 +159,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
profile = db.get('id', movie['profile_id']) profile = db.get('id', movie['profile_id'])
ret = False ret = False
index = 0 for index, q_identifier in enumerate(profile.get('qualities', [])):
for q_identifier in profile.get('qualities'):
quality_custom = { quality_custom = {
'index': index, 'index': index,
'quality': q_identifier, 'quality': q_identifier,
@ -164,8 +168,6 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
'3d': profile['3d'][index] if profile.get('3d') else False '3d': profile['3d'][index] if profile.get('3d') else False
} }
index += 1
could_not_be_released = not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year']) could_not_be_released = not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year'])
if not alway_search and could_not_be_released: if not alway_search and could_not_be_released:
too_early_to_search.append(q_identifier) too_early_to_search.append(q_identifier)
@ -189,7 +191,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
# Don't search for quality lower then already available. # Don't search for quality lower then already available.
if has_better_quality > 0: if has_better_quality > 0:
log.info('Better quality (%s) already available or snatched for %s', (q_identifier, default_title)) log.info('Better quality (%s) already available or snatched for %s', (q_identifier, default_title))
fireEvent('media.restatus', movie['_id']) fireEvent('media.restatus', movie['_id'], single = True)
break break
quality = fireEvent('quality.single', identifier = q_identifier, single = True) quality = fireEvent('quality.single', identifier = q_identifier, single = True)

1
couchpotato/core/media/movie/suggestion/main.py

@ -1,4 +1,3 @@
from couchpotato import get_db
from couchpotato.api import addApiView from couchpotato.api import addApiView
from couchpotato.core.event import fireEvent from couchpotato.core.event import fireEvent
from couchpotato.core.helpers.variable import splitString, removeDuplicate, getIdentifier from couchpotato.core.helpers.variable import splitString, removeDuplicate, getIdentifier

4
couchpotato/core/plugins/profile/main.py

@ -88,6 +88,7 @@ class ProfilePlugin(Plugin):
'core': kwargs.get('core', False), 'core': kwargs.get('core', False),
'qualities': [], 'qualities': [],
'wait_for': [], 'wait_for': [],
'stop_after': [],
'finish': [], 'finish': [],
'3d': [] '3d': []
} }
@ -97,6 +98,7 @@ class ProfilePlugin(Plugin):
for type in kwargs.get('types', []): for type in kwargs.get('types', []):
profile['qualities'].append(type.get('quality')) profile['qualities'].append(type.get('quality'))
profile['wait_for'].append(tryInt(kwargs.get('wait_for', 0))) profile['wait_for'].append(tryInt(kwargs.get('wait_for', 0)))
profile['stop_after'].append(tryInt(kwargs.get('stop_after', 0)))
profile['finish'].append((tryInt(type.get('finish')) == 1) if order > 0 else True) profile['finish'].append((tryInt(type.get('finish')) == 1) if order > 0 else True)
profile['3d'].append(tryInt(type.get('3d'))) profile['3d'].append(tryInt(type.get('3d')))
order += 1 order += 1
@ -217,6 +219,7 @@ class ProfilePlugin(Plugin):
'qualities': profile.get('qualities'), 'qualities': profile.get('qualities'),
'finish': [], 'finish': [],
'wait_for': [], 'wait_for': [],
'stop_after': [],
'3d': [] '3d': []
} }
@ -224,6 +227,7 @@ class ProfilePlugin(Plugin):
for q in profile.get('qualities'): for q in profile.get('qualities'):
pro['finish'].append(True) pro['finish'].append(True)
pro['wait_for'].append(0) pro['wait_for'].append(0)
pro['stop_after'].append(0)
pro['3d'].append(threed.pop() if threed else False) pro['3d'].append(threed.pop() if threed else False)
db.insert(pro) db.insert(pro)

5
couchpotato/core/plugins/profile/static/profile.css

@ -43,9 +43,8 @@
} }
.profile .wait_for { .profile .wait_for {
position: absolute; padding-top: 0;
right: 60px; padding-bottom: 20px;
top: 0;
} }
.profile .wait_for input { .profile .wait_for input {

25
couchpotato/core/plugins/profile/static/profile.js

@ -37,20 +37,28 @@ var Profile = new Class({
'placeholder': 'Profile name' 'placeholder': 'Profile name'
}) })
), ),
new Element('div.wait_for.ctrlHolder').adopt(
new Element('span', {'text':'Wait'}),
new Element('input.inlay.xsmall', {
'type':'text',
'value': data.wait_for && data.wait_for.length > 0 ? data.wait_for[0] : 0
}),
new Element('span', {'text':'day(s) for a better quality.'})
),
new Element('div.qualities.ctrlHolder').adopt( new Element('div.qualities.ctrlHolder').adopt(
new Element('label', {'text': 'Search for'}), new Element('label', {'text': 'Search for'}),
self.type_container = new Element('ol.types'), self.type_container = new Element('ol.types'),
new Element('div.formHint', { new Element('div.formHint', {
'html': "Search these qualities (2 minimum), from top to bottom. Use the checkbox, to stop searching after it found this quality." 'html': "Search these qualities (2 minimum), from top to bottom. Use the checkbox, to stop searching after it found this quality."
}) })
),
new Element('div.wait_for.ctrlHolder').adopt(
// "Wait the entered number of days for a checked quality, before downloading a lower quality release."
new Element('span', {'text':'Wait'}),
new Element('input.inlay.xsmall', {
'type':'text',
'value': data.wait_for && data.wait_for.length > 0 ? data.wait_for[0] : 0
}),
new Element('span', {'text':'day(s) for a better quality '}),
new Element('span.advanced', {'text':'and keep searching'}),
// "After a checked quality is found and downloaded, continue searching for even better quality releases for the entered number of days."
new Element('input.inlay.xsmall.advanced', {
'type':'text',
'value': data.stop_after && data.stop_after.length > 0 ? data.stop_after[0] : 0
}),
new Element('span.advanced', {'text':'day(s) for a better (checked) quality.'})
) )
); );
@ -117,6 +125,7 @@ var Profile = new Class({
'id' : self.data._id, 'id' : self.data._id,
'label' : self.el.getElement('.quality_label input').get('value'), 'label' : self.el.getElement('.quality_label input').get('value'),
'wait_for' : self.el.getElement('.wait_for input').get('value'), 'wait_for' : self.el.getElement('.wait_for input').get('value'),
'stop_after' : self.el.getElement('.stop_after input').get('value'),
'types': [] 'types': []
}; };

17
couchpotato/core/plugins/quality/main.py

@ -379,26 +379,31 @@ class QualityPlugin(Plugin):
if score.get(q.get('identifier')): if score.get(q.get('identifier')):
score[q.get('identifier')]['score'] -= 1 score[q.get('identifier')]['score'] -= 1
def isFinish(self, quality, profile): def isFinish(self, quality, profile, release_age = 0):
if not isinstance(profile, dict) or not profile.get('qualities'): if not isinstance(profile, dict) or not profile.get('qualities'):
return False # No profile so anything (scanned) is good enough
return True
try: try:
quality_order = [i for i, identifier in enumerate(profile['qualities']) if identifier == quality['identifier'] and bool(profile['3d'][i] if profile.get('3d') else 0) == bool(quality.get('is_3d', 0))][0] index = [i for i, identifier in enumerate(profile['qualities']) if identifier == quality['identifier'] and bool(profile['3d'][i] if profile.get('3d') else False) == bool(quality.get('is_3d', False))][0]
return profile['finish'][quality_order]
if index == 0 or profile['finish'][index] and int(release_age) >= int(profile['stop_after'][0]):
return True
return False
except: except:
return False return False
def isHigher(self, quality, compare_with, profile = None): def isHigher(self, quality, compare_with, profile = None):
if not isinstance(profile, dict) or not profile.get('qualities'): if not isinstance(profile, dict) or not profile.get('qualities'):
profile = {'qualities': self.order} profile = fireEvent('profile.default', single = True)
# Try to find quality in profile, if not found: a quality we do not want is lower than anything else # Try to find quality in profile, if not found: a quality we do not want is lower than anything else
try: try:
quality_order = [i for i, identifier in enumerate(profile['qualities']) if identifier == quality['identifier'] and bool(profile['3d'][i] if profile.get('3d') else 0) == bool(quality.get('is_3d', 0))][0] quality_order = [i for i, identifier in enumerate(profile['qualities']) if identifier == quality['identifier'] and bool(profile['3d'][i] if profile.get('3d') else 0) == bool(quality.get('is_3d', 0))][0]
except: except:
log.debug('Quality %s not found in profile identifiers %s', (quality['identifier'] + (' 3D' if quality.get('is_3d', 0) else ''), \ log.debug('Quality %s not found in profile identifiers %s', (quality['identifier'] + (' 3D' if quality.get('is_3d', 0) else ''), \
[identifier + ('3D' if (profile['3d'][i] if profile.get('3d') else 0) else '') for i, identifier in enumerate(profile['qualities'])])) [identifier + (' 3D' if (profile['3d'][i] if profile.get('3d') else 0) else '') for i, identifier in enumerate(profile['qualities'])]))
return 'lower' return 'lower'
# Try to find compare quality in profile, if not found: anything is higher than a not wanted quality # Try to find compare quality in profile, if not found: anything is higher than a not wanted quality

33
couchpotato/core/plugins/release/main.py

@ -3,7 +3,7 @@ import os
import time import time
import traceback import traceback
from CodernityDB.database import RecordDeleted from CodernityDB.database import RecordDeleted, RecordNotFound
from couchpotato import md5, get_db from couchpotato import md5, get_db
from couchpotato.api import addApiView from couchpotato.api import addApiView
from couchpotato.core.event import fireEvent, addEvent from couchpotato.core.event import fireEvent, addEvent
@ -100,9 +100,9 @@ class Release(Plugin):
if rel['status'] in ['available']: if rel['status'] in ['available']:
self.delete(rel['_id']) self.delete(rel['_id'])
# Set all snatched and downloaded releases to ignored to make sure they are ignored when re-adding the move # Set all snatched and downloaded releases to ignored to make sure they are ignored when re-adding the media
elif rel['status'] in ['snatched', 'downloaded']: elif rel['status'] in ['snatched', 'downloaded']:
self.updateStatus(rel['_id'], status = 'ignore') self.updateStatus(rel['_id'], status = 'ignored')
fireEvent('media.untag', media.get('_id'), 'recent', single = True) fireEvent('media.untag', media.get('_id'), 'recent', single = True)
@ -164,7 +164,7 @@ class Release(Plugin):
release['files'] = dict((k, [toUnicode(x) for x in v]) for k, v in group['files'].items() if v) release['files'] = dict((k, [toUnicode(x) for x in v]) for k, v in group['files'].items() if v)
db.update(release) db.update(release)
fireEvent('media.restatus', media['_id']) fireEvent('media.restatus', media['_id'], single = True)
return True return True
except: except:
@ -331,24 +331,14 @@ class Release(Plugin):
if media['status'] == 'active': if media['status'] == 'active':
profile = db.get('id', media['profile_id']) profile = db.get('id', media['profile_id'])
finished = False if fireEvent('quality.isfinish', {'identifier': rls['quality'], 'is_3d': rls.get('is_3d', False)}, profile, single = True):
if rls['quality'] in profile['qualities']:
nr = profile['qualities'].index(rls['quality'])
finished = profile['finish'][nr]
if finished:
log.info('Renamer disabled, marking media as finished: %s', log_movie) log.info('Renamer disabled, marking media as finished: %s', log_movie)
# Mark release done # Mark release done
self.updateStatus(rls['_id'], status = 'done') self.updateStatus(rls['_id'], status = 'done')
# Mark media done # Mark media done
mdia = db.get('id', media['_id']) fireEvent('media.restatus', media['_id'], single = True)
mdia['status'] = 'done'
mdia['last_edit'] = int(time.time())
db.update(mdia)
fireEvent('media.tag', media['_id'], 'recent', single = True)
return True return True
@ -511,8 +501,15 @@ class Release(Plugin):
status = list(status if isinstance(status, (list, tuple)) else [status]) status = list(status if isinstance(status, (list, tuple)) else [status])
for s in status: for s in status:
for ms in db.get_many('release_status', s, with_doc = with_doc): for ms in db.get_many('release_status', s):
yield ms['doc'] if with_doc else ms if with_doc:
try:
doc = db.get('id', ms['_id'])
yield doc
except RecordNotFound:
log.debug('Record not found, skipping: %s', ms['_id'])
else:
yield ms
def forMedia(self, media_id): def forMedia(self, media_id):

27
couchpotato/core/plugins/renamer.py

@ -446,22 +446,19 @@ class Renamer(Plugin):
# Before renaming, remove the lower quality files # Before renaming, remove the lower quality files
remove_leftovers = True remove_leftovers = True
# Mark movie "done" once it's found the quality with the finish check # Get media quality profile
profile = None profile = None
try: if media.get('profile_id'):
if media.get('status') == 'active' and media.get('profile_id'): try:
profile = db.get('id', media['profile_id']) profile = db.get('id', media['profile_id'])
if fireEvent('quality.isfinish', group['meta_data']['quality'], profile, single = True): except:
mdia = db.get('id', media['_id']) # Set profile to None as it does not exist anymore
mdia['status'] = 'done' mdia = db.get('id', media['_id'])
mdia['last_edit'] = int(time.time()) mdia['profile_id'] = None
db.update(mdia) db.update(mdia)
log.error('Error getting quality profile for %s: %s', (media_title, traceback.format_exc()))
# List movie on dashboard else:
fireEvent('media.tag', media['_id'], 'recent', single = True) log.debug('Media has no quality profile: %s', media_title)
except:
log.error('Failed marking movie finished: %s', (traceback.format_exc()))
# Mark media for dashboard # Mark media for dashboard
mark_as_recent = False mark_as_recent = False
@ -474,7 +471,7 @@ class Renamer(Plugin):
# This is where CP removes older, lesser quality releases or releases that are not wanted anymore # This is where CP removes older, lesser quality releases or releases that are not wanted anymore
is_higher = fireEvent('quality.ishigher', \ is_higher = fireEvent('quality.ishigher', \
group['meta_data']['quality'], {'identifier': release['quality'], 'is_3d': release.get('is_3d', 0)}, profile, single = True) group['meta_data']['quality'], {'identifier': release['quality'], 'is_3d': release.get('is_3d', False)}, profile, single = True)
if is_higher == 'higher': if is_higher == 'higher':
log.info('Removing lesser or not wanted quality %s for %s.', (media_title, release.get('quality'))) log.info('Removing lesser or not wanted quality %s for %s.', (media_title, release.get('quality')))

4
couchpotato/static/style/settings.css

@ -75,6 +75,8 @@
color: #edc07f; color: #edc07f;
} }
.page.show_advanced .advanced { display: block; } .page.show_advanced .advanced { display: block; }
.page.show_advanced span.advanced,
.page.show_advanced input.advanced { display: inline; }
.page.settings .tab_content { .page.settings .tab_content {
display: none; display: none;
@ -176,7 +178,7 @@
padding: 6px 0 0; padding: 6px 0 0;
} }
.page .xsmall { width: 20px !important; text-align: center; } .page .xsmall { width: 25px !important; text-align: center; }
.page .enabler { .page .enabler {
display: block; display: block;

Loading…
Cancel
Save