Example #1
0
    def addMovies(self):

        movies = fireEvent('automation.get_movies', merge=True)
        movie_ids = []

        for imdb_id in movies:

            if self.shuttingDown():
                break

            prop_name = 'automation.added.%s' % imdb_id
            added = Env.prop(prop_name, default=False)
            if not added:
                added_movie = fireEvent('movie.add',
                                        params={'identifier': imdb_id},
                                        force_readd=False,
                                        search_after=False,
                                        update_after=True,
                                        single=True)
                if added_movie:
                    movie_ids.append(added_movie['_id'])
                Env.prop(prop_name, True)

        for movie_id in movie_ids:

            if self.shuttingDown():
                break

            movie_dict = fireEvent('media.get', movie_id, single=True)
            if movie_dict:
                fireEvent('movie.searcher.single', movie_dict)

        return True
Example #2
0
    def getDomain(self, url=''):

        if not self.domain:
            for proxy in self.proxy_list:

                prop_name = 'tpb_proxy.%s' % proxy
                last_check = float(Env.prop(prop_name, default=0))
                if last_check > time.time() - 1209600:
                    continue

                data = ''
                try:
                    data = self.urlopen(proxy, timeout=3)
                except:
                    log.debug('Failed tpb proxy %s', proxy)

                if 'title="Pirate Search"' in data:
                    log.debug('Using proxy: %s', proxy)
                    self.domain = proxy
                    break

                Env.prop(prop_name, time.time())

        if not self.domain:
            log.error(
                'No TPB proxies left, please add one in settings, or let us know which one to add on the forum.'
            )
            return None

        return cleanHost(self.domain).rstrip('/') + url
Example #3
0
    def updateLibrary(self, full = True):
        last_update = float(Env.prop('manage.last_update', default = 0))

        if self.isDisabled() or (last_update > time.time() - 20):
            return

        directories = self.directories()
        added_identifiers = []

        for directory in directories:

            if not os.path.isdir(directory):
                if len(directory) > 0:
                    log.error('Directory doesn\'t exist: %s' % directory)
                continue

            log.info('Updating manage library: %s' % directory)
            identifiers = fireEvent('scanner.folder', folder = directory, newer_than = last_update, single = True)
            added_identifiers.extend(identifiers)

            # Break if CP wants to shut down
            if self.shuttingDown():
                break

        # If cleanup option is enabled, remove offline files from database
        if self.conf('cleanup') and full and not self.shuttingDown():

            # Get movies with done status
            done_movies = fireEvent('movie.list', status = 'done', single = True)

            for done_movie in done_movies:
                if done_movie['library']['identifier'] not in added_identifiers:
                    fireEvent('movie.delete', movie_id = done_movie['id'])

        Env.prop('manage.last_update', time.time())
Example #4
0
    def suggestView(self, limit = 6, **kwargs):

        movies = splitString(kwargs.get('movies', ''))
        ignored = splitString(kwargs.get('ignored', ''))
        seen = splitString(kwargs.get('seen', ''))

        cached_suggestion = self.getCache('suggestion_cached')
        if cached_suggestion:
            suggestions = cached_suggestion
        else:

            if not movies or len(movies) == 0:
                db = get_session()
                active_movies = db.query(Movie) \
                    .options(joinedload_all('library')) \
                    .filter(or_(*[Movie.status.has(identifier = s) for s in ['active', 'done']])).all()
                movies = [x.library.identifier for x in active_movies]

            if not ignored or len(ignored) == 0:
                ignored = splitString(Env.prop('suggest_ignore', default = ''))
            if not seen or len(seen) == 0:
                movies.extend(splitString(Env.prop('suggest_seen', default = '')))

            suggestions = fireEvent('movie.suggest', movies = movies, ignore = ignored, single = True)
            self.setCache('suggestion_cached', suggestions, timeout = 6048000) # Cache for 10 weeks

        return {
            'success': True,
            'count': len(suggestions),
            'suggestions': suggestions[:int(limit)]
        }
Example #5
0
    def addMovies(self):

        movies = fireEvent('automation.get_movies', merge = True)
        movie_ids = []

        for imdb_id in movies:

            if self.shuttingDown():
                break

            prop_name = 'automation.added.%s' % imdb_id
            added = Env.prop(prop_name, default = False)
            if not added:
                added_movie = fireEvent('movie.add', params = {'identifier': imdb_id}, force_readd = False, search_after = False, update_library = True, single = True)
                if added_movie:
                    movie_ids.append(added_movie['id'])
                Env.prop(prop_name, True)

        for movie_id in movie_ids:

            if self.shuttingDown():
                break

            movie_dict = fireEvent('media.get', movie_id, single = True)
            fireEvent('movie.searcher.single', movie_dict)

        return True
Example #6
0
    def getDomain(self, url=''):

        forced_domain = self.conf('domain')
        if forced_domain:
            return cleanHost(forced_domain).rstrip('/') + url

        if not self.proxy_domain:
            for proxy in self.proxy_list:

                prop_name = 'proxy.%s' % proxy
                last_check = float(Env.prop(prop_name, default=0))
                if last_check > time.time() - 1209600:
                    continue

                data = ''
                try:
                    data = self.urlopen(proxy, timeout=3, show_error=False)
                except:
                    log.debug('Failed %s proxy %s', (self.getName(), proxy))

                if self.correctProxy(data):
                    log.debug('Using proxy for %s: %s',
                              (self.getName(), proxy))
                    self.proxy_domain = proxy
                    break

                Env.prop(prop_name, time.time())

        if not self.proxy_domain:
            log.error(
                'No %s proxies left, please add one in settings, or let us know which one to add on the forum.',
                self.getName())
            return None

        return cleanHost(self.proxy_domain).rstrip('/') + url
Example #7
0
    def search(self, name, year=None, imdb_only=False):

        try:
            cache_name = name.decode('utf-8').encode('ascii', 'ignore')
        except UnicodeEncodeError:
            cache_name = unicodedata.normalize('NFKD',
                                               name).encode('ascii', 'ignore')

        prop_name = 'automation.cached.%s.%s' % (cache_name, year)
        cached_imdb = Env.prop(prop_name, default=False)
        if cached_imdb and imdb_only:
            return cached_imdb

        result = fireEvent('movie.search',
                           q='%s %s' % (name, year if year else ''),
                           limit=1,
                           merge=True)

        if len(result) > 0:
            if imdb_only and result[0].get('imdb'):
                Env.prop(prop_name, result[0].get('imdb'))

            return result[0].get('imdb') if imdb_only else result[0]
        else:
            return None
Example #8
0
    def suggestView(self, limit = 6, **kwargs):

        movies = splitString(kwargs.get('movies', ''))
        ignored = splitString(kwargs.get('ignored', ''))
        seen = splitString(kwargs.get('seen', ''))

        cached_suggestion = self.getCache('suggestion_cached')
        if cached_suggestion:
            suggestions = cached_suggestion
        else:

            if not movies or len(movies) == 0:
                db = get_session()
                active_movies = db.query(Media) \
                    .options(joinedload_all('library')) \
                    .filter(or_(*[Media.status.has(identifier = s) for s in ['active', 'done']])).all()
                movies = [x.library.identifier for x in active_movies]

            if not ignored or len(ignored) == 0:
                ignored = splitString(Env.prop('suggest_ignore', default = ''))
            if not seen or len(seen) == 0:
                movies.extend(splitString(Env.prop('suggest_seen', default = '')))

            suggestions = fireEvent('movie.suggest', movies = movies, ignore = ignored, single = True)
            self.setCache('suggestion_cached', suggestions, timeout = 6048000) # Cache for 10 weeks

        return {
            'success': True,
            'count': len(suggestions),
            'suggestions': suggestions[:int(limit)]
        }
Example #9
0
    def suggestView(self, limit = 6, **kwargs):

        movies = splitString(kwargs.get('movies', ''))
        ignored = splitString(kwargs.get('ignored', ''))
        seen = splitString(kwargs.get('seen', ''))

        cached_suggestion = self.getCache('suggestion_cached')
        if cached_suggestion:
            suggestions = cached_suggestion
        else:

            if not movies or len(movies) == 0:
                active_movies = fireEvent('media.with_status', ['active', 'done'], single = True)
                movies = [getIdentifier(x) for x in active_movies]

            if not ignored or len(ignored) == 0:
                ignored = splitString(Env.prop('suggest_ignore', default = ''))
            if not seen or len(seen) == 0:
                movies.extend(splitString(Env.prop('suggest_seen', default = '')))

            suggestions = fireEvent('movie.suggest', movies = movies, ignore = ignored, single = True)
            self.setCache('suggestion_cached', suggestions, timeout = 6048000)  # Cache for 10 weeks

        return {
            'success': True,
            'count': len(suggestions),
            'suggestions': suggestions[:int(limit)]
        }
Example #10
0
    def suggestView(self, limit=6, **kwargs):

        movies = splitString(kwargs.get('movies', ''))
        ignored = splitString(kwargs.get('ignored', ''))
        seen = splitString(kwargs.get('seen', ''))

        cached_suggestion = self.getCache('suggestion_cached')
        if cached_suggestion:
            suggestions = cached_suggestion
        else:

            if not movies or len(movies) == 0:
                active_movies = fireEvent('media.with_status',
                                          ['active', 'done'],
                                          single=True)
                movies = [getIdentifier(x) for x in active_movies]

            if not ignored or len(ignored) == 0:
                ignored = splitString(Env.prop('suggest_ignore', default=''))
            if not seen or len(seen) == 0:
                movies.extend(splitString(Env.prop('suggest_seen',
                                                   default='')))

            suggestions = fireEvent('movie.suggest',
                                    movies=movies,
                                    ignore=ignored,
                                    single=True)
            self.setCache('suggestion_cached', suggestions,
                          timeout=6048000)  # Cache for 10 weeks

        return {
            'success': True,
            'count': len(suggestions),
            'suggestions': suggestions[:int(limit)]
        }
Example #11
0
    def autoUpdate(self):
        do_check = True

        try:
            last_check = tryInt(Env.prop(self.last_check, default = 0))
            now = tryInt(time.time())
            do_check = last_check < now - 43200

            if do_check:
                Env.prop(self.last_check, value = now)
        except:
            log.error('Failed checking last time to update: %s', traceback.format_exc())

        if do_check and self.isEnabled() and self.check() and self.conf('automatic') and not self.updater.update_failed:

            if self.updater.doUpdate():

                # Notify before restarting
                try:
                    if self.conf('notification'):
                        info = self.updater.info()
                        version_date = datetime.fromtimestamp(info['update_version']['date'])
                        fireEvent('updater.updated', 'CouchPotato: Updated to a new version with hash "%s", this version is from %s' % (info['update_version']['hash'], version_date), data = info)
                except:
                    log.error('Failed notifying for update: %s', traceback.format_exc())

                fireEventAsync('app.restart')

                return True

        return False
Example #12
0
    def getDomain(self, url = ''):

        forced_domain = self.conf('domain')
        if forced_domain:
            return cleanHost(forced_domain).rstrip('/') + url

        if not self.proxy_domain:
            for proxy in self.proxy_list:

                prop_name = 'proxy.%s' % proxy
                last_check = float(Env.prop(prop_name, default = 0))
                if last_check > time.time() - 1209600:
                    continue

                data = ''
                try:
                    data = self.urlopen(proxy, timeout = 3, show_error = False)
                except:
                    log.debug('Failed %s proxy %s', (self.getName(), proxy))

                if self.correctProxy(data):
                    log.debug('Using proxy for %s: %s', (self.getName(), proxy))
                    self.proxy_domain = proxy
                    break

                Env.prop(prop_name, time.time())

        if not self.proxy_domain:
            log.error('No %s proxies left, please add one in settings, or let us know which one to add on the forum.', self.getName())
            return None

        return cleanHost(self.proxy_domain).rstrip('/') + url
Example #13
0
    def getDomain(self, url = ''):

        if not self.domain:
            for proxy in self.proxy_list:

                prop_name = 'tpb_proxy.%s' % proxy
                last_check = float(Env.prop(prop_name, default = 0))
                if last_check > time.time() - 1209600:
                    continue

                data = ''
                try:
                    data = self.urlopen(proxy, timeout = 3, show_error = False)
                except:
                    log.debug('Failed tpb proxy %s', proxy)

                if 'title="Pirate Search"' in data:
                    log.debug('Using proxy: %s', proxy)
                    self.domain = proxy
                    break

                Env.prop(prop_name, time.time())

        if not self.domain:
            log.error('No TPB proxies left, please add one in settings, or let us know which one to add on the forum.')
            return None

        return cleanHost(self.domain).rstrip('/') + url
Example #14
0
    def autoUpdate(self):
        do_check = True

        try:
            last_check = tryInt(Env.prop(self.last_check, default = 0))
            now = tryInt(time.time())
            do_check = last_check < now - 43200

            if do_check:
                Env.prop(self.last_check, value = now)
        except:
            log.error('Failed checking last time to update: %s', traceback.format_exc())

        if do_check and self.isEnabled() and self.check() and self.conf('automatic') and not self.updater.update_failed:

            if self.updater.doUpdate():

                # Notify before restarting
                try:
                    if self.conf('notification'):
                        info = self.updater.info()
                        version_date = datetime.fromtimestamp(info['update_version']['date'])
                        fireEvent('updater.updated', 'CouchPotato: Updated to a new version with hash "%s", this version is from %s' % (info['update_version']['hash'], version_date), data = info)
                except:
                    log.error('Failed notifying for update: %s', traceback.format_exc())

                fireEventAsync('app.restart')

                return True

        return False
Example #15
0
    def cleanReleases(self):

        prop_name = 'cleaned_releases'
        already_cleaned = Env.prop(prop_name, default=False)
        if already_cleaned:
            return True

        log.info('Removing releases from library movies')

        db = get_session()

        movies = db.query(Movie).all()

        done_status = fireEvent('status.get', 'done', single=True)
        available_status = fireEvent('status.get', 'available', single=True)
        snatched_status = fireEvent('status.get', 'snatched', single=True)

        for movie in movies:
            if movie.status_id == done_status.get('id'):
                for rel in movie.releases:
                    if rel.status_id in [
                            available_status.get('id'),
                            snatched_status.get('id')
                    ]:
                        fireEvent('release.delete', id=rel.id, single=True)

        Env.prop(prop_name, True)
Example #16
0
    def suggestView(self, limit=6, **kwargs):
        if self.isDisabled():
            return {'success': True, 'movies': []}

        movies = splitString(kwargs.get('movies', ''))
        ignored = splitString(kwargs.get('ignored', ''))
        seen = splitString(kwargs.get('seen', ''))

        cached_suggestion = self.getCache('suggestion_cached')
        if cached_suggestion:
            suggestions = cached_suggestion
        else:

            if not movies or len(movies) == 0:
                active_movies = fireEvent('media.with_status',
                                          ['active', 'done'],
                                          types='movie',
                                          single=True)
                movies = [getIdentifier(x) for x in active_movies]

            if not ignored or len(ignored) == 0:
                ignored = splitString(Env.prop('suggest_ignore', default=''))
            if not seen or len(seen) == 0:
                movies.extend(splitString(Env.prop('suggest_seen',
                                                   default='')))

            suggestions = fireEvent('movie.suggest',
                                    movies=movies,
                                    ignore=ignored,
                                    single=True)
            self.setCache('suggestion_cached', suggestions,
                          timeout=6048000)  # Cache for 10 weeks

        medias = []
        for suggestion in suggestions[:int(limit)]:

            # Cache poster
            posters = suggestion.get('images', {}).get('poster', [])
            poster = [x for x in posters if 'tmdb' in x]
            posters = poster if len(poster) > 0 else posters

            cached_poster = fireEvent(
                'file.download', url=posters[0],
                single=True) if len(posters) > 0 else False
            files = {'image_poster': [cached_poster]} if cached_poster else {}

            medias.append({
                'status': 'suggested',
                'title': getTitle(suggestion),
                'type': 'movie',
                'info': suggestion,
                'files': files,
                'identifiers': {
                    'imdb': suggestion.get('imdb')
                }
            })

        return {'success': True, 'movies': medias}
Example #17
0
    def addMovies(self):

        movies = fireEvent('automation.get_movies', merge = True)
        for imdb_id in movies:
            prop_name = 'automation.added.%s' % imdb_id
            added = Env.prop(prop_name, default = False)
            if not added:
                fireEvent('movie.add', params = {'identifier': imdb_id}, force_readd = False)
                Env.prop(prop_name, True)
Example #18
0
    def suggestView(self, limit = 6, **kwargs):
        if self.isDisabled():
            return {
                'success': True,
                'movies': []
            }

        movies = splitString(kwargs.get('movies', ''))
        ignored = splitString(kwargs.get('ignored', ''))
        seen = splitString(kwargs.get('seen', ''))

        cached_suggestion = self.getCache('suggestion_cached')
        if cached_suggestion:
            suggestions = cached_suggestion
        else:

            if not movies or len(movies) == 0:
                active_movies = fireEvent('media.with_status', ['active', 'done'], types = 'movie', single = True)
                movies = [getIdentifier(x) for x in active_movies]

            if not ignored or len(ignored) == 0:
                ignored = splitString(Env.prop('suggest_ignore', default = ''))
            if not seen or len(seen) == 0:
                movies.extend(splitString(Env.prop('suggest_seen', default = '')))

            suggestions = fireEvent('movie.suggest', movies = movies, ignore = ignored, single = True)
            self.setCache('suggestion_cached', suggestions, timeout = 6048000)  # Cache for 10 weeks

        medias = []
        for suggestion in suggestions[:int(limit)]:

            # Cache poster
            posters = suggestion.get('images', {}).get('poster', [])
            poster = [x for x in posters if 'tmdb' in x]
            posters = poster if len(poster) > 0 else posters

            cached_poster = fireEvent('file.download', url = posters[0], single = True) if len(posters) > 0 else False
            files = {'image_poster': [cached_poster] } if cached_poster else {}

            medias.append({
                'status': 'suggested',
                'title': getTitle(suggestion),
                'type': 'movie',
                'info': suggestion,
                'files': files,
                'identifiers': {
                    'imdb': suggestion.get('imdb')
                }
            })

        return {
            'success': True,
            'movies': medias
        }
Example #19
0
    def addMovies(self):

        movies = fireEvent('automation.get_movies', merge=True)
        for imdb_id in movies:
            prop_name = 'automation.added.%s' % imdb_id
            added = Env.prop(prop_name, default=False)
            if not added:
                fireEvent('movie.add',
                          params={'identifier': imdb_id},
                          force_readd=False)
                Env.prop(prop_name, True)
Example #20
0
    def getIMDBids(self):

        if self.isDisabled():
            return

        movies = []

        enablers = self.conf('automation_urls_use').split(',')

        index = -1
        for rss_url in self.conf('automation_urls').split(','):

            index += 1
            if not enablers[index]:
                continue
            elif 'rss.imdb' not in rss_url:
                log.error('This isn\'t the correct url.: %s', rss_url)
                continue

            prop_name = 'automation.imdb.last_update.%s' % md5(rss_url)
            last_update = float(Env.prop(prop_name, default=0))

            last_movie_added = 0
            try:
                cache_key = 'imdb.rss.%s' % md5(rss_url)

                rss_data = self.getCache(cache_key, rss_url)
                data = XMLTree.fromstring(rss_data)
                rss_movies = self.getElements(data, 'channel/item')

                for movie in rss_movies:
                    created = int(
                        time.mktime(
                            parse(self.getTextElement(movie,
                                                      "pubDate")).timetuple()))
                    imdb = getImdb(self.getTextElement(movie, "link"))

                    if created > last_movie_added:
                        last_movie_added = created

                    if not imdb or created <= last_update:
                        continue

                    movies.append(imdb)

            except:
                log.error('Failed loading IMDB watchlist: %s %s',
                          (rss_url, traceback.format_exc()))

            Env.prop(prop_name, last_movie_added)

        return movies
Example #21
0
    def getIMDBids(self):

        if self.isDisabled():
            return

        movies = []

        enablers = self.conf('automation_urls_use').split(',')

        index = -1
        for rss_url in self.conf('automation_urls').split(','):

            index += 1
            if not enablers[index]:
                continue

            prop_name = 'automation.moviesio.last_update.%s' % md5(rss_url)
            last_update = float(Env.prop(prop_name, default = 0))

            last_movie_added = 0
            try:
                cache_key = 'imdb.rss.%s' % md5(rss_url)

                rss_data = self.getCache(cache_key, rss_url, headers = {'Referer': ''})
                data = XMLTree.fromstring(rss_data)
                rss_movies = self.getElements(data, 'channel/item')

                for movie in rss_movies:
                    created = int(time.mktime(parse(self.getTextElement(movie, "pubDate")).timetuple()))

                    if created > last_movie_added:
                        last_movie_added = created
                    if created <= last_update:
                        continue

                    nameyear = fireEvent('scanner.name_year', self.getTextElement(movie, "title"), single = True)
                    imdb = self.search(nameyear.get('name'), nameyear.get('year'), imdb_only = True)

                    if not imdb:
                        continue

                    movies.append(imdb)
            except ParseError:
                log.debug('Failed loading Movies.io watchlist, probably empty: %s', (rss_url))
            except:
                log.error('Failed loading Movies.io watchlist: %s %s', (rss_url, traceback.format_exc()))

            Env.prop(prop_name, last_movie_added)

        return movies
Example #22
0
    def checkMessages(self):

        prop_name = 'messages.last_check'
        last_check = tryInt(Env.prop(prop_name, default = 0))

        messages = fireEvent('cp.messages', last_check = last_check, single = True)

        for message in messages:
            if message.get('time') > last_check:
                fireEvent('core.message', message = message.get('message'), data = message)

            if last_check < message.get('time'):
                last_check = message.get('time')

        Env.prop(prop_name, value = last_check)
Example #23
0
    def search(self, name, year = None, imdb_only = False):

        prop_name = 'automation.cached.%s.%s' % (name, year)
        cached_imdb = Env.prop(prop_name, default = False)
        if cached_imdb and imdb_only:
            return cached_imdb

        result = fireEvent('movie.search', q = '%s %s' % (name, year if year else ''), limit = 1, merge = True)

        if len(result) > 0:
            if imdb_only and result[0].get('imdb'):
                Env.prop(prop_name, result[0].get('imdb'))

            return result[0].get('imdb') if imdb_only else result[0]
        else:
            return None
Example #24
0
    def search(self, name, year = None, imdb_only = False):

        prop_name = 'automation.cached.%s.%s' % (name, year)
        cached_imdb = Env.prop(prop_name, default = False)
        if cached_imdb and imdb_only:
            return cached_imdb

        result = fireEvent('movie.search', q = '%s %s' % (name, year if year else ''), limit = 1, merge = True)

        if len(result) > 0:
            if imdb_only and result[0].get('imdb'):
                Env.prop(prop_name, result[0].get('imdb'))

            return result[0].get('imdb') if imdb_only else result[0]
        else:
            return None
Example #25
0
    def ignoreView(self, imdb = None, limit = 6, remove_only = False, **kwargs):

        ignored = splitString(Env.prop('suggest_ignore', default = ''))

        if imdb:
            if not remove_only:
                ignored.append(imdb)
                Env.prop('suggest_ignore', ','.join(set(ignored)))

            new_suggestions = self.updateSuggestionCache(ignore_imdb = imdb, limit = limit, ignored = ignored)

        return {
            'result': True,
            'ignore_count': len(ignored),
            'suggestions': new_suggestions[limit - 1:limit]
        }
Example #26
0
    def search(self, name, year=None, imdb_only=False):

        cache_name = name.decode("utf-8").encode("ascii", "ignore")
        prop_name = "automation.cached.%s.%s" % (cache_name, year)
        cached_imdb = Env.prop(prop_name, default=False)
        if cached_imdb and imdb_only:
            return cached_imdb

        result = fireEvent("movie.search", q="%s %s" % (name, year if year else ""), limit=1, merge=True)

        if len(result) > 0:
            if imdb_only and result[0].get("imdb"):
                Env.prop(prop_name, result[0].get("imdb"))

            return result[0].get("imdb") if imdb_only else result[0]
        else:
            return None
Example #27
0
    def checkMessages(self):

        prop_name = 'messages.last_check'
        last_check = tryInt(Env.prop(prop_name, default=0))

        messages = fireEvent('cp.messages', last_check=last_check, single=True)

        for message in messages:
            if message.get('time') > last_check:
                fireEvent('core.message',
                          message=message.get('message'),
                          data=message)

            if last_check < message.get('time'):
                last_check = message.get('time')

        Env.prop(prop_name, value=last_check)
Example #28
0
    def addMovies(self):

        movies = fireEvent('automation.get_movies', merge = True)
        movie_ids = []

        for imdb_id in movies:
            prop_name = 'automation.added.%s' % imdb_id
            added = Env.prop(prop_name, default = False)
            if not added:
                added_movie = fireEvent('movie.add', params = {'identifier': imdb_id}, force_readd = False, search_after = False, update_library = True, single = True)
                if added_movie:
                    movie_ids.append(added_movie['id'])
                Env.prop(prop_name, True)

        for movie_id in movie_ids:
            movie_dict = fireEvent('movie.get', movie_id, single = True)
            fireEvent('searcher.single', movie_dict)
Example #29
0
    def ignoreView(self, imdb = None, limit = 6, remove_only = False, **kwargs):

        ignored = splitString(Env.prop('suggest_ignore', default = ''))

        new_suggestions = []
        if imdb:
            if not remove_only:
                ignored.append(imdb)
                Env.prop('suggest_ignore', ','.join(set(ignored)))

            new_suggestions = self.updateSuggestionCache(ignore_imdb = imdb, limit = limit, ignored = ignored)

        return {
            'result': True,
            'ignore_count': len(ignored),
            'suggestions': new_suggestions[limit - 1:limit]
        }
Example #30
0
    def checkMessages(self):

        prop_name = "messages.last_check"
        last_check = tryInt(Env.prop(prop_name, default=0))

        messages = fireEvent("cp.messages", last_check=last_check, single=True) or []

        for message in messages:
            if message.get("time") > last_check:
                message["sticky"] = True  # Always sticky core messages

                message_type = "core.message.important" if message.get("important") else "core.message"
                fireEvent(message_type, message=message.get("message"), data=message)

            if last_check < message.get("time"):
                last_check = message.get("time")

        Env.prop(prop_name, value=last_check)
Example #31
0
    def checkMessages(self):

        prop_name = 'messages.last_check'
        last_check = tryInt(Env.prop(prop_name, default = 0))

        messages = fireEvent('cp.messages', last_check = last_check, single = True) or []

        for message in messages:
            if message.get('time') > last_check:
                message['sticky'] = True  # Always sticky core messages

                message_type = 'core.message.important' if message.get('important') else 'core.message'
                fireEvent(message_type, message = message.get('message'), data = message)

            if last_check < message.get('time'):
                last_check = message.get('time')

        Env.prop(prop_name, value = last_check)
Example #32
0
    def getIMDBids(self):

        if self.isDisabled():
            return

        movies = []

        enablers = self.conf('automation_urls_use').split(',')

        index = -1
        for rss_url in self.conf('automation_urls').split(','):

            index += 1
            if not enablers[index]:
                continue
            elif 'rss.imdb' not in rss_url:
                log.error('This isn\'t the correct url.: %s' % rss_url)
                continue

            prop_name = 'automation.imdb.last_update.%s' % md5(rss_url)
            last_update = float(Env.prop(prop_name, default = 0))

            try:
                cache_key = 'imdb.rss.%s' % md5(rss_url)

                rss_data = self.getCache(cache_key, rss_url)
                data = XMLTree.fromstring(rss_data)
                rss_movies = self.getElements(data, 'channel/item')

                for movie in rss_movies:
                    created = int(time.mktime(parse(self.getTextElement(movie, "pubDate")).timetuple()))
                    imdb = getImdb(self.getTextElement(movie, "link"))

                    if not imdb or created < last_update:
                        continue

                    movies.append(imdb)

            except:
                log.error('Failed loading IMDB watchlist: %s %s' % (rss_url, traceback.format_exc()))

            Env.prop(prop_name, time.time())

        return movies
Example #33
0
    def updateLibrary(self, full = True):
        last_update = float(Env.prop('manage.last_update', default = 0))

        if self.isDisabled() or (last_update > time.time() - 20):
            return

        directories = self.directories()
        added_identifiers = []

        for directory in directories:

            if not os.path.isdir(directory):
                if len(directory) > 0:
                    log.error('Directory doesn\'t exist: %s', directory)
                continue

            log.info('Updating manage library: %s', directory)
            identifiers = fireEvent('scanner.folder', folder = directory, newer_than = last_update if not full else 0, single = True)
            if identifiers:
                added_identifiers.extend(identifiers)

            # Break if CP wants to shut down
            if self.shuttingDown():
                break

        # If cleanup option is enabled, remove offline files from database
        if self.conf('cleanup') and full and not self.shuttingDown():

            # Get movies with done status
            total_movies, done_movies = fireEvent('movie.list', status = 'done', single = True)

            for done_movie in done_movies:
                if done_movie['library']['identifier'] not in added_identifiers:
                    fireEvent('movie.delete', movie_id = done_movie['id'], delete_from = 'all')
                else:
                    for release in done_movie.get('releases', []):
                        for release_file in release.get('files', []):
                            # Remove release not available anymore
                            if not os.path.isfile(ss(release_file['path'])):
                                fireEvent('release.clean', release['id'])
                                break

        Env.prop('manage.last_update', time.time())
Example #34
0
    def search(self, name, year = None, imdb_only = False):

        try:
            cache_name = name.decode('utf-8').encode('ascii', 'ignore')
        except UnicodeEncodeError:
            cache_name = unicodedata.normalize('NFKD', name).encode('ascii','ignore')

        prop_name = 'automation.cached.%s.%s' % (cache_name, year)
        cached_imdb = Env.prop(prop_name, default = False)
        if cached_imdb and imdb_only:
            return cached_imdb

        result = fireEvent('movie.search', q = '%s %s' % (name, year if year else ''), limit = 1, merge = True)

        if len(result) > 0:
            if imdb_only and result[0].get('imdb'):
                Env.prop(prop_name, result[0].get('imdb'))

            return result[0].get('imdb') if imdb_only else result[0]
        else:
            return None
Example #35
0
    def getIMDBids(self):

        if self.isDisabled():
            return

        movies = []
        headers = {}

        for csv_url in self.conf('automation_urls').split(','):
            prop_name = 'automation.imdb.last_update.%s' % md5(csv_url)
            last_update = float(Env.prop(prop_name, default = 0))

            try:
                cache_key = 'imdb_csv.%s' % md5(csv_url)
                csv_data = self.getCache(cache_key, csv_url)
                csv_reader = csv.reader(StringIO.StringIO(csv_data))
                if not headers:
                    nr = 0
                    for column in csv_reader.next():
                        headers[column] = nr
                        nr += 1

                for row in csv_reader:
                    created = int(time.mktime(parse(row[headers['created']]).timetuple()))
                    if created < last_update:
                        continue

                    imdb = row[headers['const']]
                    if imdb:
                        movies.append(imdb)
            except:
                log.error('Failed loading IMDB watchlist: %s %s' % (csv_url, traceback.format_exc()))

            Env.prop(prop_name, time.time())


        return movies
Example #36
0
    def cleanReleases(self):

        prop_name = 'cleaned_releases'
        already_cleaned = Env.prop(prop_name, default = False)
        if already_cleaned:
            return True

        log.info('Removing releases from library movies')

        db = get_session()

        movies = db.query(Movie).all()

        done_status = fireEvent('status.get', 'done', single = True)
        available_status = fireEvent('status.get', 'available', single = True)
        snatched_status = fireEvent('status.get', 'snatched', single = True)

        for movie in movies:
            if movie.status_id == done_status.get('id'):
                for rel in movie.releases:
                    if rel.status_id in [available_status.get('id'), snatched_status.get('id')]:
                        fireEvent('release.delete', id = rel.id, single = True)

        Env.prop(prop_name, True)
Example #37
0
    def ignoreView(self,
                   imdb=None,
                   limit=6,
                   remove_only=False,
                   mark_seen=False,
                   **kwargs):

        ignored = splitString(Env.prop('suggest_ignore', default=''))
        seen = splitString(Env.prop('suggest_seen', default=''))

        new_suggestions = []
        if imdb:
            if mark_seen:
                seen.append(imdb)
                Env.prop('suggest_seen', ','.join(set(seen)))
            elif not remove_only:
                ignored.append(imdb)
                Env.prop('suggest_ignore', ','.join(set(ignored)))

            new_suggestions = self.updateSuggestionCache(ignore_imdb=imdb,
                                                         limit=limit,
                                                         ignored=ignored,
                                                         seen=seen)

        if len(new_suggestions) <= limit:
            return {'result': False}

        # Only return new (last) item
        media = {
            'status': 'suggested',
            'title': getTitle(new_suggestions[limit]),
            'type': 'movie',
            'info': new_suggestions[limit],
            'identifiers': {
                'imdb': new_suggestions[limit].get('imdb')
            }
        }

        return {'result': True, 'movie': media}
Example #38
0
    def ignoreView(self, imdb = None, limit = 6, remove_only = False, mark_seen = False, **kwargs):

        ignored = splitString(Env.prop('suggest_ignore', default = ''))
        seen = splitString(Env.prop('suggest_seen', default = ''))

        new_suggestions = []
        if imdb:
            if mark_seen:
                seen.append(imdb)
                Env.prop('suggest_seen', ','.join(set(seen)))
            elif not remove_only:
                ignored.append(imdb)
                Env.prop('suggest_ignore', ','.join(set(ignored)))

            new_suggestions = self.updateSuggestionCache(ignore_imdb = imdb, limit = limit, ignored = ignored, seen = seen)

        if len(new_suggestions) <= limit:
            return {
                'result': False
            }

        # Only return new (last) item
        media = {
            'status': 'suggested',
            'title': getTitle(new_suggestions[limit]),
            'type': 'movie',
            'info': new_suggestions[limit],
            'identifiers': {
                'imdb': new_suggestions[limit].get('imdb')
            }
        }

        return {
            'result': True,
            'movie': media
        }
Example #39
0
    def single(self, movie, search_protocols = None, manual = False, force_download = False):

        # Find out search type
        try:
            if not search_protocols:
                search_protocols = fireEvent('searcher.protocols', single = True)
        except SearchSetupError:
            return

        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)

        found_releases = []
        previous_releases = movie.get('releases', [])
        too_early_to_search = []
        outside_eta_results = 0
        alway_search = self.conf('always_search')
        ignore_eta = manual
        total_result_count = 0

        fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title)

        # Ignore eta once every 7 days
        if not alway_search:
            prop_name = 'last_ignored_eta.%s' % movie['_id']
            last_ignored_eta = float(Env.prop(prop_name, default = 0))
            if last_ignored_eta > time.time() - 604800:
                ignore_eta = True
                Env.prop(prop_name, value = time.time())

        db = get_db()

        profile = db.get('id', movie['profile_id'])
        ret = False

        for index, q_identifier in enumerate(profile.get('qualities', [])):
            quality_custom = {
                'index': index,
                'quality': q_identifier,
                'finish': profile['finish'][index],
                'wait_for': tryInt(profile['wait_for'][index]),
                '3d': profile['3d'][index] if profile.get('3d') else False
            }

            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)

                # Skip release, if ETA isn't ignored
                if not ignore_eta:
                    continue

            has_better_quality = 0

            # See if better quality is available
            for release in movie.get('releases', []):
                if release['status'] not in ['available', 'ignored', 'failed']:
                    is_higher = fireEvent('quality.ishigher', \
                            {'identifier': q_identifier, 'is_3d': quality_custom.get('3d', 0)}, \
                            {'identifier': release['quality'], 'is_3d': release.get('is_3d', 0)}, \
                            profile, single = True)
                    if is_higher != 'higher':
                        has_better_quality += 1

            # 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'], single = True)
                break

            quality = fireEvent('quality.single', identifier = q_identifier, single = True)
            log.info('Search for %s in %s%s', (default_title, quality['label'], ' ignoring ETA' if alway_search or ignore_eta else ''))

            # Extend quality with profile customs
            quality['custom'] = quality_custom

            results = fireEvent('searcher.search', search_protocols, movie, quality, single = True) or []
            results_count = len(results)
            total_result_count += results_count
            if results_count == 0:
                log.debug('Nothing found for %s in %s', (default_title, quality['label']))

            # Keep track of releases found outside ETA window
            outside_eta_results += results_count if could_not_be_released else 0

            # 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, single = True)

            # Don't trigger download, but notify user of available releases
            if could_not_be_released:
                if results_count > 0:
                    log.debug('Found %s releases for "%s", but ETA isn\'t correct yet.', (results_count, default_title))

            # Try find a valid result and download it
            if (force_download or not could_not_be_released or alway_search) and fireEvent('release.try_download_result', results, movie, quality_custom, single = True):
                ret = True

            # Remove releases that aren't found anymore
            temp_previous_releases = []
            for release in previous_releases:
                if release.get('status') == 'available' and release.get('identifier') not in found_releases:
                    fireEvent('release.delete', release.get('_id'), single = True)
                else:
                    temp_previous_releases.append(release)
            previous_releases = temp_previous_releases
            del temp_previous_releases

            # Break if CP wants to shut down
            if self.shuttingDown() or ret:
                break

        if total_result_count > 0:
            fireEvent('media.tag', movie['_id'], 'recent', update_edited = True, single = True)

        if len(too_early_to_search) > 0:
            log.info2('Too early to search for %s, %s', (too_early_to_search, default_title))

            if outside_eta_results > 0:
                message = 'Found %s releases for "%s" before ETA. Select and download via the dashboard.' % (outside_eta_results, default_title)
                log.info(message)

                if not manual:
                    fireEvent('media.available', message = message, data = {})

        fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']})

        return ret
Example #40
0
    def updateLibrary(self, full = True):
        last_update = float(Env.prop('manage.last_update', default = 0))

        if self.in_progress:
            log.info('Already updating library: %s', self.in_progress)
            return
        elif self.isDisabled() or (last_update > time.time() - 20):
            return

        self.in_progress = {}
        fireEvent('notify.frontend', type = 'manage.updating', data = True)

        try:

            directories = self.directories()
            added_identifiers = []

            # Add some progress
            self.in_progress = {}
            for directory in directories:
                self.in_progress[os.path.normpath(directory)] = {
                    'total': None,
                    'to_go': None,
                }

            for directory in directories:
                folder = os.path.normpath(directory)

                if not os.path.isdir(folder):
                    if len(directory) > 0:
                        log.error('Directory doesn\'t exist: %s', folder)
                    continue

                log.info('Updating manage library: %s', folder)
                fireEvent('notify.frontend', type = 'manage.update', data = True, message = 'Scanning for movies in "%s"' % folder)

                onFound = self.createAddToLibrary(folder, added_identifiers)
                fireEvent('scanner.scan', folder = folder, simple = True, newer_than = last_update if not full else 0, on_found = onFound, single = True)

                # Break if CP wants to shut down
                if self.shuttingDown():
                    break

            # If cleanup option is enabled, remove offline files from database
            if self.conf('cleanup') and full and not self.shuttingDown():

                # Get movies with done status
                total_movies, done_movies = fireEvent('movie.list', status = 'done', single = True)

                for done_movie in done_movies:
                    if done_movie['library']['identifier'] not in added_identifiers:
                        fireEvent('movie.delete', movie_id = done_movie['id'], delete_from = 'all')
                    else:

                        for release in done_movie.get('releases', []):
                            if len(release.get('files', [])) == 0:
                                fireEvent('release.delete', release['id'])
                            else:
                                for release_file in release.get('files', []):
                                    # Remove release not available anymore
                                    if not os.path.isfile(ss(release_file['path'])):
                                        fireEvent('release.clean', release['id'])
                                        break

                        # Check if there are duplicate releases (different quality) use the last one, delete the rest
                        if len(done_movie.get('releases', [])) > 1:
                            used_files = {}
                            for release in done_movie.get('releases', []):

                                for release_file in release.get('files', []):
                                    already_used = used_files.get(release_file['path'])

                                    if already_used:
                                        if already_used < release['id']:
                                            fireEvent('release.delete', release['id'], single = True) # delete this one
                                        else:
                                            fireEvent('release.delete', already_used, single = True) # delete previous one
                                        break
                                    else:
                                        used_files[release_file['path']] = release.get('id')
                            del used_files

            Env.prop('manage.last_update', time.time())
        except:
            log.error('Failed updating library: %s', (traceback.format_exc()))

        while True and not self.shuttingDown():

            delete_me = {}

            for folder in self.in_progress:
                if self.in_progress[folder]['to_go'] <= 0:
                    delete_me[folder] = True

            for delete in delete_me:
                del self.in_progress[delete]

            if len(self.in_progress) == 0:
                break

            time.sleep(1)

        fireEvent('notify.frontend', type = 'manage.updating', data = False)
        self.in_progress = False
Example #41
0
    def updateLibrary(self, full=True):
        last_update = float(Env.prop('manage.last_update', default=0))

        if self.in_progress:
            log.info('Already updating library: %s', self.in_progress)
            return
        elif self.isDisabled() or (last_update > time.time() - 20):
            return

        self.in_progress = {}
        fireEvent('notify.frontend', type='manage.updating', data=True)

        try:

            directories = self.directories()
            added_identifiers = []

            # Add some progress
            self.in_progress = {}
            for directory in directories:
                self.in_progress[os.path.normpath(directory)] = {
                    'total': None,
                    'to_go': None,
                }

            for directory in directories:
                folder = os.path.normpath(directory)

                if not os.path.isdir(folder):
                    if len(directory) > 0:
                        log.error('Directory doesn\'t exist: %s', folder)
                    continue

                log.info('Updating manage library: %s', folder)
                fireEvent('notify.frontend',
                          type='manage.update',
                          data=True,
                          message='Scanning for movies in "%s"' % folder)

                onFound = self.createAddToLibrary(folder, added_identifiers)
                fireEvent('scanner.scan',
                          folder=folder,
                          simple=True,
                          newer_than=last_update if not full else 0,
                          on_found=onFound,
                          single=True)

                # Break if CP wants to shut down
                if self.shuttingDown():
                    break

            # If cleanup option is enabled, remove offline files from database
            if self.conf('cleanup') and full and not self.shuttingDown():

                # Get movies with done status
                total_movies, done_movies = fireEvent('movie.list',
                                                      status='done',
                                                      single=True)

                for done_movie in done_movies:
                    if done_movie['library'][
                            'identifier'] not in added_identifiers:
                        fireEvent('movie.delete',
                                  movie_id=done_movie['id'],
                                  delete_from='all')
                    else:
                        for release in done_movie.get('releases', []):
                            for release_file in release.get('files', []):
                                # Remove release not available anymore
                                if not os.path.isfile(ss(
                                        release_file['path'])):
                                    fireEvent('release.clean', release['id'])
                                    break

            Env.prop('manage.last_update', time.time())
        except:
            log.error('Failed updating library: %s', (traceback.format_exc()))

        while True and not self.shuttingDown():

            delete_me = {}

            for folder in self.in_progress:
                if self.in_progress[folder]['to_go'] <= 0:
                    delete_me[folder] = True

            for delete in delete_me:
                del self.in_progress[delete]

            if len(self.in_progress) == 0:
                break

            time.sleep(1)

        fireEvent('notify.frontend', type='manage.updating', data=False)
        self.in_progress = False
Example #42
0
    def getIMDBids(self):

        if self.isDisabled():
            return

        movies = []

        enablers = self.conf('automation_urls_use').split(',')

        index = -1
        for rss_url in self.conf('automation_urls').split(','):

            index += 1
            if not enablers[index]:
                continue

            prop_name = 'automation.moviesio.last_update.%s' % md5(rss_url)
            last_update = float(Env.prop(prop_name, default=0))

            last_movie_added = 0
            try:
                cache_key = 'imdb.rss.%s' % md5(rss_url)

                rss_data = self.getCache(cache_key,
                                         rss_url,
                                         headers={'Referer': ''})
                data = XMLTree.fromstring(rss_data)
                rss_movies = self.getElements(data, 'channel/item')

                for movie in rss_movies:
                    created = int(
                        time.mktime(
                            parse(self.getTextElement(movie,
                                                      "pubDate")).timetuple()))

                    if created > last_movie_added:
                        last_movie_added = created
                    if created <= last_update:
                        continue

                    nameyear = fireEvent('scanner.name_year',
                                         self.getTextElement(movie, "title"),
                                         single=True)
                    imdb = self.search(nameyear.get('name'),
                                       nameyear.get('year'),
                                       imdb_only=True)

                    if not imdb:
                        continue

                    movies.append(imdb)
            except ParseError:
                log.debug(
                    'Failed loading Movies.io watchlist, probably empty: %s',
                    (rss_url))
            except:
                log.error('Failed loading Movies.io watchlist: %s %s',
                          (rss_url, traceback.format_exc()))

            Env.prop(prop_name, last_movie_added)

        return movies
Example #43
0
    def single(self,
               movie,
               search_protocols=None,
               manual=False,
               force_download=False):

        # Find out search type
        try:
            if not search_protocols:
                search_protocols = fireEvent('searcher.protocols', single=True)
        except SearchSetupError:
            return

        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)

        found_releases = []
        previous_releases = movie.get('releases', [])
        too_early_to_search = []
        outside_eta_results = 0
        alway_search = self.conf('always_search')
        ignore_eta = manual
        total_result_count = 0

        fireEvent('notify.frontend',
                  type='movie.searcher.started',
                  data={'_id': movie['_id']},
                  message='Searching for "%s"' % default_title)

        # Ignore eta once every 7 days
        if not alway_search:
            prop_name = 'last_ignored_eta.%s' % movie['_id']
            last_ignored_eta = float(Env.prop(prop_name, default=0))
            if last_ignored_eta > time.time() - 604800:
                ignore_eta = True
                Env.prop(prop_name, value=time.time())

        db = get_db()

        profile = db.get('id', movie['profile_id'])
        ret = False

        for index, q_identifier in enumerate(profile.get('qualities', [])):
            quality_custom = {
                'index': index,
                'quality': q_identifier,
                'finish': profile['finish'][index],
                'wait_for': tryInt(profile['wait_for'][index]),
                '3d': profile['3d'][index] if profile.get('3d') else False
            }

            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)

                # Skip release, if ETA isn't ignored
                if not ignore_eta:
                    continue

            has_better_quality = 0

            # See if better quality is available
            for release in movie.get('releases', []):
                if release['status'] not in ['available', 'ignored', 'failed']:
                    is_higher = fireEvent('quality.ishigher', \
                            {'identifier': q_identifier, 'is_3d': quality_custom.get('3d', 0)}, \
                            {'identifier': release['quality'], 'is_3d': release.get('is_3d', 0)}, \
                            profile, single = True)
                    if is_higher != 'higher':
                        has_better_quality += 1

            # 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'], single=True)
                break

            quality = fireEvent('quality.single',
                                identifier=q_identifier,
                                single=True)
            log.info('Search for %s in %s%s',
                     (default_title, quality['label'],
                      ' ignoring ETA' if alway_search or ignore_eta else ''))

            # Extend quality with profile customs
            quality['custom'] = quality_custom

            results = fireEvent('searcher.search',
                                search_protocols,
                                movie,
                                quality,
                                single=True) or []
            results_count = len(results)
            total_result_count += results_count
            if results_count == 0:
                log.debug('Nothing found for %s in %s',
                          (default_title, quality['label']))

            # Keep track of releases found outside ETA window
            outside_eta_results += results_count if could_not_be_released else 0

            # 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,
                                        single=True)

            # Don't trigger download, but notify user of available releases
            if could_not_be_released:
                if results_count > 0:
                    log.debug(
                        'Found %s releases for "%s", but ETA isn\'t correct yet.',
                        (results_count, default_title))

            # Try find a valid result and download it
            if (force_download or not could_not_be_released or
                    alway_search) and fireEvent('release.try_download_result',
                                                results,
                                                movie,
                                                quality_custom,
                                                single=True):
                ret = True

            # Remove releases that aren't found anymore
            temp_previous_releases = []
            for release in previous_releases:
                if release.get('status') == 'available' and release.get(
                        'identifier') not in found_releases:
                    fireEvent('release.delete',
                              release.get('_id'),
                              single=True)
                else:
                    temp_previous_releases.append(release)
            previous_releases = temp_previous_releases
            del temp_previous_releases

            # Break if CP wants to shut down
            if self.shuttingDown() or ret:
                break

        if total_result_count > 0:
            fireEvent('media.tag',
                      movie['_id'],
                      'recent',
                      update_edited=True,
                      single=True)

        if len(too_early_to_search) > 0:
            log.info2('Too early to search for %s, %s',
                      (too_early_to_search, default_title))

            if outside_eta_results > 0:
                message = 'Found %s releases for "%s" before ETA. Select and download via the dashboard.' % (
                    outside_eta_results, default_title)
                log.info(message)

                if not manual:
                    fireEvent('media.available', message=message, data={})

        fireEvent('notify.frontend',
                  type='movie.searcher.ended',
                  data={'_id': movie['_id']})

        return ret
Example #44
0
    def updateLibrary(self, full = True):
        last_update_key = 'manage.last_update%s' % ('_full' if full else '')
        last_update = float(Env.prop(last_update_key, default = 0))

        if self.in_progress:
            log.info('Already updating library: %s', self.in_progress)
            return
        elif self.isDisabled() or (last_update > time.time() - 20):
            return

        self.in_progress = {}
        fireEvent('notify.frontend', type = 'manage.updating', data = True)

        try:

            directories = self.directories()
            directories.sort()
            added_identifiers = []

            # Add some progress
            for directory in directories:
                self.in_progress[os.path.normpath(directory)] = {
                    'started': False,
                    'eta': -1,
                    'total': None,
                    'to_go': None,
                }

            for directory in directories:
                folder = os.path.normpath(directory)
                self.in_progress[os.path.normpath(directory)]['started'] = tryInt(time.time())

                if not os.path.isdir(folder):
                    if len(directory) > 0:
                        log.error('Directory doesn\'t exist: %s', folder)
                    continue

                log.info('Updating manage library: %s', folder)
                fireEvent('notify.frontend', type = 'manage.update', data = True, message = 'Scanning for movies in "%s"' % folder)

                onFound = self.createAddToLibrary(folder, added_identifiers)
                fireEvent('scanner.scan', folder = folder, simple = True, newer_than = last_update if not full else 0, on_found = onFound, single = True)

                # Break if CP wants to shut down
                if self.shuttingDown():
                    break

            # If cleanup option is enabled, remove offline files from database
            if self.conf('cleanup') and full and not self.shuttingDown():

                # Get movies with done status
                total_movies, done_movies = fireEvent('media.list', types = 'movie', status = 'done', release_status = 'done', status_or = True, single = True)

                deleted_releases = []
                for done_movie in done_movies:
                    if getIdentifier(done_movie) not in added_identifiers:
                        fireEvent('media.delete', media_id = done_movie['_id'], delete_from = 'all')
                    else:

                        releases = done_movie.get('releases', [])

                        for release in releases:
                            if release.get('files'):
                                brk = False
                                for file_type in release.get('files', {}):
                                    for release_file in release['files'][file_type]:
                                        # Remove release not available anymore
                                        if not os.path.isfile(sp(release_file)):
                                            fireEvent('release.clean', release['_id'])
                                            brk = True
                                            break
                                    if brk:
                                        break

                        # Check if there are duplicate releases (different quality) use the last one, delete the rest
                        if len(releases) > 1:
                            used_files = {}
                            for release in releases:
                                for file_type in release.get('files', {}):
                                    for release_file in release['files'][file_type]:
                                        already_used = used_files.get(release_file)

                                        if already_used:
                                            release_id = release['_id'] if already_used.get('last_edit', 0) < release.get('last_edit', 0) else already_used['_id']
                                            if release_id not in deleted_releases:
                                                fireEvent('release.delete', release_id, single = True)
                                                deleted_releases.append(release_id)
                                            break
                                        else:
                                            used_files[release_file] = release
                            del used_files

                    # Break if CP wants to shut down
                    if self.shuttingDown():
                        break

                if not self.shuttingDown():
                    db = get_db()
                    db.reindex()

            Env.prop(last_update_key, time.time())
        except:
            log.error('Failed updating library: %s', (traceback.format_exc()))

        while self.in_progress and len(self.in_progress) > 0 and not self.shuttingDown():

            delete_me = {}

            for folder in self.in_progress:
                if self.in_progress[folder]['to_go'] <= 0:
                    delete_me[folder] = True

            for delete in delete_me:
                del self.in_progress[delete]

            time.sleep(1)

        fireEvent('notify.frontend', type = 'manage.updating', data = False)
        self.in_progress = False
Example #45
0
    def updateLibrary(self, full=True):
        last_update_key = 'manage.last_update%s' % ('_full' if full else '')
        last_update = float(Env.prop(last_update_key, default=0))

        if self.in_progress:
            log.info('Already updating library: %s', self.in_progress)
            return
        elif self.isDisabled() or (last_update > time.time() - 20):
            return

        self.in_progress = {}
        fireEvent('notify.frontend', type='manage.updating', data=True)

        try:

            directories = self.directories()
            directories.sort()
            added_identifiers = []

            # Add some progress
            for directory in directories:
                self.in_progress[os.path.normpath(directory)] = {
                    'started': False,
                    'eta': -1,
                    'total': None,
                    'to_go': None,
                }

            for directory in directories:
                folder = os.path.normpath(directory)
                self.in_progress[os.path.normpath(
                    directory)]['started'] = tryInt(time.time())

                if not os.path.isdir(folder):
                    if len(directory) > 0:
                        log.error('Directory doesn\'t exist: %s', folder)
                    continue

                log.info('Updating manage library: %s', folder)
                fireEvent('notify.frontend',
                          type='manage.update',
                          data=True,
                          message='Scanning for movies in "%s"' % folder)

                onFound = self.createAddToLibrary(folder, added_identifiers)
                fireEvent('scanner.scan',
                          folder=folder,
                          simple=True,
                          newer_than=last_update if not full else 0,
                          on_found=onFound,
                          single=True)

                # Break if CP wants to shut down
                if self.shuttingDown():
                    break

            # If cleanup option is enabled, remove offline files from database
            if self.conf('cleanup') and full and not self.shuttingDown():

                # Get movies with done status
                total_movies, done_movies = fireEvent('media.list',
                                                      types='movie',
                                                      status='done',
                                                      release_status='done',
                                                      status_or=True,
                                                      single=True)

                deleted_releases = []
                for done_movie in done_movies:
                    if getIdentifier(done_movie) not in added_identifiers:
                        fireEvent('media.delete',
                                  media_id=done_movie['_id'],
                                  delete_from='all')
                    else:

                        releases = done_movie.get('releases', [])

                        for release in releases:
                            if release.get('files'):
                                brk = False
                                for file_type in release.get('files', {}):
                                    for release_file in release['files'][
                                            file_type]:
                                        # Remove release not available anymore
                                        if not os.path.isfile(
                                                sp(release_file)):
                                            fireEvent('release.clean',
                                                      release['_id'])
                                            brk = True
                                            break
                                    if brk:
                                        break

                        # Check if there are duplicate releases (different quality) use the last one, delete the rest
                        if len(releases) > 1:
                            used_files = {}
                            for release in releases:
                                for file_type in release.get('files', {}):
                                    for release_file in release['files'][
                                            file_type]:
                                        already_used = used_files.get(
                                            release_file)

                                        if already_used:
                                            release_id = release[
                                                '_id'] if already_used.get(
                                                    'last_edit',
                                                    0) < release.get(
                                                        'last_edit', 0
                                                    ) else already_used['_id']
                                            if release_id not in deleted_releases:
                                                fireEvent('release.delete',
                                                          release_id,
                                                          single=True)
                                                deleted_releases.append(
                                                    release_id)
                                            break
                                        else:
                                            used_files[release_file] = release
                            del used_files

                    # Break if CP wants to shut down
                    if self.shuttingDown():
                        break

                if not self.shuttingDown():
                    db = get_db()
                    db.reindex()

            Env.prop(last_update_key, time.time())
        except:
            log.error('Failed updating library: %s', (traceback.format_exc()))

        while self.in_progress and len(
                self.in_progress) > 0 and not self.shuttingDown():

            delete_me = {}

            for folder in self.in_progress:
                if self.in_progress[folder]['to_go'] <= 0:
                    delete_me[folder] = True

            for delete in delete_me:
                del self.in_progress[delete]

            time.sleep(1)

        fireEvent('notify.frontend', type='manage.updating', data=False)
        self.in_progress = False
Example #46
0
    def single(self, movie, search_protocols=None, manual=False, force_download=False):

        # Find out search type
        try:
            if not search_protocols:
                search_protocols = fireEvent("searcher.protocols", single=True)
        except SearchSetupError:
            return

        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)

        found_releases = []
        previous_releases = movie.get("releases", [])
        too_early_to_search = []
        outside_eta_results = 0
        alway_search = self.conf("always_search")
        ignore_eta = manual
        total_result_count = 0

        fireEvent(
            "notify.frontend",
            type="movie.searcher.started",
            data={"_id": movie["_id"]},
            message='Searching for "%s"' % default_title,
        )

        # Ignore eta once every 7 days
        if not alway_search:
            prop_name = "last_ignored_eta.%s" % movie["_id"]
            last_ignored_eta = float(Env.prop(prop_name, default=0))
            if last_ignored_eta > time.time() - 604800:
                ignore_eta = True
                Env.prop(prop_name, value=time.time())

        db = get_db()

        profile = db.get("id", movie["profile_id"])
        ret = False

        for index, q_identifier in enumerate(profile.get("qualities", [])):
            quality_custom = {
                "index": index,
                "quality": q_identifier,
                "finish": profile["finish"][index],
                "wait_for": tryInt(profile["wait_for"][index]),
                "3d": profile["3d"][index] if profile.get("3d") else False,
            }

            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)

                # Skip release, if ETA isn't ignored
                if not ignore_eta:
                    continue

            has_better_quality = 0

            # See if better quality is available
            for release in movie.get("releases", []):
                if release["status"] not in ["available", "ignored", "failed"]:
                    is_higher = fireEvent(
                        "quality.ishigher",
                        {"identifier": q_identifier, "is_3d": quality_custom.get("3d", 0)},
                        {"identifier": release["quality"], "is_3d": release.get("is_3d", 0)},
                        profile,
                        single=True,
                    )
                    if is_higher != "higher":
                        has_better_quality += 1

            # 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"], single=True)
                break

            quality = fireEvent("quality.single", identifier=q_identifier, single=True)
            log.info(
                "Search for %s in %s%s",
                (default_title, quality["label"], " ignoring ETA" if alway_search or ignore_eta else ""),
            )

            # Extend quality with profile customs
            quality["custom"] = quality_custom

            results = fireEvent("searcher.search", search_protocols, movie, quality, single=True) or []
            results_count = len(results)
            total_result_count += results_count
            if results_count == 0:
                log.debug("Nothing found for %s in %s", (default_title, quality["label"]))

            # Keep track of releases found outside ETA window
            outside_eta_results += results_count if could_not_be_released else 0

            # 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, single=True)

            # Don't trigger download, but notify user of available releases
            if could_not_be_released:
                if results_count > 0:
                    log.debug('Found %s releases for "%s", but ETA isn\'t correct yet.', (results_count, default_title))

            # Try find a valid result and download it
            if (force_download or not could_not_be_released or alway_search) and fireEvent(
                "release.try_download_result", results, movie, quality_custom, single=True
            ):
                ret = True

            # Remove releases that aren't found anymore
            temp_previous_releases = []
            for release in previous_releases:
                if release.get("status") == "available" and release.get("identifier") not in found_releases:
                    fireEvent("release.delete", release.get("_id"), single=True)
                else:
                    temp_previous_releases.append(release)
            previous_releases = temp_previous_releases
            del temp_previous_releases

            # Break if CP wants to shut down
            if self.shuttingDown() or ret:
                break

        if total_result_count > 0:
            fireEvent("media.tag", movie["_id"], "recent", update_edited=True, single=True)

        if len(too_early_to_search) > 0:
            log.info2("Too early to search for %s, %s", (too_early_to_search, default_title))

            if outside_eta_results > 0:
                message = 'Found %s releases for "%s" before ETA. Select and download via the dashboard.' % (
                    outside_eta_results,
                    default_title,
                )
                log.info(message)

                if not manual:
                    fireEvent("media.available", message=message, data={})

        fireEvent("notify.frontend", type="movie.searcher.ended", data={"_id": movie["_id"]})

        return ret