Flexget icon indicating copy to clipboard operation
Flexget copied to clipboard

Quality Recognization Error

Open YihaoJW opened this issue 4 weeks ago • 9 comments

Summary

Issue Summary

Two releases are being parsed differently even though one of them contains a strictly better HDR profile:

  • Name: Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HDR10+ HEVC-NTb
    Detected: 2160p webdl h265 hdr dd+5.1 (wrong)

  • Name: Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HEVC-NTb
    Detected: 2160p webdl h265 dolbyvision dd+5.1 (correct)

The first release contains Dolby Vision + HDR10+, but is only recognized as standard hdr (rather than hdrplus or dolbyvision or hybrid_hdr) , causing the quality comparer to incorrectly allow a downgrade when an upgrade check is performed. (Flexget will make second one a upgrade of first one)


Proposed Improvement

The first one should be recognized as hybird_hdr. After that there is a issue that hybird_hdr do not have internal logic does not distinguish different mixed HDR formats by relative quality.
I suggest enhancing the hybrid_hdr handling with a simple scoring-based model so that hybrid HDR stacks evaluate correctly.

Example Scoring:

  • Dolby Vision = 40
  • HDR10+ = 20
  • HDR10 = 10

Applied to the above releases:

  • DoVi + HDR10+ → 60 (correctly highest)
  • DoVi + HDR10 → 50
  • DoVi only → 40

This prevents scenarios where a lower-quality DoVi-only release is treated as an upgrade over a DoVi+HDR10+ release.


Additional Suggestion

reorder_quality should support assigning Hybrid_HDR qualities based on this scoring structure.
This ensures the comparer can reliably rank hybrid HDR releases and avoid unintended downgrades.

Minimal reproducible config


Log


Additional information

FlexGet Version: 3.18.28 Python Version: OS Version: Installation Method: Docker Using Daemon (yes/no): Yes

YihaoJW avatar Dec 01 '25 18:12 YihaoJW

The parsing is already scored https://github.com/Flexget/Flexget/blob/f73d6fa46e78af3aeec6ba5a0f1e2b6a714ca9b3/flexget/utils/qualities.py#L158-L169

BrutuZ avatar Dec 01 '25 19:12 BrutuZ

Also, cannot repro the issue, hybrid was recognized as expected

tasks:
  test:
    mock:
      - Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HDR10+ HEVC-NTb
      - Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HEVC-NTb
    series:
       - Pluribus
title               : Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HDR10+ HEVC-NTb
quality             : 2160p webdl h265 hybrid_hdr dd+5.1

title               : Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HDR10+ HEVC-NTb
quality             : 2160p webdl h265 dolbyvision dd+5.1

BrutuZ avatar Dec 01 '25 20:12 BrutuZ

The parsing is already scored

Flexget/flexget/utils/qualities.py

Lines 158 to 169 in f73d6fa

hdr = r'hdr([^\w]?10)?' hdr_plus = r'hdr(10)?[^\w]?(+|p|plus)' dovi = r'(dolby[^\w]?vision|dv|dovi)' _color_ranges = [ QualityComponent('color_range', 10, '8bit', r'8[^\w]?bits?|hi8p?|sdr'), QualityComponent('color_range', 20, '10bit', r'10[^\w]?bits?|hi10p?'), QualityComponent('color_range', 30, 'hdr', hdr), QualityComponent('color_range', 40, 'hdrplus', hdr_plus), QualityComponent('color_range', 50, 'dolbyvision', dovi), QualityComponent('color_range', 60, 'hybrid_hdr', f'(({dovi}|{hdr_plus}|{hdr})\W?){{2,3}}'), ]

Thanks for sharing the code and example, from my understanding hybrid_hdr is currently treated as a single fixed value (60), so it can’t differentiate combinations (e.g., DoVi+HDR10+ vs DoVi+HDR10). So if it supports combo-aware/additive scoring for this field, hybrid formats can be ranked more accurately and future proof if more combination is possible.

Also, cannot repro the issue, hybrid was recognized as expected

tasks: test: mock: - Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HDR10+ HEVC-NTb - Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HEVC-NTb series: - Pluribus

title               : Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HDR10+ HEVC-NTb
quality             : 2160p webdl h265 hybrid_hdr dd+5.1

title               : Pluribus S01E05 2160p WEB-DL DD+5.1 DoVi HDR10+ HEVC-NTb
quality             : 2160p webdl h265 dolbyvision dd+5.1

Thanks for clarification, I rerun the test it's recognized correctly with 3.18.28.

YihaoJW avatar Dec 02 '25 07:12 YihaoJW

@BrutuZ After another experiment, I found something strange.

When I using flexget exec --task task_name --dump trace --no-cache --now the dump result show correct quality

quality : 2160p webdl h265 hybrid_hdr dd+5.1 reason : target quality release_group : None rss_pubdate : 2025-12-05 02:14:54+00:00 season_pack : None series_episode : 6 series_episodes : 1 series_exact : False series_guessed : True series_id : S01E06 series_id_type : ep series_identified_by : ep series_name : Pluribus series_parser : <SeriesParseResult(data=Pluribus S01E06 2160p WEB-DL DD+5.1 DoVi HDR10+ HEVC-NTb,name=Pluribus,id=(1, 6),season=1,season_pack=None,episode=6,quality=2160p webdl h265 hybrid_hdr dd+5.1,proper=0,special=False,status=OK)>

But after the entry has been accept I run flexget series show Pluribus to confirm what series plugin record, and it show a different record.

The Table show a different quality result

Identifier Last seen Release titles Quality Proper
S01E06 0h Pluribus S01E06 2160p WEB-DL DD+5.1 DoVi HDR10+ HEVC-NTb * 2160p webdl h265 hdr dd+5.1

Here is my configuration that related to this task

templates:
  site_general:
    transmission:
      host: transmission
      username: REPLACE_ME
      password: REPLACE_ME
      path: /data/SITE

tasks:
  site_watchlist_shows:
    no_entries_ok: yes
    template: site_general
    rss:
      url: "REPLACE_WITH_PRIVATE_RSS_URL"
      all_entries: no
    priority: 5

    trakt_lookup: yes
    imdb_lookup: yes
    tmdb_lookup: yes

    all_series:
      timeframe: 6 hours
      quality: 1080p+ webdl
      target: 2160p+ dolbyvision+ webdl
      upgrade: yes
      season_packs: always
      propers: yes

    reorder_quality:
      webdl:
        above: bluray

    sort_by:
      field: quality
      reverse: yes

    best_quality:
      identified_by: "{{ media_id }} {{ series_id|default('') }}"
      on_best: do_nothing
      on_lower: reject
      single_best: no

    set:
      path: "/data/SITE/{{series_name}}/Season {{series_season}}/"
      bandwidth_priority: 1

    exists_series:
      path: "/data/SITE/{{series_name}}/"
      allow_different_qualities: better

Not sure why series plugin log the quality difference than what tasks emitting.

That also the reason I find originally believe it's a mismatch.

YihaoJW avatar Dec 06 '25 09:12 YihaoJW

Based on my initial analysis, the root cause appears to be:

  1. It originally correctly detected as 2160p webdl h265 hybrid_hdr dd+5.1.
  2. It was saved into the database using the detected results 2160p webdl h265 hybrid_hdr dd+5.1 rather than the original input.
  3. When retrieving from the database, 2160p webdl h265 hybrid_hdr dd+5.1 will convert into hdr due to the matching rule. (hybrid_hdr will be based on hdr rather than hybrid_hdr) If we change add hybird_hdr matching to it should quickly solve the problem

https://github.com/Flexget/Flexget/blob/f73d6fa46e78af3aeec6ba5a0f1e2b6a714ca9b3/flexget/utils/qualities.py#L167 QualityComponent('color_range', 60, 'hybrid_hdr', f'(({dovi}|{hdr_plus}|{hdr})\\W?){{2,3}}|hybrid[\\W_]?hdr'),

My local test passes with this patch: running flexget series show Pluribus will print a table that quality is hybrid_hdr

That said, I’m not fully confident this is the best fix yet—it may have unintended side effects in other components.

YihaoJW avatar Dec 06 '25 10:12 YihaoJW

When retrieving from the database, 2160p webdl h265 hybrid_hdr dd+5.1 will convert into hdr due to the matching rule. (hybrid_hdr will be based on hdr rather than hybrid_hdr)

No idea how that could be the case, the RegEx matching is done on the title, not on the string representation of the recognized quality

BrutuZ avatar Dec 06 '25 19:12 BrutuZ

If I’m understanding correctly, the EpisodeRelease class reads data from the dataset via SQLAlchemy ORM, including: id, title, identifier, quality, series_id.

Looking at the series-related file: the quality column read from the database becomes _quality, is wrapped by quality_property, and is exposed as quality. That means when quality is accessed, it does not read from title. https://github.com/Flexget/Flexget/blob/35c1cef147d403ea16efa980a64d64f782d7b9b0/flexget/components/series/db.py#L418-L419

quality_property sends the stored attribute through the Qualities plugin: https://github.com/Flexget/Flexget/blob/35c1cef147d403ea16efa980a64d64f782d7b9b0/flexget/utils/database.py#L148-L156

So, when a record is read, it starts from the database quality column, gets processed through quality_property, and becomes release.quality.

However, when it is saved, the quality has already been set using quality: Quality, and when accessing quality.name, it returns a hybird_hdr name rather than the original name. https://github.com/Flexget/Flexget/blob/35c1cef147d403ea16efa980a64d64f782d7b9b0/flexget/components/series/db.py#L1507

And since quality has a backing variable _quality, the setter shown above uses quality.name to set _quality, which is ultimately what gets saved into the database.

So the full chain maps quality through the Qualities plugin twice. For hybird_hdr, the repeated process is not idempotent, but for the others it is (e.g., DoVi -> dolbyvision -> dolbyvision -> ..., HDR10 -> hdr -> hdr -> ..., instead of DoVi HDR -> hybird_hdr -> hdr -> hdr -> ....)

That means, if I’m not misunderstanding (I’m new to this project’s source code, so I may be interpreting it wrong): when a record is fetched from the dataset, the qualities are based on the stored quality string (not the title), and that quality string is then sent through the Qualities plugin again for processing. From that process, only hybird_hdr has an issue, since it replaces the original DoVi HDR with hybird_hdr, and the next time it won’t match as hybird_hdr but as hdr.

That also explains what I saw earlier: when dumping, it was recognized as hybird_hdr, but when running flexget series show Pluribus, the table shows a different result.

I applied a quick fix:

  1. I patched it to QualityComponent('color_range', 60, 'hybrid_hdr', f'(({dovi}|{hdr_plus}|{hdr})\\W?){{2,3}}|hybrid[\\W_]?hdr'), by adding hybrid[\\W_]?hdr at score 60 to make the mapping idempotent.
  2. Restarted the FlexGet Docker container, and
  3. Ran flexget series show Pluribus again — it returned hybird_hdr in the result table.

In other words, after the patch I didn’t need to rerun any tasks or modify the database. Just reading the existing table now produces the correct result in the CLI and Web page.

YihaoJW avatar Dec 07 '25 01:12 YihaoJW

I see, so the issue is not the expression per-se, but the way it's saved on the DB as a string and then re-parsed with the RegEx instead of using the names directly.

I suppose this could be a better approach to hard-coding the title in the RegEx? https://github.com/Flexget/Flexget/blob/35c1cef147d403ea16efa980a64d64f782d7b9b0/flexget/utils/database.py#L150

-        return qualities.Quality(getattr(self, text_attr))
+        return qualities.get(getattr(self, text_attr))

BrutuZ avatar Dec 07 '25 02:12 BrutuZ

https://github.com/Flexget/Flexget/issues/4760#issuecomment-3621527351 Yes — that would be a more fundamental fix. Thanks for the suggestion/change. 👍

The core issue isn’t really the regex itself, but that the value gets processed twice: it’s stored in the DB as a string though Quality(...), and then later parsed again via Quality(...). For most quality strings that’s effectively idempotent, but hybrid_hdr isn’t, so the second pass can change the content.

Since I’m still new to this repo, I wasn’t confident that changing the behavior around the DB-backed values wouldn’t introduce side effects in other parts of the project. So I went with a safer workaround in my own running environment by make hybrid_hdr idempotent.

YihaoJW avatar Dec 07 '25 04:12 YihaoJW

I generated a pull request https://github.com/Flexget/Flexget/pull/4779. To apply suggested change on https://github.com/Flexget/Flexget/issues/4760#issuecomment-3621527351

YihaoJW avatar Dec 12 '25 20:12 YihaoJW