# 006 - Search Dialog

**Aktualizováno:** 2026-02-08 22:21

---

## Co to je

Search Dialog je centralni UI pro vyber souboru k prehrani. Uzivatel vidi metadata filmu/epizody (poster, nazev, rok, zanr, hodnoceni, popis) a pod nimi serazeny seznam souboru nalezenych na Webshare.cz a/nebo KRA.sk. Nad seznamem jsou 3 akcni tlacitka ve stylu pill-shaped: **Prehrat**, **Stahnout**, **Hrat hru**.

---

## Soubory

| Soubor | Ucel |
|--------|------|
| [DialogWcsSearch.py](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py) | Python trida `SearchDialog` + vstupni funkce `show_search_dialog()` (1144 radku) |
| [DialogWcsSearch.xml](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/resources/skins/Default/1080i/search/DialogWcsSearch.xml) | XML layout dialogu (700 radku) |

---

## Vstupni bod: `show_search_dialog()`

Toto je **jedina funkce**, kterou volaji ostatni moduly. Je definovana na [radku 988](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L988).

### Signatura

```python
def show_search_dialog(
    search_query,               # Dotaz pro Webshare/KRA.sk API (povinny)
    movie_title='',             # Zobrazeny nazev v hlavicce dialogu
    movie_year='',              # Rok vydani
    poster_url='',              # URL posteru (vzdy poster serialu, ne still epizody)
    rating='',                  # Hodnoceni z TMDb (napr. "7.5")
    genre='',                   # Zanry (napr. "Drama, Thriller")
    director='',                # Reziser
    runtime='',                 # Delka (napr. "120 min")
    plot='',                    # Popis deje
    search_results=[],          # Predpripravene vysledky NEBO [] = dialog vyhledava sam
    is_episode=False,           # True = epizoda serialu
    series_name='',             # Nazev serialu (pro autoplay a hledani)
    season_number=None,         # Cislo sezony (int)
    episode_number=None,        # Cislo epizody (int)
    tmdb_id=None,               # TMDb ID (int)
    preferred_quality=None,     # Preferovana kvalita pro razeni (napr. "1080p")
    autoplay_request=False,     # True = automaticky vybere prvni soubor BEZ zobrazeni UI
    fanart_url='',              # Fanart URL pro pozadi dialogu
    episode_title='',           # Nazev epizody (napr. "Pilot")
    episode_thumb='',           # Still epizody (pouziva se pro info dialog, NE pro poster UI)
    schedule_episode_info=False,# Naplanovat info dialog po prehrani
    from_library=False,         # True = volano z Kodi knihovny (ovlivnuje autoplay)
    manual_search=False         # True = rucni hledani (skryje TMDb metadata)
)
# Vraci: dict {'ident': str, 'name': str, 'provider': str} nebo None
```

### Autoplay shortcut (bez UI)

Pokud `autoplay_request=True` nebo je v nastaveni `autoplay_first_item=True`, dialog se **nezobrazuje**. Funkce vytvori docasnou instanci `SearchDialog`, nacte vysledky pres `_perform_search()`, a vrati prvni soubor. Pokud je nastavena `preferred_quality`, hleda soubor s odpovidajici kvalitou.

> [!IMPORTANT]
> Autoplay se spusti POUZE pokud je globalni nastaveni `autoplay_enabled_global` zapnuto.

### Normalni flow (s UI)

1. Vytvori instanci `SearchDialog` s XML `search/DialogWcsSearch.xml`
2. Zavola `dialog.doModal()` -- blokujici, ceka na uzivateluv vyber
3. Po zavreni dialogu vrati `dialog.selected_result` (dict nebo None)

---

## Trida `SearchDialog`

Dedi z `xbmcgui.WindowXMLDialog`. XML soubor: `search/DialogWcsSearch.xml`.

### `__init__()` ([radek 56](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L56))

Ulozi vsechna kwargs do `self.*` atributu. Klicove atributy:

| Atribut | Typ | Vyznam |
|---------|-----|--------|
| `self.search_query` | str | Hledany vyraz |
| `self.search_results` | list | Predane vysledky nebo [] |
| `self.selected_result` | dict/None | Vysledek vyberu uzivatelem |
| `self.is_episode` | bool | Rozliseni film vs. epizoda |
| `self.autoplay_enabled` | bool | Stav autoplay prepinace |
| `self.from_library` | bool | Zdroj volani |
| `self.manual_search` | bool | Rucni hledani |
| `self.autoplay_manager` | AutoplayManager | Singleton pro rizeni autoplay |

> [!NOTE]
> `self.autoplay_enabled` je `True` pouze pokud: `is_episode=True` AND `autoplay_enabled_global=True` (nastaveni addonu).

---

### `onInit()` ([radek 102](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L102))

Sekvence inicializace:

1. **Nastaveni Window properties** -- vsechna metadata se zapisi jako `WCS.Search.*` properties pro XML:

| Property | Zdroj |
|----------|-------|
| `WCS.Search.Title` | `"Hledany vyraz: {search_query}"` |
| `WCS.Search.Info` | `"Hledany vyraz: {search_query}"` |
| `WCS.Search.Poster` | `poster_url` |
| `WCS.Search.MovieTitle` | `movie_title` |
| `WCS.Search.Fanart` | `fanart_url` |
| `WCS.Search.Rating` | `rating` (formatovano jako "X.X/10") |
| `WCS.Search.Genre` | `genre` |
| `WCS.Search.Director` | `director` |
| `WCS.Search.Runtime` | `runtime` |
| `WCS.Search.Year` | `movie_year` |
| `WCS.Search.Plot` | `plot` |
| `WCS.Search.EpisodeTitle` | `episode_title` |
| `WCS.Episode.IsEpisode` | `"true"/"false"` |
| `WCS.Episode.FromLibrary` | `"true"/"false"` |
| `WCS.Autoplay.Label` | Dynamicky text o stavu autoplay |

2. **Manual search override** -- pokud `manual_search=True`:
   - `MovieTitle` = `"Webshare: {query}"`
   - `Poster` = ikona addonu
   - `Plot` = informace o primem hledani
   - Vsechna ostatni metadata se vynuluji

3. **Vyhledavani** -- pokud `search_results` je prazdny, zavola `_perform_search()`. Zobrazuje busy dialog behem hledani.

4. **Razeni** vysledku podle typu:
   - Epizoda: `sort_series_files_by_priority()` (z `SeriesSearch.py`)
   - Film: `sort_files_by_priority(filter_series=True)` (z `MovieSearch.py`)
   - Manual search: `sort_files_by_priority(filter_series=False)`

5. **Naplneni UI**:
   - `_populate_results_list()` -- naplni seznam souboru (control 50)
   - `_populate_action_buttons()` -- naplni pill-shaped tlacitka (control 103)

6. **Focus** -- nastavi na control 103 (akcni tlacitka). Vyjimka: pro `DialogAIChatSearch.xml` focus na control 50.

---

### `_perform_search()` ([radek 264](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L264))

Zjisti ktere providery jsou zapnute (`webshare_enabled`, `kraska_enabled`) a zavola prislusne metody:

- `_perform_search_webshare()` ([radek 479](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L479)) -- Webshare API, limit 50, pro epizody pouziva `do_webshare_search_with_retry()`, alternativni dotaz pokud 0 vysledku
- `_perform_search_kraska()` ([radek 305](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L305)) -- Stream Cinema API, konvertuje SC format na Webshare-like dict

Kazdy vysledek ma klice: `name`, `ident`, `size`, `provider` (`'webshare'`/`'kraska'`), `positive_votes`, `negative_votes`.

---

### `_populate_results_list()` ([radek 669](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L669))

Pro kazdy soubor ze `self.search_results`:

1. Extrahuje metadata z nazvu souboru: `extract_clean_title()`, `extract_quality()`, `extract_language()`, `extract_format()`, `format_size()`, `calculate_rating()`
2. Filtruje soubory s neodpovidajici velikosti (`is_valid_file_size()`)
3. Vytvori `xbmcgui.ListItem` s properties:

| Property | Obsah |
|----------|-------|
| `clean_title` | Cisteny nazev |
| `quality` | Barevne formatovana kvalita (napr. `[COLOR FF44AAFF]1080p[/COLOR]`) |
| `quality_raw` | Surovy text kvality (`4K`, `1080p`, `720p`, `480p`, `SD`) |
| `quality_color` | Barva quality pill (`FFFECC33` zlata pro 4K, `FF44AAFF` modra pro 1080p, atd.) |
| `quality_wide` | `"1"` pro sirsi pill (1080p/720p/480p), `"0"` pro uzsi (4K/SD) |
| `metadata_line` | Slouceny retezec: jazyk · format · velikost · rating s `[COLOR]` tagy |
| `language` | Jazyk (cz, en, ...) |
| `format` | Format souboru (mkv, avi, ...) |
| `format_upper` | Format velkymi pismeny (MKV, AVI, ...) |
| `size_formatted` | Velikost (napr. "4.2 GB") |
| `user_rating` | Hodnoceni souboru |
| `ident` | Webshare ident nebo SC provider path |
| `filename` | Plny nazev souboru |
| `provider` | `"webshare"` nebo `"kraska"` |
| `provider_name` | `"webshare.cz"` nebo `"kra.sk"` |
| `provider_color` | Barva badge (`FF00B4D8` tyrkysova / `FFFF69B4` ruzova) |

Pokud je nastavena `preferred_quality`, soubory se prerazuji a oznaci hvezdickou.

---

### `_populate_action_buttons()` ([radek 581](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L581))

Naplni list control 103 tremi polozkami:

| action_id | Label | Akce |
|-----------|-------|------|
| `play_best` | Prehrat | `_play_best_item()` -- vybere 1. soubor |
| `download` | Stahnout | `_open_download_picker()` -- otevre download dialog |
| `play_game` | Hrat hru | `start_game()` -- spusti textovou hru |

Kazda polozka ma property `action_id` pro identifikaci v `onClick()`.

---

### `onClick()` ([radek 767](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L767))

| Control ID | Akce |
|------------|------|
| **50** (seznam vysledku) | `_play_selected_item()` -- prehrani vybraneho souboru |
| **103** (akcni tlacitka) | Precte `action_id` z vybrane polozky a zavola prislusnou metodu |
| **71** (autoplay toggle) | Prepne `self.autoplay_enabled` (pouze pro epizody z knihovny) |

---

### `_play_selected_item()` / `_play_best_item()` ([radek 601](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L601))

Obe metody:
1. Ziskaji `ident`, `name`, `provider` z vybrane polozky
2. Ulozi do `self.selected_result = {'ident': ..., 'name': ..., 'provider': ...}`
3. Zavolaji `_handle_autoplay_and_close()`

Rozdil: `_play_best_item()` vzdy vybere polozku na indexu 0 (nejlepsi podle razeni).

---

### `_handle_autoplay_and_close()` ([radek 636](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L636))

1. Pokud je epizoda a autoplay zapnuty: nastavi `AutoplayManager` (singleton z `DialogMediaInfoWithGame`)
2. Pokud je epizoda a **neni** z knihovny: naplanuje info dialog pres `schedule_episode_info_dialog()` z `wcs/utils.py`
3. Zavola `self.close()`

> [!IMPORTANT]
> Info dialog se neplanuje pro epizody z knihovny -- ty maji vlastni mechanismus v `LibraryManager._activate_episode_info_only()`.

---

### `start_game()` ([radek 903](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L903))

Spusti `TextGameWindow` (dialog pro textovou AI hru). Predava metadata (title, plot, genre, media_type, episode info). U serialu nastavi `media_type="series"` a preda `season_number`/`episode_number`.

---

### `_open_download_picker()` ([radek 803](file:///Users/chudy/Library/Application%20Support/Kodi/addons/plugin.video.milionar/wcs/ui/dialogs/DialogWcsSearch.py#L803))

Importuje `DialogWcsDownload.show_download_dialog()` a preda ji vsechna metadata + vysledky hledani.

---

## XML layout -- control IDs

| ID | Typ | Ucel |
|----|-----|------|
| -- | `image` | Dimming overlay (fade animace, `CC000000`) |
| -- | `group` | Hlavni panel (slide-up animace, 1064x840, vycentrovany, `E0` pruhlednost) |
| -- | `image` | Poster (100x150, diffuse maska pro zakulacene rohy) |
| -- | `label` | MovieTitle, Year+Genre+Runtime+Rating |
| **103** | `list` (horizontal) | Akcni tlacitka (pill-shaped, height 60px) |
| -- | `label` | Hledany vyraz pruh (badge_pill pozadi) |
| **50** | `panel` | Seznam vysledku (vertikalni, scrollovatelny, height=97px na polozku) |
| **71** | `button` | Autoplay toggle (dole) |
| **60** | `scrollbar` | Vertikalni scrollbar (zakulaceny, 5px siroka) |

### Animace

| Udalost | Efekt | Trvani |
|---------|-------|--------|
| WindowOpen | Dimming fade 0→100% | 1500ms |
| WindowOpen | Panel slide zdola | 400ms (cubic easing) |
| WindowClose | Dimming fade 100→0% | 250ms |
| WindowClose | Panel slide dolu | 250ms (cubic easing) |

Zavreni je zamerne rychle (250ms) pro plynuly prechod do prehravani.

### Pruhlednost panelu

Panel pozadi: `E0000000` (88% opacita) -- mirne prosvita fanart/pozadi pod nim.

### Vizualni design

#### Poster se zakulacenymi rohy

Poster pouziva `diffuse` atribut pro orez zakulacenych rohu:

```xml
<texture diffuse=".../poster_mask_rounded.png">$INFO[...Poster]</texture>
```

Maska `poster_mask_rounded.png` (100x150px): bila oblast = viditelna, pruhledne rohy = orizle.

#### Akcni tlacitka (control 103)

Horizontalni list se tremi pill-shaped tlacitky (height=60px, width=220px). Focus stav ma glow vrstvu (30% bila) + solidni pill (70% bila):

```xml
<itemlayout height="60" width="220">
    <texture colordiffuse="25808080">btn_action_pill.png</texture>
    <textcolor>88FFFFFF</textcolor>
</itemlayout>
<focusedlayout height="60" width="220">
    <!-- glow: 216x56, colordiffuse 30FFFFFF -->
    <!-- solid: 210x50, colordiffuse 70FFFFFF -->
    <textcolor>FFFFFFFF</textcolor>
</focusedlayout>
```

#### Polozky vysledku (panel 50)

Kazda polozka (97px vyska) ma 2 radky:

**1. radek:** Ikona providera + barevny badge + nazev souboru
- Webshare: tyrkysova (`FF00B4D8`), badge 155px
- KRA.sk: ruzova (`FFFF69B4`), badge 90px

**2. radek:** Quality pill + slouceny metadata label
- Quality pill ma podminnou sirku: 50px (4K, SD) / 85px (1080p, 720p, 480p)
- Metadata label (`metadata_line`): `CZ · MKV · 4.2 GB · 0+/0-` s `[COLOR]` tagy

#### Focus efekt na polozkach

Glow vrstva (`500078D7`, cela sirka panelu 984px) + inset pozadi (978x84px, `30FFFFFF`).
Animace: fade 50→100% pri focusu.

Navigace: `onup="103"` z panelu 50, `ondown="50"` z listu 103.

---

## Kdo vola `show_search_dialog()`

### 1. Film z addonu -- `utils.py`

Funkce `play_movie_from_addon()` sestavi metadata z TMDb parametru a zavola:
```python
show_search_dialog(
    search_query="Nazev filmu rok",
    movie_title=title, movie_year=year,
    poster_url=poster, fanart_url=fanart,
    rating=rating, genre=genre, director=director,
    runtime=runtime, plot=plot,
    search_results=[], tmdb_id=tmdb_id
)
```
Po navratu resolvuje `ident` pres Webshare a spusti prehravani.

### 2. Film -- rucni hledani -- `MovieSearch.py`

Funkce `search_and_select_movie_file()` provede hledani, serazeni a preda **predpripravene vysledky**:
```python
show_search_dialog(
    search_query=query,
    movie_title=title, movie_year=year,
    poster_url=poster, ...
    search_results=sorted_files,  # UZ SERAZENE
    manual_search=True  # nebo False
)
```

### 3. Epizoda z addonu -- `SeriesSearch.py`

Funkce `_internal_search_and_select_series_file()` hleda pres oba providery, seradi a preda:
```python
show_search_dialog(
    search_query=query,
    movie_title="Serial -- Epizoda",
    search_results=files,
    is_episode=True,
    series_name=series_name,
    season_number=season, episode_number=episode,
    episode_title=ep_name,
    episode_thumb=episode_thumb,
    tmdb_id=tmdb_id
)
```

### 4. Epizoda z knihovny -- `LibraryManager.py`

Funkce `resolve_episode_from_library()` ziska metadata z Kodi JSON-RPC API a preda:
```python
show_search_dialog(
    search_query="Serial S01E05",
    movie_title="Serial -- Nazev epizody",
    search_results=[],  # PRAZDNE -- dialog vyhledava sam
    is_episode=True,
    from_library=True,  # DULEZITE: ovlivnuje autoplay a info dialog
    autoplay_request=autoplay_request,
    ...metadata z knihovny...
)
```

> [!IMPORTANT]
> Rozdil oproti addonu: `from_library=True` znamena ze autoplay toggle je aktivni a info dialog se neplanuje z `_handle_autoplay_and_close()` (resi to `LibraryManager`).

### 5. Pres router (RunPlugin) -- `router.py`

Akce `show_search_dialog` v routeru prevede URL parametry na kwargs a zavola funkci. Pouziva se pro spusteni z externich zdroju (napr. AI doporuceni).

---

## Datovy flow

```
Volajici modul
    |
    v
show_search_dialog()
    |
    +-- autoplay_request=True? --> _perform_search() --> vrat 1. soubor (bez UI)
    |
    +-- normalni flow:
        |
        v
    SearchDialog.__init__() --> uloz kwargs
        |
        v
    dialog.doModal() --> onInit()
        |
        +-- setProperty() pro vsechna metadata
        +-- _perform_search() (pokud search_results == [])
        |       +-- _perform_search_webshare()
        |       +-- _perform_search_kraska()
        +-- sort_*_by_priority()
        +-- _populate_results_list() --> control 50
        +-- _populate_action_buttons() --> control 103
        +-- setFocusId(103)
        |
        v
    [Uzivatel interaguje]
        |
        +-- klik na control 50 --> _play_selected_item()
        +-- klik na control 103 --> onClick() --> action_id routing
        +-- ESC/Back --> onAction() --> close()
        |
        v
    _handle_autoplay_and_close()
        +-- nastavi AutoplayManager (pro serialy)
        +-- schedule_episode_info_dialog() (pro epizody mimo knihovnu)
        +-- self.close()
        |
        v
    dialog.selected_result --> navrat do volajiciho modulu
```

---

## Zavislosti

| Modul | Pouziti |
|-------|---------|
| `wcs.webshare.WebshareClient` | API volani na Webshare, `revalidate()`, `api_call()` |
| `wcs.streamcinema.SCClient` | API volani na Stream Cinema (pro KRA.sk) |
| `wcs.search.MovieSearch` | `sort_files_by_priority()` |
| `wcs.search.SeriesSearch` | `sort_series_files_by_priority()`, `do_webshare_search_with_retry()` |
| `wcs.utils` | `extract_quality()`, `extract_language()`, `format_size()`, `calculate_rating()`, `is_valid_file_size()`, `schedule_episode_info_dialog()` |
| `wcs.playback.DialogMediaInfoWithGame` | `get_autoplay_manager()` (singleton) |
| `wcs.ui.dialogs.DialogWcsDownload` | Download picker dialog |
| `wcs.games.DialogWcsTextGame` | Textova AI hra |
| `btn_action_pill.png` | Textura pro pill-shaped tlacitka (sdilena s AI chat dialogy) |
| `badge_pill.png` | Textura pro quality a provider badge (zakulacene rohy) |
| `poster_mask_rounded.png` | Diffuse maska pro zakulacene rohy posteru (100x150px) |
| `item_bg_rounded.png` | Zakulacene pozadi polozek seznamu |
| `icon_provider_webshare.png` | Ikona providera Webshare |
| `icon_provider_kraska.png` | Ikona providera KRA.sk |
