More nosql

This commit is contained in:
Ruud committed 2014-02-02 20:41:14 +01:00
1 parent 63743dd2b6
commit 99252074be
82 files changed
+643 -715

No files matched your search

+3 -8
View File
@@ -1,8 +1,7 @@
import traceback
from couchpotato import get_session, get_db, CPLog
from couchpotato.core.event import addEvent, fireEventAsync, fireEvent
from couchpotato import get_db, CPLog
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
from couchpotato.core.plugins.base import Plugin
from couchpotato.core.settings.model import Media
log = CPLog(__name__)
@@ -34,11 +33,9 @@ class MediaBase(Plugin):
media = db.get('id', media_id)
event_name = '%s.searcher.single' % media.get('type')
fireEvent(event_name, media, on_complete = self.createNotifyFront(media_id))
fireEventAsync(event_name, media, on_complete = self.createNotifyFront(media_id))
except:
log.error('Failed creating onComplete: %s', traceback.format_exc())
finally:
pass #db.close()
return onComplete
@@ -53,7 +50,5 @@ class MediaBase(Plugin):
fireEvent('notify.frontend', type = event_name, data = media)
except:
log.error('Failed creating onComplete: %s', traceback.format_exc())
finally:
pass #db.close()
return notifyFront
@@ -1,13 +0,0 @@
from couchpotato.core.event import addEvent
from couchpotato.core.plugins.base import Plugin
class LibraryBase(Plugin):
_type = None
def initType(self):
addEvent('library.types', self.getType)
def getType(self):
return self._type
+12 -7
View File
@@ -14,9 +14,14 @@ class MediaIMDBIndex(HashIndex):
return int(key.strip('t'))
def make_key_value(self, data):
if data.get('type') == 'media' and data.get('identifier'):
if data.get('_t') == 'media' and data.get('identifier'):
return int(data['identifier'].strip('t')), None
def run_to_dict(self, db, media_id, dict = None):
if not dict: dict = {}
return db.get('id', media_id)
def run_with_status(self, db, status = []):
status = list(status if isinstance(status, (list, tuple)) else [status])
@@ -29,15 +34,15 @@ class MediaIMDBIndex(HashIndex):
class MediaStatusIndex(TreeBasedIndex):
def __init__(self, *args, **kwargs):
kwargs['key_format'] = '16s'
kwargs['key_format'] = '32s'
super(MediaStatusIndex, self).__init__(*args, **kwargs)
def make_key(self, key):
return md5(key).digest()
return md5(key).hexdigest()
def make_key_value(self, data):
if data.get('type') == 'media' and data.get('status'):
return md5(data.get('status')).digest(), None
if data.get('_t') == 'media' and data.get('status'):
return md5(data.get('status')).hexdigest(), None
class TitleIndex(MultiTreeBasedIndex):
@@ -52,7 +57,7 @@ from itertools import izip"""
def make_key_value(self, data):
if data.get('type') == 'title' and len(data.get('title', '')) > 0:
if data.get('_t') == 'title' and len(data.get('title', '')) > 0:
out = set()
title = data.get('title').lower()
@@ -80,5 +85,5 @@ class YearIndex(TreeBasedIndex):
return key
def make_key_value(self, data):
if data.get('type') == 'media' and data.get('year') is not None:
if data.get('_t') == 'media' and data.get('year') is not None:
return data['year'], None
+100 -99
View File
@@ -1,16 +1,14 @@
import traceback
import time
from couchpotato import get_session, tryInt, get_db
from couchpotato.api import addApiView
from couchpotato.core.event import fireEvent, fireEventAsync, addEvent
from couchpotato.core.helpers.encoding import toUnicode
from couchpotato.core.helpers.variable import mergeDicts, splitString, getImdb, getTitle
from couchpotato.core.logger import CPLog
from couchpotato.core.media import MediaBase
from .index import MediaIMDBIndex, TitleIndex, MediaStatusIndex, YearIndex
from couchpotato.core.settings.model import Library, LibraryTitle, Release, \
Media
from sqlalchemy.orm import joinedload_all
from sqlalchemy.sql.expression import or_, asc, not_, desc
from string import ascii_lowercase
log = CPLog(__name__)
@@ -63,10 +61,10 @@ class MediaPlugin(MediaBase):
addEvent('database.setup', self.databaseSetup)
addEvent('app.load2', self.addSingleRefreshView)
addEvent('app.load2', self.addSingleListView)
addEvent('app.load2', self.addSingleCharView)
addEvent('app.load2', self.addSingleDeleteView)
addEvent('app.load', self.addSingleRefreshView)
addEvent('app.load', self.addSingleListView)
addEvent('app.load', self.addSingleCharView)
addEvent('app.load', self.addSingleDeleteView)
addEvent('media.get', self.get)
addEvent('media.list', self.list)
@@ -126,7 +124,7 @@ class MediaPlugin(MediaBase):
try:
media = get_db().get('id', media_id)
default_title = getTitle(media_id)
default_title = getTitle(media)
event = 'library.update.%s' % media.get('type')
def handler():
@@ -157,7 +155,7 @@ class MediaPlugin(MediaBase):
results = None
if m:
results = db.run('media', 'to_dict', m, self.default_dict)
results = db.run('media', 'to_dict', m['_id'])
return results
@@ -172,7 +170,9 @@ class MediaPlugin(MediaBase):
def list(self, types = None, status = None, release_status = None, limit_offset = None, starts_with = None, search = None, order = None):
db = get_session()
db = get_db()
start = time.time()
# Make a list from string
if status and not isinstance(status, (list, tuple)):
@@ -182,120 +182,128 @@ class MediaPlugin(MediaBase):
if types and not isinstance(types, (list, tuple)):
types = [types]
# query movie ids
q = db.query(Media) \
.with_entities(Media.id) \
.group_by(Media.id)
# query media ids
all_media_ids = set([x['_id'] for x in db.all('media')])
media_ids = all_media_ids
filter_by = {}
# Filter on movie status
if status and len(status) > 0:
statuses = fireEvent('status.get', status, single = len(status) > 1)
statuses = [s.get('id') for s in statuses]
q = q.filter(Media.status_id.in_(statuses))
filter_by['media_status'] = set()
for media_status in db.run('media', 'with_status', status):
filter_by['media_status'].add(media_status.get('_id'))
# Filter on release status
if release_status and len(release_status) > 0:
q = q.join(Media.releases)
filter_by['release_status'] = set()
for release_status in db.run('release', 'with_status', release_status):
filter_by['release_status'].add(release_status.get('media_id'))
statuses = fireEvent('status.get', release_status, single = len(release_status) > 1)
statuses = [s.get('id') for s in statuses]
q = q.filter(Release.status_id.in_(statuses))
# Filter by combining ids
for x in filter_by:
media_ids = media_ids & filter_by[x]
# Filter on type
if types and len(types) > 0:
try: q = q.filter(Media.type.in_(types))
except: pass
# if types and len(types) > 0:
# try: q = q.filter(Media.type.in_(types))
# except: pass
# Only join when searching / ordering
if starts_with or search or order != 'release_order':
q = q.join(Media.library, Library.titles) \
.filter(LibraryTitle.default == True)
# if starts_with or search or order != 'release_order':
# q = q.join(Media.library, Library.titles) \
# .filter(LibraryTitle.default == True)
# Add search filters
filter_or = []
if starts_with:
starts_with = toUnicode(starts_with.lower())
if starts_with in ascii_lowercase:
filter_or.append(LibraryTitle.simple_title.startswith(starts_with))
else:
ignore = []
for letter in ascii_lowercase:
ignore.append(LibraryTitle.simple_title.startswith(toUnicode(letter)))
filter_or.append(not_(or_(*ignore)))
# filter_or = []
# if starts_with:
# starts_with = toUnicode(starts_with.lower())
# if starts_with in ascii_lowercase:
# filter_or.append(LibraryTitle.simple_title.startswith(starts_with))
# else:
# ignore = []
# for letter in ascii_lowercase:
# ignore.append(LibraryTitle.simple_title.startswith(toUnicode(letter)))
# filter_or.append(not_(or_(*ignore)))
if search:
filter_or.append(LibraryTitle.simple_title.like('%%' + search + '%%'))
search_ids = db.get_many('media_title', search)
for search_id in search_ids:
print search_id
if len(filter_or) > 0:
q = q.filter(or_(*filter_or))
#if len(filter_or) > 0:
# pass #q = q.filter(or_(*filter_or))
total_count = q.count()
total_count = len(media_ids)
if total_count == 0:
return 0, []
if order == 'release_order':
q = q.order_by(desc(Release.last_edit))
else:
q = q.order_by(asc(LibraryTitle.simple_title))
# if order == 'release_order':
# q = q.order_by(desc(Release.last_edit))
# else:
# q = q.order_by(asc(LibraryTitle.simple_title))
if limit_offset:
splt = splitString(limit_offset) if isinstance(limit_offset, (str, unicode)) else limit_offset
limit = splt[0]
offset = 0 if len(splt) is 1 else splt[1]
q = q.limit(limit).offset(offset)
# if limit_offset:
# splt = splitString(limit_offset) if isinstance(limit_offset, (str, unicode)) else limit_offset
# limit = splt[0]
# offset = 0 if len(splt) is 1 else splt[1]
# q = q.limit(limit).offset(offset)
# Get all media_ids in sorted order
media_ids = [m.id for m in q.all()]
# media_ids = [m.id for m in q.all()]
# List release statuses
releases = db.query(Release) \
.filter(Release.movie_id.in_(media_ids)) \
.all()
# releases = db.query(Release) \
# .filter(Release.movie_id.in_(media_ids)) \
# .all()
release_statuses = dict((m, set()) for m in media_ids)
releases_count = dict((m, 0) for m in media_ids)
for release in releases:
release_statuses[release.movie_id].add('%d,%d' % (release.status_id, release.quality_id))
releases_count[release.movie_id] += 1
# release_statuses = dict((m, set()) for m in media_ids)
# releases_count = dict((m, 0) for m in media_ids)
# for release in releases:
# release_statuses[release.movie_id].add('%d,%d' % (release.status_id, release.quality_id))
# releases_count[release.movie_id] += 1
# Get main movie data
q2 = db.query(Media) \
.options(joinedload_all('library.titles')) \
.options(joinedload_all('library.files')) \
.options(joinedload_all('status')) \
.options(joinedload_all('files'))
# q2 = db.query(Media) \
# .options(joinedload_all('library.titles')) \
# .options(joinedload_all('library.files')) \
# .options(joinedload_all('status')) \
# .options(joinedload_all('files'))
q2 = q2.filter(Media.id.in_(media_ids))
# q2 = q2.filter(Media.id.in_(media_ids))
results = q2.all()
# results = q2.all()
# Create dict by movie id
movie_dict = {}
for movie in results:
movie_dict[movie.id] = movie
# medias = {}
# for media in media_ids:
# movie_dict[movie.id] = movie
# List movies based on media_ids order
movies = []
medias = []
for media_id in media_ids:
releases = []
for r in release_statuses.get(media_id):
x = splitString(r)
releases.append({'status_id': x[0], 'quality_id': x[1]})
# Merge releases with movie dict
movies.append(mergeDicts(movie_dict[media_id].to_dict({
media = db.run('media', 'to_dict', media_id, {
'library': {'titles': {}, 'files': {}},
'files': {},
}), {
'releases': releases,
'releases_count': releases_count.get(media_id),
}))
})
pass #db.close()
return total_count, movies
media['releases'] = []
for r in db.get_many('release', media_id, with_doc = True):
media['releases'].append(r)
# Merge releases with movie dict
medias.append(media)
# medias.append(mergeDicts(movie_dict[media_id].to_dict({
# 'library': {'titles': {}, 'files': {}},
# 'files': {},
# }), {
# 'releases': releases,
# 'releases_count': releases_count.get(media_id),
# }))
print time.time() - start
return total_count, medias
def listView(self, **kwargs):
@@ -424,19 +432,18 @@ class MediaPlugin(MediaBase):
db.commit()
deleted = True
else:
done_status = fireEvent('status.get', 'done', single = True)
total_releases = len(media.releases)
total_deleted = 0
new_movie_status = None
for release in media.releases:
if delete_from in ['wanted', 'snatched', 'late']:
if release.status_id != done_status.get('id'):
if release.get('status') != 'done':
db.delete(release)
total_deleted += 1
new_movie_status = 'done'
elif delete_from == 'manage':
if release.status_id == done_status.get('id'):
if release.get('status') == 'done':
db.delete(release)
total_deleted += 1
new_movie_status = 'active'
@@ -447,9 +454,8 @@ class MediaPlugin(MediaBase):
db.commit()
deleted = True
elif new_movie_status:
new_status = fireEvent('status.get', new_movie_status, single = True)
media.profile_id = None
media.status_id = new_status.get('id')
media['status'] = new_status
db.commit()
else:
fireEvent('media.restatus', media.id, single = True)
@@ -458,9 +464,6 @@ class MediaPlugin(MediaBase):
fireEvent('notify.frontend', type = 'movie.deleted', data = media.to_dict())
except:
log.error('Failed deleting media: %s', traceback.format_exc())
db.rollback()
finally:
pass #db.close()
return True
@@ -483,8 +486,6 @@ class MediaPlugin(MediaBase):
def restatus(self, media_id):
active_status, done_status = fireEvent('status.get', ['active', 'done'], single = True)
try:
db = get_session()
@@ -494,17 +495,17 @@ class MediaPlugin(MediaBase):
return False
log.debug('Changing status for %s', m.library.titles[0].title)
if not m.profile:
m.status_id = done_status.get('id')
if not m['profile_id']:
m['status'] = 'done'
else:
move_to_wanted = True
for t in m.profile.types:
for release in m.releases:
if t.quality.identifier is release.quality.identifier and (release.status_id is done_status.get('id') and t.finish):
if t.quality.identifier is release.quality.identifier and (release.get('status') == 'done' and t.finish):
move_to_wanted = False
m.status_id = active_status.get('id') if move_to_wanted else done_status.get('id')
m['status'] = 'active' if move_to_wanted else 'done'
db.commit()
+1 -1
View File
@@ -25,7 +25,7 @@ class Search(Plugin):
}"""}
})
addEvent('app.load2', self.addSingleSearches)
addEvent('app.load', self.addSingleSearches)
def search(self, q = '', types = None, **kwargs):
@@ -28,7 +28,7 @@ class SearcherBase(Plugin):
fireEvent('schedule.cron', '%s.searcher.all' % _type, self.searchAll,
day = self.conf('cron_day'), hour = self.conf('cron_hour'), minute = self.conf('cron_minute'))
addEvent('app.load2', setCrons)
addEvent('app.load', setCrons)
addEvent('setting.save.%s_searcher.cron_day.after' % _type, setCrons)
addEvent('setting.save.%s_searcher.cron_hour.after' % _type, setCrons)
addEvent('setting.save.%s_searcher.cron_minute.after' % _type, setCrons)
@@ -48,7 +48,7 @@ class Searcher(SearcherBase):
results = []
for search_protocol in protocols:
protocol_results = fireEvent('provider.search.%s.%s' % (search_protocol, media['type']), media, quality, merge = True)
protocol_results = fireEvent('provider.search.%s.%s' % (search_protocol, media.get('type')), media, quality, merge = True)
if protocol_results:
results += protocol_results
+174 -19
View File
@@ -1,13 +1,13 @@
import traceback
from couchpotato import get_session, get_db
from couchpotato import get_db
from couchpotato.api import addApiView
from couchpotato.core.event import fireEvent, fireEventAsync, addEvent
from couchpotato.core.helpers.encoding import toUnicode
from couchpotato.core.helpers.variable import splitString, tryInt, getTitle
from couchpotato.core.logger import CPLog
from couchpotato.core.media.movie import MovieTypeBase
from couchpotato.core.settings.model import Media
import time
import six
log = CPLog(__name__)
@@ -42,8 +42,10 @@ class MovieBase(MovieTypeBase):
})
addEvent('movie.add', self.add)
addEvent('movie.update_info', self.updateInfo)
addEvent('movie.update_release_dates', self.updateReleaseDate)
def add(self, params = None, force_readd = True, search_after = True, update_library = False, status_id = None):
def add(self, params = None, force_readd = True, search_after = True, update_library = False, status = None):
if not params: params = {}
if not params.get('identifier'):
@@ -62,27 +64,55 @@ class MovieBase(MovieTypeBase):
except:
pass
# library = fireEvent('library.add.movie', single = True, attrs = params, update_after = update_library)
info = fireEvent('movie.info', merge = True, extended = False, identifier = params.get('identifier'))
# Set default title
default_title = toUnicode(info.get('title'))
titles = info.get('titles', [])
counter = 0
def_title = None
for title in titles:
if (len(default_title) == 0 and counter == 0) or len(titles) == 1 or title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == six.u('') and toUnicode(titles[0]) == title):
def_title = toUnicode(title)
break
counter += 1
if not def_title:
def_title = toUnicode(titles[0])
# Default profile and category
default_profile = fireEvent('profile.default', single = True)
cat_id = params.get('category_id')
try:
db = get_db()
media = {
'_t': 'media',
'type': 'movie',
'title': def_title,
'identifier': params.get('identifier'),
'status': status if status else 'active',
'profile_id': params.get('profile_id', default_profile.get('_id')),
'category_id': tryInt(cat_id) if cat_id is not None and tryInt(cat_id) > 0 else None,
}
new = False
try:
m = db.get('movie', params.get('identifier'), with_doc = True)['doc']
m = db.get('media', params.get('identifier'), with_doc = True)['doc']
except:
new = True
m = db.insert({
'type': 'movie',
'identifier': params.get('identifier'),
'status': status_id if status_id else 'active',
'profile_id': params.get('profile_id', default_profile.get('id')),
'category_id': tryInt(cat_id) if cat_id is not None and tryInt(cat_id) > 0 else None,
})
m = db.insert(media)
# Update dict to be usable
m.update(media)
# Update movie info
try: del info['in_wanted']
except: pass
try: del info['in_library']
except: pass
m['info'] = info
added = True
do_search = False
@@ -92,7 +122,7 @@ class MovieBase(MovieTypeBase):
if search_after:
onComplete = self.createOnComplete(m['_id'])
# fireEventAsync('library.update.movie', params.get('identifier'), default_title = params.get('title', ''), on_complete = onComplete)
fireEventAsync('movie.update_info', m['_id'], default_title = params.get('title', ''), on_complete = onComplete)
search_after = False
elif force_readd:
@@ -106,24 +136,24 @@ class MovieBase(MovieTypeBase):
fireEvent('release.delete', release['_id'], single = True)
m['profile_id'] = params.get('profile_id', default_profile.get('id'))
m['category_id'] = tryInt(cat_id) if cat_id is not None and tryInt(cat_id) > 0 else (m['category_id'] or None)
m['category_id'] = tryInt(cat_id) if cat_id is not None and tryInt(cat_id) > 0 else (m.get('category_id') or None)
else:
log.debug('Movie already exists, not updating: %s', params)
added = False
if force_readd:
m['status'] = status_id if status_id else 'active'
m['last_edit'] = int(time.time())
do_search = True
db.update(m)
if added:
db.update(m)
# Remove releases
for rel in db.run('release', 'for_media', m['_id']):
if rel['status'] is 'available':
db.delete(rel)
movie_dict = db.run('movie', 'to_dict', m['_id'])
movie_dict = db.run('media', 'to_dict', m['_id'])
if do_search and search_after:
onComplete = self.createOnComplete(m['_id'])
@@ -137,7 +167,7 @@ class MovieBase(MovieTypeBase):
if title:
message = 'Successfully added "%s" to your wanted list.' % title
else:
message = 'Succesfully added to your wanted list.'
message = 'Successfully added to your wanted list.'
fireEvent('notify.frontend', type = 'movie.added', data = movie_dict, message = message)
return movie_dict
@@ -161,7 +191,7 @@ class MovieBase(MovieTypeBase):
for media_id in ids:
try:
m = db.get('media', media_id)
m = db.get('id', media_id)
m['profile_id'] = kwargs.get('profile_id')
cat_id = kwargs.get('category_id')
@@ -197,3 +227,128 @@ class MovieBase(MovieTypeBase):
return {
'success': False,
}
def updateInfo(self, media_id = None, identifier = None, default_title = None, extended = False):
"""
Update movie information inside media['doc']['info']
@param media_id: document id
@param default_title: default title, if empty, use first one or existing one
@param extended: update with extended info (parses more info, actors, images from some info providers)
@return: dict, with media
"""
if self.shuttingDown():
return
try:
db = get_db()
if media_id:
media = db.get('id', media_id)
else:
media = db.get('media', identifier, with_doc = True)['doc']
info = fireEvent('movie.info', merge = True, extended = extended, identifier = media.get('identifier'))
# Don't need those here
try: del info['in_wanted']
except: pass
try: del info['in_library']
except: pass
if not info or len(info) == 0:
log.error('Could not update, no movie info to work with: %s', media.get('identifier'))
return False
# Update basic info
media['info'] = info
titles = info.get('titles', [])
log.debug('Adding titles: %s', titles)
counter = 0
def_title = None
for title in titles:
if (len(default_title) == 0 and counter == 0) or len(titles) == 1 or title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == six.u('') and toUnicode(titles[0]) == title):
def_title = toUnicode(title)
break
counter += 1
if not def_title:
def_title = toUnicode(titles[0])
media = {
'title': def_title,
}
# Files
images = info.get('images', [])
media['files'] = media.get('filed', [])
for image_type in ['poster']:
for image in images.get(image_type, []):
if not isinstance(image, (str, unicode)):
continue
file_path = fireEvent('file.download', url = image, single = True)
media['files'].append({
'type': 'image_%s' % image_type,
'path': file_path
})
db.update(media)
return media
except:
log.error('Failed update media: %s', traceback.format_exc())
return {}
def updateReleaseDate(self, media_id):
"""
Update releasedate (eta) info only
@param media_id: document id
@return: dict, with dates dvd, theater, bluray, expires
"""
try:
db = get_db()
media = db.get('id', media_id)
if not media.get('info'):
info_dict = self.updateInfo(media_id)
dates = info_dict.get('info', {}).get('release_date')
else:
dates = media.get('info').get('release_date')
if dates and (dates.get('expires', 0) < time.time() or dates.get('expires', 0) > time.time() + (604800 * 4)) or not dates:
dates = fireEvent('movie.info.release_date', identifier = media['identifier'], merge = True)
media['info'].update({'release_date': dates})
db.update(media)
return dates
except:
log.error('Failed updating release dates: %s', traceback.format_exc())
return {}
def simplifyTitle(self, title):
"""
Removes all special chars from a title so it's easier to make sortable
@param title: media title
@return: string, simplified title
"""
title = toUnicode(title)
nr_prefix = '' if title[0] in ascii_letters else '#'
title = simplifyString(title)
for prefix in ['the ']:
if prefix == title[:len(prefix)]:
title = title[len(prefix):]
break
return nr_prefix + title
@@ -191,11 +191,9 @@ MA.Release = new Class({
self.releases.each(function(release){
var status = Status.get(release.status_id),
quality = Quality.getProfile(release.quality_id) || {},
var quality = Quality.getProfile(release.quality_id) || {},
info = release.info,
provider = self.get(release, 'provider') + (release.info['provider_extra'] ? self.get(release, 'provider_extra') : '');
release.status = status;
var release_name = self.get(release, 'name');
if(release.files && release.files.length > 0){
@@ -215,7 +213,7 @@ MA.Release = new Class({
'id': 'release_'+release.id
}).adopt(
new Element('span.name', {'text': release_name, 'title': release_name}),
new Element('span.status', {'text': status.identifier, 'class': 'release_status '+status.identifier}),
new Element('span.status', {'text': status.identifier, 'class': 'release_status '+release.status}),
new Element('span.quality', {'text': quality.get('label') || 'n/a'}),
new Element('span.size', {'text': release.info['size'] ? Math.floor(self.get(release, 'size')) : 'n/a'}),
new Element('span.age', {'text': self.get(release, 'age')}),
@@ -257,22 +255,20 @@ MA.Release = new Class({
if(notification.data.id != release.id) return;
var q = self.movie.quality.getElement('.q_id' + release.quality_id),
status = Status.get(release.status_id),
new_status = Status.get(notification.data.status_id);
new_status = notification.data.status;
release.status_id = new_status.id
release.el.set('class', 'item ' + new_status.identifier);
release.el.set('class', 'item ' + new_status);
var status_el = release.el.getElement('.release_status');
status_el.set('class', 'release_status ' + new_status.identifier);
status_el.set('class', 'release_status ' + new_status);
status_el.set('text', new_status.identifier);
if(!q && (new_status.identifier == 'snatched' || new_status.identifier == 'seeding' || new_status.identifier == 'done'))
if(!q && (new_status == 'snatched' || new_status == 'seeding' || new_status == 'done'))
var q = self.addQuality(release.quality_id);
if(new_status && q && !q.hasClass(new_status.identifier)) {
q.removeClass(status.identifier).addClass(new_status.identifier);
q.set('title', q.get('title').replace(status.label, new_status.label));
if(q && !q.hasClass(new_status)) {
q.removeClass(release.status).addClass(new_status);
q.set('title', q.get('title').replace(status, new_status));
}
}
@@ -344,12 +340,10 @@ MA.Release = new Class({
self.movie.data.releases.each(function(release){
if(has_available && has_snatched) return;
var status = Status.get(release.status_id);
if(['snatched', 'downloaded', 'seeding'].contains(status.identifier))
if(['snatched', 'downloaded', 'seeding'].contains(release.status))
has_snatched = true;
if(['available'].contains(status.identifier))
if(['available'].contains(release.status))
has_available = true;
});
@@ -726,10 +720,10 @@ MA.Readd = new Class({
create: function(){
var self = this;
var movie_done = Status.get(self.movie.data.status_id).identifier == 'done';
var movie_done = self.movie.data.status == 'done';
if(!movie_done)
var snatched = self.movie.data.releases.filter(function(release){
return release.status && (release.status.identifier == 'snatched' || release.status.identifier == 'downloaded' || release.status.identifier == 'done');
return release.status && (release.status == 'snatched' || release.status == 'downloaded' || release.status == 'done');
}).length;
if(movie_done || snatched && snatched > 0)
@@ -924,4 +918,4 @@ MA.Files = new Class({
self.movie.slide('in', self.options_container);
},
});
});
@@ -59,7 +59,7 @@ var Movie = new Class({
if(!self.data.releases)
self.data.releases = [];
self.data.releases.push({'quality_id': data.quality_id, 'status_id': data.status_id});
self.data.releases.push({'quality_id': data.quality_id, 'status': data.status});
self.updateReleases();
}
}
@@ -146,8 +146,7 @@ var Movie = new Class({
create: function(){
var self = this;
var s = Status.get(self.get('status_id'));
self.el.addClass('status_'+s.identifier);
self.el.addClass('status_'+self.get('status'));
self.el.adopt(
self.select_checkbox = new Element('input[type=checkbox].inlay', {
@@ -157,7 +156,7 @@ var Movie = new Class({
}
}
}),
self.thumbnail = File.Select.single('poster', self.data.library.files),
self.thumbnail = File.Select.single('poster', self.data.files || []),
self.data_container = new Element('div.data.inlay.light').adopt(
self.info_container = new Element('div.info').adopt(
new Element('div.title').adopt(
@@ -165,11 +164,11 @@ var Movie = new Class({
'text': self.getTitle() || 'n/a'
}),
self.year = new Element('div.year', {
'text': self.data.library.year || 'n/a'
'text': self.data.info.year || 'n/a'
})
),
self.description = new Element('div.description', {
'text': self.data.library.plot
'text': self.data.info.plot
}),
self.quality = new Element('div.quality', {
'events': {
@@ -221,14 +220,14 @@ var Movie = new Class({
self.data.releases.each(function(release){
var q = self.quality.getElement('.q_id'+ release.quality_id),
status = Status.get(release.status_id);
status = release.status;
if(!q && (status.identifier == 'snatched' || status.identifier == 'seeding' || status.identifier == 'done'))
if(!q && (status == 'snatched' || status == 'seeding' || status == 'done'))
var q = self.addQuality(release.quality_id)
if (status && q && !q.hasClass(status.identifier)){
q.addClass(status.identifier);
q.set('title', (q.get('title') ? q.get('title') : '') + ' status: '+ status.label)
if (q && !q.hasClass(status)){
q.addClass(status);
q.set('title', (q.get('title') ? q.get('title') : '') + ' status: '+ status)
}
});
@@ -249,16 +248,10 @@ var Movie = new Class({
getTitle: function(){
var self = this;
var titles = self.data.library.titles;
var title = titles.filter(function(title){
return title['default']
}).pop()
if(title)
return self.getUnprefixedTitle(title.title)
else if(titles.length > 0)
return self.getUnprefixedTitle(titles[0].title)
if(self.data.title)
return self.getUnprefixedTitle(self.data.title)
else if(self.data.info.titles.length > 0)
return self.getUnprefixedTitle(self.data.info.titles[0])
return 'Unknown movie'
},
@@ -313,7 +306,7 @@ var Movie = new Class({
},
get: function(attr){
return this.data[attr] || this.data.library[attr]
return this.data[attr] || this.data.info[attr]
},
select: function(bool){
@@ -197,8 +197,8 @@ Block.Search.MovieItem = new Class({
profiles.each(function(profile){
new Element('option', {
'value': profile.id ? profile.id : profile.data.id,
'text': profile.label ? profile.label : profile.data.label
'value': profile.get('_id'),
'text': profile.get('label')
}).inject(self.profile_select)
});
Whitespace-only changes.
@@ -1,7 +0,0 @@
from .main import MovieLibraryPlugin
def start():
return MovieLibraryPlugin()
config = []
@@ -1,193 +0,0 @@
from couchpotato import get_session
from couchpotato.core.event import addEvent, fireEventAsync, fireEvent
from couchpotato.core.helpers.encoding import toUnicode, simplifyString
from couchpotato.core.logger import CPLog
from couchpotato.core.media._base.library import LibraryBase
from couchpotato.core.settings.model import Library, LibraryTitle, File
from string import ascii_letters
import time
import traceback
import six
log = CPLog(__name__)
class MovieLibraryPlugin(LibraryBase):
default_dict = {'titles': {}, 'files': {}}
def __init__(self):
addEvent('library.add.movie', self.add)
addEvent('library.update.movie', self.update)
addEvent('library.update.movie.release_date', self.updateReleaseDate)
def add(self, attrs = None, update_after = True):
if not attrs: attrs = {}
primary_provider = attrs.get('primary_provider', 'imdb')
try:
db = get_session()
l = db.query(Library).filter_by(identifier = attrs.get('identifier')).first()
if not l:
status = fireEvent('status.get', 'needs_update', single = True)
l = Library(
year = attrs.get('year'),
identifier = attrs.get('identifier'),
plot = toUnicode(attrs.get('plot')),
tagline = toUnicode(attrs.get('tagline')),
status_id = status.get('id'),
info = {}
)
title = LibraryTitle(
title = toUnicode(attrs.get('title')),
simple_title = self.simplifyTitle(attrs.get('title')),
)
l.titles.append(title)
db.add(l)
db.commit()
# Update library info
if update_after is not False:
handle = fireEventAsync if update_after is 'async' else fireEvent
handle('library.update.movie', identifier = l.identifier, default_title = toUnicode(attrs.get('title', '')))
library_dict = l.to_dict(self.default_dict)
return library_dict
except:
log.error('Failed adding media: %s', traceback.format_exc())
db.rollback()
finally:
pass #db.close()
return {}
def update(self, identifier, default_title = '', extended = False):
if self.shuttingDown():
return
try:
db = get_session()
library = db.query(Library).filter_by(identifier = identifier).first()
done_status = fireEvent('status.get', 'done', single = True)
info = fireEvent('movie.info', merge = True, extended = extended, identifier = identifier)
# Don't need those here
try: del info['in_wanted']
except: pass
try: del info['in_library']
except: pass
if not info or len(info) == 0:
log.error('Could not update, no movie info to work with: %s', identifier)
return False
# Main info
library.plot = toUnicode(info.get('plot', ''))
library.tagline = toUnicode(info.get('tagline', ''))
library.year = info.get('year', 0)
library.status_id = done_status.get('id')
library.info.update(info)
db.commit()
# Titles
[db.delete(title) for title in library.titles]
db.commit()
titles = info.get('titles', [])
log.debug('Adding titles: %s', titles)
counter = 0
def_title = None
for title in titles:
if (len(default_title) == 0 and counter == 0) or len(titles) == 1 or title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == six.u('') and toUnicode(titles[0]) == title):
def_title = toUnicode(title)
break
counter += 1
if not def_title:
def_title = toUnicode(titles[0])
for title in titles:
if not title:
continue
title = toUnicode(title)
t = LibraryTitle(
title = title,
simple_title = self.simplifyTitle(title),
default = title == def_title
)
library.titles.append(t)
db.commit()
# Files
images = info.get('images', [])
library['files'] = []
for image_type in ['poster']:
for image in images.get(image_type, []):
if not isinstance(image, (str, unicode)):
continue
file_path = fireEvent('file.download', url = image, single = True)
# TODO: save in movie doc
library['files'].append({
'type': 'image_%s' % image_type,
'path': file_path
})
library_dict = library.to_dict(self.default_dict)
return library_dict
except:
log.error('Failed update media: %s', traceback.format_exc())
return {}
def updateReleaseDate(self, identifier):
try:
db = get_session()
library = db.query(Library).filter_by(identifier = identifier).first()
if not library.info:
library_dict = self.update(identifier)
dates = library_dict.get('info', {}).get('release_date')
else:
dates = library.info.get('release_date')
if dates and (dates.get('expires', 0) < time.time() or dates.get('expires', 0) > time.time() + (604800 * 4)) or not dates:
dates = fireEvent('movie.release_date', identifier = identifier, merge = True)
library.info.update({'release_date': dates})
db.commit()
return dates
except:
log.error('Failed updating release dates: %s', traceback.format_exc())
db.rollback()
finally:
pass #db.close()
return {}
def simplifyTitle(self, title):
title = toUnicode(title)
nr_prefix = '' if title[0] in ascii_letters else '#'
title = simplifyString(title)
for prefix in ['the ']:
if prefix == title[:len(prefix)]:
title = title[len(prefix):]
break
return nr_prefix + title
+34 -28
View File
@@ -1,4 +1,4 @@
from couchpotato import get_session, get_db
from couchpotato import get_db
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
from couchpotato.core.helpers.encoding import simplifyString
@@ -6,7 +6,6 @@ from couchpotato.core.helpers.variable import getTitle, possibleTitles, getImdb
from couchpotato.core.logger import CPLog
from couchpotato.core.media._base.searcher.base import SearcherBase
from couchpotato.core.media.movie import MovieTypeBase
from couchpotato.core.settings.model import Media, Release
from couchpotato.environment import Env
from datetime import date
import random
@@ -51,7 +50,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
})
if self.conf('run_on_launch'):
addEvent('app.load2', self.searchAll)
addEvent('app.load', self.searchAll)
def searchAllView(self, **kwargs):
@@ -94,7 +93,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
self.single(movie_dict, search_protocols)
except IndexError:
log.error('Forcing library update for %s, if you see this often, please report: %s', (movie['identifier'], traceback.format_exc()))
fireEvent('library.update.movie', movie['identifier'])
fireEvent('movie.update_info', movie['_id'])
except:
log.error('Search failed for %s: %s', (movie['identifier'], traceback.format_exc()))
@@ -111,10 +110,6 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
def single(self, movie, search_protocols = None, manual = False):
# movies don't contain 'type' yet, so just set to default here
if 'type' not in movie:
movie['type'] = 'movie'
# Find out search type
try:
if not search_protocols:
@@ -127,7 +122,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
return
pre_releases = fireEvent('quality.pre_releases', single = True)
release_dates = fireEvent('library.update.movie.release_date', identifier = movie['library']['identifier'], merge = True)
release_dates = fireEvent('movie.update_release_date', media_id = movie['_id'], merge = True)
found_releases = []
too_early_to_search = []
@@ -142,39 +137,50 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
db = get_db()
profile = db.get('id', movie['profile_id'])
quality_order = fireEvent('quality.order', single = True)
media_releases = db.get_many('release', movie['_id'])
ret = False
for quality_type in movie['profile']['types']:
if not self.conf('always_search') and not self.couldBeReleased(quality_type['quality']['identifier'] in pre_releases, release_dates, movie['year']):
too_early_to_search.append(quality_type['quality']['identifier'])
for q_identifier in profile.get('qualities'):
index = profile['qualities'].index(q_identifier)
quality_custom = {
'quality': q_identifier,
'finish': profile['finish'][index],
'wait_for': profile['wait_for'][index]
}
if not self.conf('always_search') and not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year']):
too_early_to_search.append(q_identifier)
continue
has_better_quality = 0
# See if better quality is available
for release in movie['releases']:
if release['quality']['order'] <= quality_type['quality']['order'] and release['status'] not in ['available', 'ignored', 'failed']:
for release in media_releases:
if quality_order.index(release['quality']) <= quality_order.index(q_identifier) and release['status'] not in ['available', 'ignored', 'failed']:
has_better_quality += 1
# Don't search for quality lower then already available.
if has_better_quality is 0:
log.info('Search for %s in %s', (default_title, quality_type['quality']['label']))
quality = fireEvent('quality.single', identifier = quality_type['quality']['identifier'], single = True)
quality = fireEvent('quality.single', identifier = q_identifier, single = True)
log.info('Search for %s in %s', (default_title, quality['label']))
results = fireEvent('searcher.search', search_protocols, movie, quality, single = True) or []
if len(results) == 0:
log.debug('Nothing found for %s in %s', (default_title, quality_type['quality']['label']))
log.debug('Nothing found for %s in %s', (default_title, quality['label']))
# Check if movie isn't deleted while searching
if not fireEvent('media.get', movie.get('_id'), single = True):
break
# Add them to this movie releases list
found_releases += fireEvent('release.create_from_search', results, movie, quality_type, single = True)
found_releases += fireEvent('release.create_from_search', results, movie, quality, single = True)
# Try find a valid result and download it
if fireEvent('release.try_download_result', results, movie, quality_type, manual, single = True):
if fireEvent('release.try_download_result', results, movie, quality_custom, manual, single = True):
ret = True
# Remove releases that aren't found anymore
@@ -183,7 +189,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
fireEvent('release.delete', release.get('_id'), single = True)
else:
log.info('Better quality (%s) already available or snatched for %s', (quality_type['quality']['label'], default_title))
log.info('Better quality (%s) already available or snatched for %s', (quality['label'], default_title))
fireEvent('media.restatus', movie['_id'])
break
@@ -219,7 +225,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True)
# Contains lower quality string
if fireEvent('searcher.contains_other_quality', nzb, movie_year = media['library']['year'], preferred_quality = preferred_quality, single = True):
if fireEvent('searcher.contains_other_quality', nzb, movie_year = media['info']['year'], preferred_quality = preferred_quality, single = True):
log.info2('Wrong: %s, looking for %s', (nzb['name'], quality['label']))
return False
@@ -249,23 +255,23 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
return True
# Check if nzb contains imdb link
if getImdb(nzb.get('description', '')) == media['library']['identifier']:
if getImdb(nzb.get('description', '')) == media['identifier']:
return True
for raw_title in media['library']['titles']:
for movie_title in possibleTitles(raw_title['title']):
for raw_title in media['info']['titles']:
for movie_title in possibleTitles(raw_title):
movie_words = re.split('\W+', simplifyString(movie_title))
if fireEvent('searcher.correct_name', nzb['name'], movie_title, single = True):
# if no IMDB link, at least check year range 1
if len(movie_words) > 2 and fireEvent('searcher.correct_year', nzb['name'], media['library']['year'], 1, single = True):
if len(movie_words) > 2 and fireEvent('searcher.correct_year', nzb['name'], media['info']['year'], 1, single = True):
return True
# if no IMDB link, at least check year
if len(movie_words) <= 2 and fireEvent('searcher.correct_year', nzb['name'], media['library']['year'], 0, single = True):
if len(movie_words) <= 2 and fireEvent('searcher.correct_year', nzb['name'], media['info']['year'], 0, single = True):
return True
log.info("Wrong: %s, undetermined naming. Looking for '%s (%s)'", (nzb['name'], media_title, media['library']['year']))
log.info("Wrong: %s, undetermined naming. Looking for '%s (%s)'", (nzb['name'], media_title, media['info']['year']))
return False
def couldBeReleased(self, is_pre_release, dates, year = None):
@@ -337,7 +343,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
def getSearchTitle(self, media):
if media['type'] == 'movie':
return getTitle(media['library'])
return getTitle(media)
class SearchSetupError(Exception):
pass
@@ -89,8 +89,6 @@ class Suggestion(Plugin):
# Get new results and add them
if len(new_suggestions) - 1 < limit:
active_status, done_status = fireEvent('status.get', ['active', 'done'], single = True)
db = get_session()
active_movies = db.query(Media) \
.join(Library) \