Source code for flexget.plugins.filter.upgrade

from datetime import datetime

from loguru import logger
from sqlalchemy import Column, DateTime, Integer, String, Unicode

from flexget import db_schema, plugin
from flexget.db_schema import Session
from flexget.entry import Entry
from flexget.event import event
from flexget.utils import qualities
from flexget.utils.database import quality_property
from flexget.utils.tools import group_entries, parse_timedelta

logger = logger.bind(name='upgrade')

Base = db_schema.versioned_base('upgrade', 0)

entry_actions = {'accept': Entry.accept, 'reject': Entry.reject, 'fail': Entry.fail}


[docs] class EntryUpgrade(Base): __tablename__ = 'upgrade' id = Column(Unicode, primary_key=True, index=True) title = Column(Unicode) _quality = Column('quality', String) quality = quality_property('_quality') proper_count = Column(Integer, default=0) first_seen = Column(DateTime, default=datetime.now) updated = Column(DateTime, index=True, default=datetime.now) def __str__(self): return f'<Upgrade(id={self.id},added={self.added},quality={self.quality})>'
[docs] class FilterUpgrade: schema = { 'type': 'object', 'properties': { 'identified_by': {'type': 'string'}, 'tracking': {'type': 'boolean'}, 'target': {'type': 'string', 'format': 'quality_requirements'}, 'on_lower': {'type': 'string', 'enum': ['accept', 'reject', 'do_nothing']}, 'timeframe': {'type': 'string', 'format': 'interval'}, 'propers': {'type': 'boolean'}, }, 'additionalProperties': False, }
[docs] def prepare_config(self, config): if not config or config is False: return None if config is True: config = {} config.setdefault('identified_by', 'auto') config.setdefault('tracking', True) config.setdefault('on_lower', 'do_nothing') config.setdefault('target', None) config.setdefault('timeframe', None) config.setdefault('propers', True) return config
[docs] def filter_entries(self, entries, existing, target, action_on_lower): target_requirement = qualities.Requirements(target) if target else None filtered = [] for entry in entries: # Filter out entries within target if target and not target_requirement.allows(entry['quality']): logger.debug( 'Skipping {} as does not meet upgrade quality requirements', entry['title'] ) if action_on_lower: action_on_lower(entry, 'does not meet upgrade quality requirements') continue if entry['quality'] < existing.quality: logger.debug('Skipping {} as lower quality then existing', entry['title']) if action_on_lower: action_on_lower(entry, 'lower quality then existing') continue if ( entry['quality'] == existing.quality and entry.get('proper_count', 0) <= existing.proper_count ): logger.debug('Skipping {} as same quality but lower proper', entry['title']) if action_on_lower: action_on_lower(entry, 'lower proper then existing') continue filtered.append(entry) return filtered
[docs] def on_task_filter(self, task, config): config = self.prepare_config(config) if not config or not config['target']: return identified_by = ( '{{ media_id }}' if config['identified_by'] == 'auto' else config['identified_by'] ) grouped_entries = group_entries(task.accepted + task.undecided, identified_by) if not grouped_entries: return with Session() as session: # Prefetch Data existing_ids = ( session .query(EntryUpgrade) .filter(EntryUpgrade.id.in_(grouped_entries.keys())) .all() ) existing_ids = {e.id: e for e in existing_ids} for identifier, entries in grouped_entries.items(): if not entries: continue existing = existing_ids.get(identifier) if not existing: # No existing, do_nothing continue logger.debug( 'Looking for upgrades for identifier {} (within {} entries)', identifier, len(entries), ) # Check if passed allowed timeframe if config['timeframe']: expires = existing.first_seen + parse_timedelta(config['timeframe']) if expires <= datetime.now(): # Timeframe reached, allow logger.debug( 'Skipping upgrade with identifier {} as timeframe reached', identifier ) continue # Filter out lower quality and propers action_on_lower = ( entry_actions[config['on_lower']] if config['on_lower'] != 'do_nothing' else None ) upgradeable = self.filter_entries( entries, existing, config['target'], action_on_lower ) # Skip if we have no entries after filtering if not upgradeable: continue # Sort entities in order of quality and best proper upgradeable.sort( key=lambda e: (e['quality'], e.get('proper_count', 0)), reverse=True ) # First entry will be the best quality best = upgradeable.pop(0) best.accept('upgraded quality') logger.debug( 'Found {} as upgraded quality for identifier {}', best['title'], identifier ) # Process rest for entry in upgradeable: logger.debug( 'Skipping {} as lower quality then best {}', entry['title'], best['title'] ) if action_on_lower: action_on_lower(entry, 'lower quality then best match')
[docs] def on_task_learn(self, task, config): config = self.prepare_config(config) if not config or not config['tracking']: return identified_by = ( '{{ media_id }}' if config['identified_by'] == 'auto' else config['identified_by'] ) grouped_entries = group_entries(task.accepted, identified_by) if not grouped_entries: return with Session() as session: # Prefetch Data existing_ids = ( session .query(EntryUpgrade) .filter(EntryUpgrade.id.in_(grouped_entries.keys())) .all() ) existing_ids = {e.id: e for e in existing_ids} for identifier, entries in grouped_entries.items(): if not entries: continue # Sort entities in order of quality entries.sort(key=lambda e: e['quality'], reverse=True) # First entry will be the best quality best_entry = entries[0] existing = existing_ids.get(identifier) if not existing: existing = EntryUpgrade() existing.id = identifier session.add(existing) elif existing.quality > best_entry['quality']: continue existing.quality = best_entry['quality'] existing.title = best_entry['title'] existing.proper_count = best_entry.get('proper_count', 0) existing.updated = datetime.now() logger.debug( 'Tracking upgrade on identifier `{}` current quality `{}`', identifier, best_entry['quality'], )
[docs] @event('plugin.register') def register_plugin(): plugin.register(FilterUpgrade, 'upgrade', api_ver=2)