Merge branch 'refs/heads/develop'
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from couchpotato.api import api_docs, api_docs_missing, api
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.variable import md5, tryInt
|
||||
@@ -5,9 +9,6 @@ from couchpotato.core.logger import CPLog
|
||||
from couchpotato.environment import Env
|
||||
from tornado import template
|
||||
from tornado.web import RequestHandler, authenticated
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -5,7 +5,6 @@ from urlparse import urlparse
|
||||
import os
|
||||
|
||||
from couchpotato.core._base.downloader.main import DownloaderBase, ReleaseDownloadList
|
||||
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import sp
|
||||
from couchpotato.core.helpers.variable import cleanHost, splitString
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
from datetime import timedelta
|
||||
from operator import itemgetter
|
||||
import time
|
||||
import traceback
|
||||
from string import ascii_lowercase
|
||||
|
||||
@@ -164,8 +167,15 @@ class MediaPlugin(MediaBase):
|
||||
status = list(status if isinstance(status, (list, tuple)) else [status])
|
||||
|
||||
for s in status:
|
||||
for ms in db.get_many('media_status', s, with_doc = with_doc):
|
||||
yield ms['doc'] if with_doc else ms
|
||||
for ms in db.get_many('media_status', s):
|
||||
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):
|
||||
|
||||
@@ -452,20 +462,20 @@ class MediaPlugin(MediaBase):
|
||||
if not m['profile_id']:
|
||||
m['status'] = 'done'
|
||||
else:
|
||||
move_to_wanted = True
|
||||
m['status'] = 'active'
|
||||
|
||||
try:
|
||||
profile = db.get('id', m['profile_id'])
|
||||
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']:
|
||||
index = profile['qualities'].index(q_identifier)
|
||||
if done_releases:
|
||||
# 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:
|
||||
log.debug('Failed restatus: %s', traceback.format_exc())
|
||||
|
||||
@@ -473,7 +483,10 @@ class MediaPlugin(MediaBase):
|
||||
if previous_status != m['status']:
|
||||
db.update(m)
|
||||
|
||||
return True
|
||||
# Tag media as recent
|
||||
self.tag(media_id, 'recent')
|
||||
|
||||
return m['status']
|
||||
except:
|
||||
log.error('Failed restatus: %s', traceback.format_exc())
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ class MovieBase(MovieTypeBase):
|
||||
|
||||
db.update(m)
|
||||
|
||||
fireEvent('media.restatus', m['_id'])
|
||||
fireEvent('media.restatus', m['_id'], single = True)
|
||||
|
||||
m = db.get('id', media_id)
|
||||
|
||||
|
||||
@@ -120,8 +120,19 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
|
||||
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.')
|
||||
fireEvent('media.restatus', movie['_id'], single = True)
|
||||
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)
|
||||
release_dates = fireEvent('movie.update_release_dates', movie['_id'], merge = True)
|
||||
|
||||
@@ -133,12 +144,6 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
ignore_eta = manual
|
||||
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)
|
||||
|
||||
# Ignore eta once every 7 days
|
||||
@@ -154,8 +159,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
profile = db.get('id', movie['profile_id'])
|
||||
ret = False
|
||||
|
||||
index = 0
|
||||
for q_identifier in profile.get('qualities'):
|
||||
for index, q_identifier in enumerate(profile.get('qualities', [])):
|
||||
quality_custom = {
|
||||
'index': index,
|
||||
'quality': q_identifier,
|
||||
@@ -164,8 +168,6 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
'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'])
|
||||
if not alway_search and could_not_be_released:
|
||||
too_early_to_search.append(q_identifier)
|
||||
@@ -189,7 +191,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
# Don't search for quality lower then already available.
|
||||
if has_better_quality > 0:
|
||||
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
|
||||
|
||||
quality = fireEvent('quality.single', identifier = q_identifier, single = True)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.variable import splitString, removeDuplicate, getIdentifier
|
||||
|
||||
@@ -88,6 +88,7 @@ class ProfilePlugin(Plugin):
|
||||
'core': kwargs.get('core', False),
|
||||
'qualities': [],
|
||||
'wait_for': [],
|
||||
'stop_after': [],
|
||||
'finish': [],
|
||||
'3d': []
|
||||
}
|
||||
@@ -97,6 +98,7 @@ class ProfilePlugin(Plugin):
|
||||
for type in kwargs.get('types', []):
|
||||
profile['qualities'].append(type.get('quality'))
|
||||
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['3d'].append(tryInt(type.get('3d')))
|
||||
order += 1
|
||||
@@ -217,6 +219,7 @@ class ProfilePlugin(Plugin):
|
||||
'qualities': profile.get('qualities'),
|
||||
'finish': [],
|
||||
'wait_for': [],
|
||||
'stop_after': [],
|
||||
'3d': []
|
||||
}
|
||||
|
||||
@@ -224,6 +227,7 @@ class ProfilePlugin(Plugin):
|
||||
for q in profile.get('qualities'):
|
||||
pro['finish'].append(True)
|
||||
pro['wait_for'].append(0)
|
||||
pro['stop_after'].append(0)
|
||||
pro['3d'].append(threed.pop() if threed else False)
|
||||
|
||||
db.insert(pro)
|
||||
|
||||
@@ -43,9 +43,8 @@
|
||||
}
|
||||
|
||||
.profile .wait_for {
|
||||
position: absolute;
|
||||
right: 60px;
|
||||
top: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.profile .wait_for input {
|
||||
|
||||
@@ -37,20 +37,28 @@ var Profile = new Class({
|
||||
'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('label', {'text': 'Search for'}),
|
||||
self.type_container = new Element('ol.types'),
|
||||
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."
|
||||
})
|
||||
),
|
||||
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,
|
||||
'label' : self.el.getElement('.quality_label input').get('value'),
|
||||
'wait_for' : self.el.getElement('.wait_for input').get('value'),
|
||||
'stop_after' : self.el.getElement('.stop_after input').get('value'),
|
||||
'types': []
|
||||
};
|
||||
|
||||
|
||||
@@ -379,26 +379,31 @@ class QualityPlugin(Plugin):
|
||||
if score.get(q.get('identifier')):
|
||||
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'):
|
||||
return False
|
||||
# No profile so anything (scanned) is good enough
|
||||
return True
|
||||
|
||||
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]
|
||||
return profile['finish'][quality_order]
|
||||
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]
|
||||
|
||||
if index == 0 or profile['finish'][index] and int(release_age) >= int(profile['stop_after'][0]):
|
||||
return True
|
||||
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def isHigher(self, quality, compare_with, profile = None):
|
||||
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:
|
||||
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:
|
||||
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'
|
||||
|
||||
# Try to find compare quality in profile, if not found: anything is higher than a not wanted quality
|
||||
|
||||
@@ -3,7 +3,7 @@ import os
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from CodernityDB.database import RecordDeleted
|
||||
from CodernityDB.database import RecordDeleted, RecordNotFound
|
||||
from couchpotato import md5, get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, addEvent
|
||||
@@ -100,9 +100,9 @@ class Release(Plugin):
|
||||
if rel['status'] in ['available']:
|
||||
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']:
|
||||
self.updateStatus(rel['_id'], status = 'ignore')
|
||||
self.updateStatus(rel['_id'], status = 'ignored')
|
||||
|
||||
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)
|
||||
db.update(release)
|
||||
|
||||
fireEvent('media.restatus', media['_id'])
|
||||
fireEvent('media.restatus', media['_id'], single = True)
|
||||
|
||||
return True
|
||||
except:
|
||||
@@ -331,24 +331,14 @@ class Release(Plugin):
|
||||
|
||||
if media['status'] == 'active':
|
||||
profile = db.get('id', media['profile_id'])
|
||||
finished = False
|
||||
if rls['quality'] in profile['qualities']:
|
||||
nr = profile['qualities'].index(rls['quality'])
|
||||
finished = profile['finish'][nr]
|
||||
|
||||
if finished:
|
||||
if fireEvent('quality.isfinish', {'identifier': rls['quality'], 'is_3d': rls.get('is_3d', False)}, profile, single = True):
|
||||
log.info('Renamer disabled, marking media as finished: %s', log_movie)
|
||||
|
||||
# Mark release done
|
||||
self.updateStatus(rls['_id'], status = 'done')
|
||||
|
||||
# Mark media done
|
||||
mdia = db.get('id', media['_id'])
|
||||
mdia['status'] = 'done'
|
||||
mdia['last_edit'] = int(time.time())
|
||||
db.update(mdia)
|
||||
|
||||
fireEvent('media.tag', media['_id'], 'recent', single = True)
|
||||
fireEvent('media.restatus', media['_id'], single = True)
|
||||
|
||||
return True
|
||||
|
||||
@@ -511,8 +501,15 @@ class Release(Plugin):
|
||||
status = list(status if isinstance(status, (list, tuple)) else [status])
|
||||
|
||||
for s in status:
|
||||
for ms in db.get_many('release_status', s, with_doc = with_doc):
|
||||
yield ms['doc'] if with_doc else ms
|
||||
for ms in db.get_many('release_status', s):
|
||||
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):
|
||||
|
||||
|
||||
@@ -446,22 +446,19 @@ class Renamer(Plugin):
|
||||
# Before renaming, remove the lower quality files
|
||||
remove_leftovers = True
|
||||
|
||||
# Mark movie "done" once it's found the quality with the finish check
|
||||
# Get media quality profile
|
||||
profile = None
|
||||
try:
|
||||
if media.get('status') == 'active' and media.get('profile_id'):
|
||||
if media.get('profile_id'):
|
||||
try:
|
||||
profile = db.get('id', media['profile_id'])
|
||||
if fireEvent('quality.isfinish', group['meta_data']['quality'], profile, single = True):
|
||||
mdia = db.get('id', media['_id'])
|
||||
mdia['status'] = 'done'
|
||||
mdia['last_edit'] = int(time.time())
|
||||
db.update(mdia)
|
||||
|
||||
# List movie on dashboard
|
||||
fireEvent('media.tag', media['_id'], 'recent', single = True)
|
||||
|
||||
except:
|
||||
log.error('Failed marking movie finished: %s', (traceback.format_exc()))
|
||||
except:
|
||||
# Set profile to None as it does not exist anymore
|
||||
mdia = db.get('id', media['_id'])
|
||||
mdia['profile_id'] = None
|
||||
db.update(mdia)
|
||||
log.error('Error getting quality profile for %s: %s', (media_title, traceback.format_exc()))
|
||||
else:
|
||||
log.debug('Media has no quality profile: %s', media_title)
|
||||
|
||||
# Mark media for dashboard
|
||||
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
|
||||
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':
|
||||
log.info('Removing lesser or not wanted quality %s for %s.', (media_title, release.get('quality')))
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
color: #edc07f;
|
||||
}
|
||||
.page.show_advanced .advanced { display: block; }
|
||||
.page.show_advanced span.advanced,
|
||||
.page.show_advanced input.advanced { display: inline; }
|
||||
|
||||
.page.settings .tab_content {
|
||||
display: none;
|
||||
@@ -176,7 +178,7 @@
|
||||
padding: 6px 0 0;
|
||||
}
|
||||
|
||||
.page .xsmall { width: 20px !important; text-align: center; }
|
||||
.page .xsmall { width: 25px !important; text-align: center; }
|
||||
|
||||
.page .enabler {
|
||||
display: block;
|
||||
|
||||
Reference in New Issue
Block a user