Skip to content

Generic upgrade and timeframe plugins#2036

Merged
stevezau merged 44 commits intodevelopfrom
generic_upgrade_plugin
Jan 8, 2018
Merged

Generic upgrade and timeframe plugins#2036
stevezau merged 44 commits intodevelopfrom
generic_upgrade_plugin

Conversation

@stevezau
Copy link
Copy Markdown
Contributor

@stevezau stevezau commented Dec 9, 2017

Motivation for changes:

Currently, flexget does not support the concept of movie upgrade/timeframes. Series upgrades/timeframe are possible but the code is messy. There is no easy/generic way to upgrade entry qualities or wait for a timeframe before accepting the best quality.

Detailed changes:

The following plugins are meant to support generic timeframes/upgrades by tracking new field id. This field is set by metainfo plugins.

@stevezau stevezau self-assigned this Dec 9, 2017

def prepare_config(self, config):
if not config or isinstance(config, bool):
return {'identified_by': 'auto', 'on_lower': 'skip', 'tracking': True}
Copy link
Copy Markdown
Contributor

@cvium cvium Dec 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather the defaults were set only in prepare_config with dict.setdefault(). We don't really want to maintain the default values in two places

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish there was a better way.. I wish we didn't near prepare_config at all. But we need it because it's oneOf

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind prepare_config.


# Action lower qualities
if config['on_lower'] != 'skip':
entry_actions = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor thing: You're recreating this dict for every identifier.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to module level

# Group by Identifier
for entry in entries:
identifier = entry.get('id') if identified_by == 'auto' else entry.render(identified_by)
if identifier:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just my personal preference, I prefer to keep the flow control leaner:

if not identifier:
    log.debug('No identifier found for', entry['title'])
    continue
grouped_entries[identifier.lower()].append(entry)

just a suggestion obviously

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

@stevezau stevezau changed the title [WIP] Generic upgrade plugin [WIP] Generic upgrade and timeframe plugins Dec 28, 2017
target_downloaded = False

if config['target']:
target_quality = qualities.Quality(config['target'])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is returned if the user inserted a quality requirement and not a quality?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on my tests it returns the highest quality

target_quality = qualities.Quality(config['target'])
target_requirement = qualities.Requirements(config['target'])
# Filter out entries within target
entries = list(filter(lambda e: target_requirement.allows(e['quality']), entries))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like list comprehension here better too...

entries = [e if target_requirement.allows(e['quality']) for e in entries]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but it should filter out ones that don't match? that will just include them all again.. you are missing the else part..

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since it runs on the same variable it'll just recreate it with those matching the statement. no need for else in list comprehension

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entries = [e if target_requirement.allows(e['quality']) for e in entries] is not valid tho? You need an else after the if?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops sorry:
entries = [e for e in entries if target_requirement.allows(e['quality'])]

continue

# We may have no entries within target
if len(entries) == 0:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more pythonic to write if not entries

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already fixed this, will push later


# Action lower qualities
if config['on_lower'] != 'skip':
for entry in entries:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldnt you pop the first entry to not iterate over it twice?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure it really matters?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well i think itll try to accept it twice, not sure if that's an issue or not

_quality = Column('quality', String)
quality = quality_property('_quality')
proper = Column(Boolean)
added = Column(DateTime, index=True)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can set default=datetime.now() here and not handle in via the plugin

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah missed that

existing = EntryUpgrade()
existing.id = identifier
session.add(existing)
else:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe change this to elif

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, done

for entry in entries:
action = entry_actions[config['on_lower']]
if entry['quality'] > existing.quality and target_downloaded:
msg = 'on_lower %s because already at target quality' % config['on_lower']
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These messages sound weird. Maybe create a mapping between the config values and the proper tense. And remove the on_lower portion.

Secondly, I think it would be better to change the if-blocks to

if something:
  do stuff
elif other something:
  do other stuff
elif third something:
  do third stuff
else:
  continue
action(entry, msg)

# No existing, skip
continue

target_downloaded = False
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this will be used somehow later, so this is just a reminder that this variable is currently never True.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah for later use, still wip

@stevezau stevezau changed the title [WIP] Generic upgrade and timeframe plugins Generic upgrade and timeframe plugins Dec 28, 2017
Copy link
Copy Markdown
Member

@liiight liiight left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe some more logging?

# Action lower qualities
if config['on_lower'] != 'skip':
for entry in entries:
if entry == best:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this since you used pop

proper_count = Column(Integer, default=0)

def __init__(self):
self.first_seen = datetime.now()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for this, you init it in the column declaration


def __init__(self):
self.first_seen = datetime.now()
self.updated = datetime.now()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you didn't create a corresponding column for updated

try:
self.backlog = plugin.get_plugin_by_name('backlog')
except plugin.DependencyError:
log.warning('Unable to utilize backlog plugin, so episodes may slip through timeframe.')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add self.backlog = None or it will crash later on

identified_by = '{{ id }}' if config['identified_by'] == 'auto' else config['identified_by']

grouped_entries = group_entries(task.accepted + task.undecided, identified_by)
if len(grouped_entries) == 0:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not grouped_entries: is more pythonic


id_timeframe.title = best_entry['title']
id_timeframe.quality = best_entry['quality']
id_timeframe.title = best_entry.get('proper_count', 0)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you probably meant id_timeframe.proper_count here

identified_by = '{{ id }}' if config['identified_by'] == 'auto' else config['identified_by']

grouped_entries = group_entries(task.accepted, identified_by)
if len(grouped_entries) == 0:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not grouped_entries

first_seen = Column(DateTime, default=datetime.now)
updated = Column(DateTime, index=True, default=datetime.now)

def __init__(self):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to init, you declared the defaults already

}

def prepare_config(self, config):
if config is None or config is False:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not config

identified_by = '{{ id }}' if config['identified_by'] == 'auto' else config['identified_by']

grouped_entries = group_entries(task.accepted + task.undecided, identified_by)
if len(grouped_entries) == 0:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not grouped_entries

identified_by = '{{ id }}' if config['identified_by'] == 'auto' else config['identified_by']

grouped_entries = group_entries(task.accepted, identified_by)
if len(grouped_entries) == 0:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not grouped_entries

@stevezau
Copy link
Copy Markdown
Contributor Author

stevezau commented Jan 8, 2018

Reviewed by @liiight

@stevezau stevezau merged commit 9544f56 into develop Jan 8, 2018
@stevezau stevezau deleted the generic_upgrade_plugin branch January 8, 2018 11:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants