Full Code of PrajwalVandana/maestro-cli for AI

master b0f790aabe2b cached
27 files
406.9 KB
140.9k tokens
206 symbols
1 requests
Download .txt
Showing preview only (421K chars total). Download the full file or copy to clipboard to get everything.
Repository: PrajwalVandana/maestro-cli
Branch: master
Commit: b0f790aabe2b
Files: 27
Total size: 406.9 KB

Directory structure:
gitextract_c46jkt4m/

├── .github/
│   └── workflows/
│       └── cross-build.yml
├── .gitignore
├── LICENSE
├── data/
│   └── icons/
│       └── maestro_icon.icns
├── dist/
│   └── maestro_music-2.0.6-py3-none-any.whl
├── install-scripts/
│   ├── mac
│   ├── ubuntu
│   └── windows.nsi
├── maestro/
│   ├── __init__.py
│   ├── __main__.py
│   ├── __version__.py
│   ├── config.py
│   ├── helpers.py
│   ├── icon.py
│   ├── jit_funcs.py
│   ├── mac_presence.py
│   └── main.py
├── readme.md
├── requirements.txt
├── scripts/
│   ├── add_album_art.py
│   ├── custom_album_art.py
│   └── rename_tracktitles.py
├── setup.py
├── specs/
│   ├── maestro-mac.spec
│   ├── maestro-ubuntu.spec
│   └── maestro-windows.spec
└── uninstall-scripts/
    └── unix

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/cross-build.yml
================================================
name: Cross-Platform Build

on:
  push:
    branches:
      - dev

jobs:
  pyinstaller-build-windows:
    runs-on: windows-latest
    steps:
      - name: Build executable
        uses: PrajwalVandana/pyinstaller@c7c491de8409921df7045f681695d4d5ab71a4e0
        id: pyinstaller
        with:
          python_ver: '3.12'
          spec: specs/maestro-windows.spec
          requirements: requirements.txt
      - name: Install NSIS
        uses: repolevedavaj/install-nsis@v1.0.2
        with:
          nsis-version: '3.10'
      - name: Run NSIS and zip dist
        run: |
          makensis install-scripts/windows.nsi
          Compress-Archive -Path "${{ steps.pyinstaller.outputs.executable_path }}/maestro/*" -Destination "${{ steps.pyinstaller.outputs.executable_path }}/maestro-windows.zip"
      - name: Upload to release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            install-scripts/maestro-installer.exe
            ${{ steps.pyinstaller.outputs.executable_path }}/maestro-windows.zip
          token: ${{ secrets.GITHUB_TOKEN }}
          draft: true
          prerelease: true
  pyinstaller-build-mac:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [macos-12, macos-latest]
    steps:
      - name: Build executable
        uses: PrajwalVandana/pyinstaller@c7c491de8409921df7045f681695d4d5ab71a4e0
        id: pyinstaller
        with:
          python_ver: '3.12'
          spec: specs/maestro-mac.spec
          requirements: requirements.txt
      - name: Read version from __version__.py
        id: version
        # read VERSION = "a.b.c" from __version__.py
        run: |
          VERSION=$(sed -n 's/VERSION = "\([^"]*\)"/\1/p' maestro/__version__.py)
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
      - name: Add install script
        run: |
          mkdir -p "${{ steps.pyinstaller.outputs.executable_path }}/Scripts"
          mv install-scripts/mac "${{ steps.pyinstaller.outputs.executable_path }}/Scripts/postinstall"
      - name: Create .pkg
        id: pkgbuild
        # main branch
        # uses: PrajwalVandana/generate-mac-installer-github-action@fd5c2a03cfc2be65e32095573392ed03423a4208
        # dev branch
        uses: PrajwalVandana/generate-mac-installer-github-action@9d0c29930827283cce48688cb2154b47c92a4042
        with:
          root-directory:  "${{ steps.pyinstaller.outputs.executable_path }}/maestro"
          scripts-directory:  "${{ steps.pyinstaller.outputs.executable_path }}/Scripts"
          identifier: com.maestro.maestro-cli
          version: ${{ steps.version.outputs.version }}
          install-location: /usr/local/bin/maestro-bundle/
      - name: Rename .pkg, compress dist
        run: |
          mv "${{ steps.pkgbuild.outputs.output-path }}" "${{ steps.pyinstaller.outputs.executable_path }}/${{ matrix.os == 'macos-latest' && 'maestro-apple-silicon' || 'maestro-mac-intel' }}.pkg"
          tar -czf "${{ steps.pyinstaller.outputs.executable_path }}/${{ matrix.os == 'macos-latest' && 'maestro-apple-silicon' || 'maestro-mac-intel' }}.tar.gz" "${{ steps.pyinstaller.outputs.executable_path }}/maestro"
      - name: Upload to release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            ${{ steps.pyinstaller.outputs.executable_path }}/${{ matrix.os == 'macos-latest' && 'maestro-apple-silicon' || 'maestro-mac-intel' }}.pkg
            ${{ steps.pyinstaller.outputs.executable_path }}/${{ matrix.os == 'macos-latest' && 'maestro-apple-silicon' || 'maestro-mac-intel' }}.tar.gz
          token: ${{ secrets.GITHUB_TOKEN }}
          draft: true
          prerelease: true
  pyinstaller-build-linux:
    runs-on: ubuntu-20.04
    steps:
      - name: Build executable
        uses: PrajwalVandana/pyinstaller@c7c491de8409921df7045f681695d4d5ab71a4e0
        id: pyinstaller
        with:
          python_ver: '3.12'
          spec: specs/maestro-ubuntu.spec
          requirements: requirements.txt
      - name: Add install script and compress
        run: |
          mv install-scripts/ubuntu "${{ steps.pyinstaller.outputs.executable_path }}/maestro/install-maestro"
          tar -czf "${{ steps.pyinstaller.outputs.executable_path }}/maestro-ubuntu.tar.gz" "${{ steps.pyinstaller.outputs.executable_path }}/maestro"
      - name: Upload to release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            ${{ steps.pyinstaller.outputs.executable_path }}/maestro-ubuntu.tar.gz
          token: ${{ secrets.GITHUB_TOKEN }}
          draft: true
          prerelease: true


================================================
FILE: .gitignore
================================================
.vscode
build
*.egg-info
**/*.log
*.egg
__pycache__
*.code-workspace
**/*.env
.pylintrc

todo.md
test_songs/
song_info.txt
*_test.py
*_ref.*
sound_vis_cache/
gui_helper.py
**/*.spotdl
profile.*

================================================
FILE: LICENSE
================================================
Copyright © 2022 Prajwal Vandana

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.



================================================
FILE: install-scripts/mac
================================================
#!/bin/sh

MAESTRO_BUNDLE_LOC=/usr/local/bin/maestro-bundle
MAESTRO_SYMLINK_LOC=/usr/local/bin/maestro

# Make maestro executable
chmod +x $MAESTRO_BUNDLE_LOC/maestro

# Recursively find and disable quarantine
xattr -d -r com.apple.quarantine $MAESTRO_BUNDLE_LOC

# Remove existing symlink
rm -rf $MAESTRO_SYMLINK_LOC

# Create a symlink to the maestro executable
ln -s $MAESTRO_BUNDLE_LOC/maestro $MAESTRO_SYMLINK_LOC


================================================
FILE: install-scripts/ubuntu
================================================
#!/bin/sh

PREINSTALL_LOC=.
MAESTRO_BUNDLE_LOC=/usr/local/bin/maestro-bundle
MAESTRO_SYMLINK_LOC=/usr/local/bin/maestro

# Make maestro executable
echo Changing permissions for maestro executable
chmod +x $PREINSTALL_LOC/maestro || echo Failed to change permissions for maestro executable, try running with sudo

# Remove existing bundle files
echo Removing any existing maestro bundle files at $MAESTRO_BUNDLE_LOC
rm -rf $MAESTRO_BUNDLE_LOC || echo Failed to remove any existing maestro bundle files at $MAESTRO_BUNDLE_LOC, try running with sudo

# Move files to $MAESTRO_BUNDLE_LOC
echo Creating maestro bundle at $MAESTRO_BUNDLE_LOC
mkdir -p $MAESTRO_BUNDLE_LOC || echo Failed to create maestro bundle directory at $MAESTRO_BUNDLE_LOC, try running with sudo
echo Moving files to $MAESTRO_BUNDLE_LOC
mv $PREINSTALL_LOC/maestro $MAESTRO_BUNDLE_LOC/maestro || echo Failed to move maestro executable to $MAESTRO_BUNDLE_LOC, try running with sudo
mv $PREINSTALL_LOC/_internal $MAESTRO_BUNDLE_LOC/_internal || echo Failed to move dependency files to $MAESTRO_BUNDLE_LOC, try running with sudo

# Remove existing symlink
echo Removing any existing symlink at $MAESTRO_SYMLINK_LOC
rm -rf $MAESTRO_SYMLINK_LOC || echo Failed to remove any existing maestro symlink at $MAESTRO_SYMLINK_LOC, try running with sudo

# Create a symlink to the maestro executable
echo Creating symlink to maestro executable
ln -s $MAESTRO_BUNDLE_LOC/maestro $MAESTRO_SYMLINK_LOC || echo Failed to create symlink to maestro executable, try running with sudo


================================================
FILE: install-scripts/windows.nsi
================================================
!define PRODUCT_NAME "maestro-cli"

OutFile "maestro-installer.exe"

Section
    ; Set installation directory
    SetOutPath $PROGRAMFILES64\maestro-bundle

    ; Add files to installation directory
    File ..\dist\maestro\maestro.exe
    File /r ..\dist\maestro\_internal

    ; Add $PROGRAMFILES64\maestro-bundle to PATH
    EnVar::SetHKCU
    EnVar::AddValue "Path" "$PROGRAMFILES64\maestro-bundle"

    ; Add uninstaller registry key
    WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_NAME}"
    WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UninstallString" "$PROGRAMFILES64\maestro-uninstall.exe"
SectionEnd

Section -Post
    WriteUninstaller "$PROGRAMFILES64\maestro-uninstall.exe"
SectionEnd

Section "Uninstall"
    ; Remove $PROGRAMFILES64\maestro-bundle
    RMDir /r "$PROGRAMFILES64\maestro-bundle"

    ; Remove $PROGRAMFILES64\maestro-bundle from PATH
    EnVar::SetHKCU
    EnVar::DeleteValue "Path" "$PROGRAMFILES64\maestro-bundle"

    ; Remove uninstaller registry key
    DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"

    ; Remove uninstaller
    Delete "$PROGRAMFILES64\maestro-uninstall.exe"
SectionEnd

================================================
FILE: maestro/__init__.py
================================================


================================================
FILE: maestro/__main__.py
================================================
import sys

from multiprocessing import freeze_support

from maestro.main import cli


if __name__ == "__main__":
    # check if frozen
    if getattr(sys, "frozen", False):
        freeze_support()

    # click passes ctx, no param needed
    cli()  # pylint: disable=no-value-for-parameter


================================================
FILE: maestro/__version__.py
================================================
VERSION = "2.0.6"

================================================
FILE: maestro/config.py
================================================
import os

from datetime import date, datetime
from urllib.parse import urljoin


# region constants

DISCORD_ID = 1039038199881810040

PROMPT_MODES_LIST = ["insert", "append", "tag", "find"]
PROMPT_MODES = {mode: i for i, mode in enumerate(PROMPT_MODES_LIST)}
LOOP_MODES = {
    "none": 0,
    "one": 1,
    "inf": 2,
}

EXTS = (".mp3", ".wav", ".flac", ".ogg")
METADATA_KEYS = (
    "album",
    "albumartist",
    "artist",
    "artwork",
    "comment",
    "compilation",
    "composer",
    "discnumber",
    "genre",
    "lyrics",
    "totaldiscs",
    "totaltracks",
    "tracknumber",
    "tracktitle",
    "year",
    "isrc",
    "#bitrate",
    "#codec",
    "#length",
    "#channels",
    "#bitspersample",
    "#samplerate",
)
INDIC_SCRIPTS = (
    "bengali",
    "assamese",
    "modi",
    "malayalam",
    "devanagari",
    "sinhala",
    "tibetan",
    "gurmukhi",
    "tamil",
    "balinese",
    "thai",
    "burmese",
    "telugu",
    "kannada",
    "gujarati",
    "urdu",
    "lao",
    "javanese",
    "manipuri",
    "oriya",
    "khmer",
)

CUR_YEAR = date.today().year


# region paths
MAESTRO_DIR = os.path.join(os.path.expanduser("~"), ".maestro-files/")

SETTINGS_FILE = os.path.join(MAESTRO_DIR, "settings.json")
LOGFILE = os.path.join(MAESTRO_DIR, "maestro.log")
OLD_LOG_DIR = os.path.join(MAESTRO_DIR, "old-logs/")
DEFAULT_SETTINGS = {
    "song_directory": os.path.join(MAESTRO_DIR, "songs/"),
    "last_version_sync": 0,
}

OLD_SONGS_INFO_PATH = os.path.join(MAESTRO_DIR, "songs.txt")
SONGS_INFO_PATH = os.path.join(MAESTRO_DIR, "songs.json")
OLD_STATS_DIR = os.path.join(MAESTRO_DIR, "stats/")
OVERRIDE_LYRICS_DIR = os.path.join(MAESTRO_DIR, "override-lyrics/")
TRANSLATED_LYRICS_DIR = os.path.join(MAESTRO_DIR, "translated-lyrics/")
# endregion

# region player
HORIZONTAL_BLOCKS = {
    1: "▏",
    2: "▎",
    3: "▍",
    4: "▌",
    5: "▋",
    6: "▊",
    7: "▉",
    8: "█",
}
SCRUB_TIME = 5  # in seconds
VOLUME_STEP = 1
MIN_PROGRESS_BAR_WIDTH = 20
MIN_VOLUME_BAR_WIDTH, MAX_VOLUME_BAR_WIDTH = 10, 40
LYRIC_PADDING = 3

_FARTHEST_RIGHT_CONTROL_DESC = 5
INDENT_CONTROL_DESC = 0
PLAY_CONTROLS = [
    ("SPACE", "pause/play"),
    ("b", "go [b]ack to previous song"),
    ("r", "[r]eplay song"),
    ("n", "skip to [n]ext song"),
    (
        "l",
        "[l]oop the current song once ('l' in status bar). press again to loop infinitely ('L' in status bar). press once again to turn off looping",
    ),
    ("c", "toggle [c]lip mode"),
    ("v", "toggle [v]isualization"),
    ("LEFT", "rewind 5s"),
    ("RIGHT", "fast forward 5s"),
    ("[/-", "decrease volume"),
    ("]/=", "increase volume (] has issues on Windows)"),
    ("m", "[m]ute/unmute"),
    (
        "e",
        "[e]nd the song player after the current song finishes (indicator in status bar, 'e' to cancel)",
    ),
    ("q", "[q]uit the song player immediately"),
    (
        "UP/DOWN",
        "to scroll through the queue/lyrics (mouse scrolling should also work)",
    ),
    (
        "SHIFT+UP/DOWN",
        "move the selected song up/down in the queue",
    ),
    ("ENTER", "play the selected song/seek to selected lyric"),
    ("p", "sna[p] back to the currently playing song/lyric"),
    (
        "g",
        "go to the next pa[g]e/loop of the queue (ignored if not repeating queue)",
    ),
    (
        "BACKSPACE/DELETE",
        "delete the selected (not necessarily currently playing!) song from the queue",
    ),
    ("d", "toggle [D]iscord rich presence"),
    (
        "a",
        "[a]dd a song to the end of the queue (opens a prompt to enter the song name or ID: ENTER to confirm, ESC to cancel)",
    ),
    (
        "i",
        "[i]nsert a song in the queue after the selected song (opens a prompt like 'a')",
    ),
    (
        ",",
        "add (comma-separated) tag(s) to all songs in the queue (opens a prompt like 'a')",
    ),
    (
        "s",
        "toggle [s]tream (streams to maestro-music.vercel.app/listen-along/[USERNAME]), requires login",
    ),
    ("y", "toggle l[y]rics"),
    (
        "t",
        "toggle [t]ranslated lyrics (if available, ignored if lyrics mode is off)",
    ),
    ("{/_", "focus queue"),
    ("}/+", "focus lyrics (} has issues on Windows)"),
    (
        "o",
        "rel[o]ad song data (useful if you've changed e.g lyrics, tags, metadata, etc. while playing)",
    ),
    ("h", "toggle this [h]elp message"),
    ("f", "[f]ind a song in the queue (opens a prompt like 'a')"),
]
for key, desc in PLAY_CONTROLS:
    if INDENT_CONTROL_DESC < len(key) <= _FARTHEST_RIGHT_CONTROL_DESC:
        INDENT_CONTROL_DESC = len(key)
# endregion

# region visualizer
FPS = 60

STEP_SIZE = 512  # librosa default
VIS_SAMPLE_RATE = STEP_SIZE * FPS

VERTICAL_BLOCKS = {
    0: " ",
    1: "▁",
    2: "▂",
    3: "▃",
    4: "▄",
    5: "▅",
    6: "▆",
    7: "▇",
    8: "█",
}
VISUALIZER_HEIGHT = 8  # should divide 80
WAVEFORM_HEIGHT = 6  # should also divide 80

VIS_FLATTEN_FACTOR = 3  # higher = more flattening; 1 = no flattening
WAVEFORM_FLATTEN_FACTOR = 20
# endregion

# region stream
STREAM_SAMPLE_RATE = 44100
STREAM_CHUNK_SIZE = 256

ICECAST_SERVER = (
    "maestro-icecast.eastus2.cloudapp.azure.com"  # Azure-hosted Icecast server
)

MAESTRO_SITE = "https://maestro-music.vercel.app"
# MAESTRO_SITE = "http://localhost:3000"  # DEBUG

IMAGE_URL = f"{MAESTRO_SITE}/api/get_artwork/"
# endregion

# region auth
AUTH_SERVER = f"{MAESTRO_SITE}/api/"
# AUTH_SERVER = "http://localhost:5001/api/"  # DEBUG
USER_EXISTS_URL = urljoin(AUTH_SERVER, "user_exists")
SIGNUP_URL = urljoin(AUTH_SERVER, "signup")
LOGIN_URL = urljoin(AUTH_SERVER, "login")
UPDATE_METADATA_URL = urljoin(AUTH_SERVER, "update_metadata")
UPDATE_ARTWORK_URL = urljoin(AUTH_SERVER, "update_artwork")
UPDATE_TIMESTAMP_URL = urljoin(AUTH_SERVER, "update_timestamp")
# endregion

# endregion


settings = {}


def print_to_logfile(*args, **kwargs):
    if "file" in kwargs:
        raise ValueError("file kwargs not allowed for 'print_to_logfile'")
    print(
        datetime.now().strftime("[%Y-%m-%d %H:%M:%S]"),
        *args,
        **kwargs,
        file=open(LOGFILE, "a", encoding="utf-8"),
    )


================================================
FILE: maestro/helpers.py
================================================
# region imports

import atexit
import curses
import logging
import os
import subprocess
import threading

logging.disable(logging.CRITICAL)

import click
import msgspec

from getpass import getpass
from random import randint
from time import sleep, time
from typing import Iterable
from urllib.parse import quote, quote_plus

from maestro import config
from maestro.config import print_to_logfile

# endregion


class Song:
    def __init__(self, song_id: int):
        if song_id < 1:
            raise ValueError("Song ID must be greater than 0.")
        self._song_id = song_id

        self._metadata = None
        self._metadata_changed = False

        self._parsed_lyrics = False
        self._parsed_override_lyrics = False
        self._parsed_translated_lyrics = False

        atexit.register(self._save)

    def reset(self):
        self._metadata = None
        self._metadata_changed = False

        self._parsed_lyrics = False
        self._parsed_override_lyrics = False
        self._parsed_translated_lyrics = False

    def __eq__(self, value):
        if not isinstance(value, type(self)):
            return False
        return value.song_id == self.song_id

    def __hash__(self):
        return hash(self.song_id)

    def __repr__(self):
        return f"Song(ID={self.song_id})"

    @property
    def override_lyrics_path(self):
        return os.path.join(
            config.OVERRIDE_LYRICS_DIR, f"{self.song_title}.lrc"
        )

    @property
    def translated_lyrics_path(self):
        return os.path.join(
            config.TRANSLATED_LYRICS_DIR, f"{self.song_title}.lrc"
        )

    @property
    def song_id(self):
        return self._song_id

    @property
    def song_file(self):
        """e.g. song.mp3"""
        return SONG_DATA[self._song_id]["filename"]

    @property
    def song_path(self):
        """e.g. /path/to/song.mp3"""
        return os.path.join(config.settings["song_directory"], self.song_file)

    @property
    def song_title(self):
        """e.g. 'song'"""
        return os.path.splitext(self.song_file)[0]

    @song_title.setter
    def song_title(self, v):
        old_path = self.song_path
        old_override_lyrics_path = self.override_lyrics_path
        old_translated_lyrics_path = self.translated_lyrics_path

        SONG_DATA[self._song_id]["filename"] = (
            v + os.path.splitext(self.song_file)[1]
        )

        if os.path.exists(old_path):
            os.rename(old_path, self.song_path)
        if os.path.exists(old_override_lyrics_path):
            os.rename(old_override_lyrics_path, self.override_lyrics_path)
        if os.path.exists(old_translated_lyrics_path):
            os.rename(old_translated_lyrics_path, self.translated_lyrics_path)

        self._load_metadata()
        self._metadata["tracktitle"] = v
        self._metadata_changed = True

    @property
    def tags(self) -> set[str]:
        return SONG_DATA[self._song_id]["tags"]

    @tags.setter
    def tags(self, v: set[str]):
        SONG_DATA[self._song_id]["tags"] = v

    @property
    def clips(self) -> dict[str, list[int, int]]:
        return SONG_DATA[self._song_id]["clips"]

    @property
    def set_clip(self) -> str:
        return SONG_DATA[self._song_id]["set-clip"]

    @set_clip.setter
    def set_clip(self, v: str):
        SONG_DATA[self._song_id]["set-clip"] = v

    @property
    def listen_times(self) -> dict[int | str, float]:
        return SONG_DATA[self._song_id]["stats"]

    def _load_metadata(self):
        import music_tag

        self._metadata = music_tag.load_file(self.song_path)

    @property
    def artist(self):
        return self.get_metadata("artist") or "No Artist"

    @artist.setter
    def artist(self, v):
        self.set_metadata("artist", v)

    @property
    def album(self):
        return self.get_metadata("album") or "No Album"

    @album.setter
    def album(self, v):
        self.set_metadata("album", v)

    @property
    def album_artist(self):
        return self.get_metadata("albumartist") or "No Album Artist"

    @album_artist.setter
    def album_artist(self, v):
        self.set_metadata("albumartist", v)

    @property
    def duration(self):
        return self.get_metadata("#length")

    @property
    def artwork(self):
        if self._metadata is None:
            self._load_metadata()
        return (
            self._metadata["artwork"].first
            if "artwork" in self._metadata
            else None
        )

    @property
    def raw_lyrics(self) -> str | None:
        return self.get_metadata("lyrics")

    @raw_lyrics.setter
    def raw_lyrics(self, v):
        if self._metadata is None:
            self._load_metadata()

        if v is None and "lyrics" in self._metadata:
            del self._metadata["lyrics"]
        else:
            self._metadata["lyrics"] = v
        self._parsed_lyrics = False
        self._metadata_changed = True

    @property
    def raw_override_lyrics(self) -> str | None:
        if not os.path.exists(self.override_lyrics_path):
            return None
        with open(self.override_lyrics_path, "r", encoding="utf-8") as f:
            return f.read()

    @raw_override_lyrics.setter
    def raw_override_lyrics(self, v):
        import safer

        if v is None:
            if os.path.exists(self.override_lyrics_path):
                os.remove(self.override_lyrics_path)
        else:
            os.makedirs(config.OVERRIDE_LYRICS_DIR, exist_ok=True)
            with safer.open(
                self.override_lyrics_path, "w", encoding="utf-8"
            ) as f:
                f.write(v)

        self._parsed_override_lyrics = False

    @property
    def raw_translated_lyrics(self) -> str | None:
        if not os.path.exists(self.translated_lyrics_path):
            return None
        with open(self.translated_lyrics_path, "r", encoding="utf-8") as f:
            return f.read()

    @raw_translated_lyrics.setter
    def raw_translated_lyrics(self, v):
        import safer

        if v is None:
            if os.path.exists(self.translated_lyrics_path):
                os.remove(self.translated_lyrics_path)
        else:
            os.makedirs(config.TRANSLATED_LYRICS_DIR, exist_ok=True)
            with safer.open(
                self.translated_lyrics_path, "w", encoding="utf-8"
            ) as f:
                f.write(v)

    def _parse_lyrics(self, raw_lyrics):
        if raw_lyrics is None:
            return None

        raw_lyrics_list = raw_lyrics.splitlines()
        for line in raw_lyrics_list:
            if line and not line.strip().startswith("["):  # not LRC format
                return raw_lyrics_list

        import pylrc

        return pylrc.parse(raw_lyrics) if raw_lyrics else None

    @property
    def parsed_lyrics(self):
        if self._parsed_lyrics is False:
            self._parsed_lyrics = self._parse_lyrics(self.raw_lyrics)
        return self._parsed_lyrics

    @property
    def parsed_override_lyrics(self):
        if self._parsed_override_lyrics is False:
            self._parsed_override_lyrics = self._parse_lyrics(
                self.raw_override_lyrics
            )
        return self._parsed_override_lyrics

    @property
    def parsed_translated_lyrics(self):
        if self._parsed_translated_lyrics is False:
            self._parsed_translated_lyrics = self._parse_lyrics(
                self.raw_translated_lyrics
            )
        return self._parsed_translated_lyrics

    def get_metadata(self, key, resolve=True):
        """
        Get metadata value for `key`.

        If 'resolve' is False, then a MetadataItem is returned instead of the
        resolved value.
        """
        if self._metadata is None:
            self._load_metadata()

        if key not in self._metadata:
            return None
        if resolve:
            return self._metadata[key].first
        return self._metadata[key]

    def set_metadata(self, key, value):
        if self._metadata is None:
            self._load_metadata()

        if key not in config.METADATA_KEYS:
            raise ValueError(f"{key} is not a valid metadata key.")
        if key.startswith("#"):
            raise ValueError(f"{key} is not editable.")

        if key == "tracktitle":
            self.song_title = value  # also change file names
        elif key == "lyrics":
            self.raw_lyrics = value  # unset self._parsed_lyrics
        elif value is None:
            if key in self._metadata:
                del self._metadata[key]
        else:
            self._metadata[key] = value

        self._metadata_changed = True

    def remove_from_data(self):
        del SONG_DATA[self.song_id]

    def _save(self):
        if self._metadata_changed:
            self._metadata.save()


class SongData:
    def __init__(self):
        self.songs = None
        atexit.register(self._save)

    def load(self):
        self.songs = {}
        with open(config.SONGS_INFO_PATH, "r", encoding="utf-8") as f:
            s = f.read()
            if not s:
                return
            d = msgspec.json.decode(s)
            for k, v in d.items():
                self.songs[int(k)] = v

                if "tags" not in v:
                    v["tags"] = set()
                else:
                    v["tags"] = set(v["tags"])

                v["stats_"] = {}
                for year in v["stats"]:
                    if year.isdigit():
                        v["stats_"][int(year)] = v["stats"][year]
                    else:
                        v["stats_"][year] = v["stats"][year]
                v["stats"] = v.pop("stats_")
                if "set-clip" not in v:
                    v["set-clip"] = "default"

    def __getitem__(self, key):
        if self.songs is None:
            self.load()
        return self.songs[key]

    def __setitem__(self, key, value):
        if self.songs is None:
            self.load()
        self.songs[key] = value

    def __delitem__(self, key):
        if self.songs is None:
            self.load()
        del self.songs[key]

    def __iter__(self):
        if self.songs is None:
            self.load()
        return iter(self.songs)

    def items(self):
        if self.songs is None:
            self.load()
        return self.songs.items()

    def values(self):
        if self.songs is None:
            self.load()
        return self.songs.values()

    def _save(self):
        import safer

        if self.songs is not None:
            with safer.open(config.SONGS_INFO_PATH, "wb") as f:
                f.write(msgspec.json.encode(self.songs))

    def add_song(self, filename, tags=None):
        if tags is None:
            tags = set()

        if self.songs is None:
            self.load()
        if not self.songs:
            song_id = 1
        else:
            song_id = max(self) + 1
        self.songs[song_id] = {
            "filename": os.path.split(filename)[1],
            "tags": set(tags),
            "clips": {},
            "stats": {
                config.CUR_YEAR: 0.0,
                "total": 0.0,
            },
            "set-clip": "default",
        }

        song = Song(song_id)
        song.set_metadata("tracktitle", song.song_title)

        return song


SONG_DATA = SongData()


class Songs:
    """
    Wrapper around dict of all `Song` objects.
    """

    def __init__(self):
        self._songs = None
        self._song_data = SONG_DATA

    def load(self):
        self._songs = {Song(k) for k in self._song_data}

    def __contains__(self, value: Song):
        if self._songs is None:
            self.load()
        return value in self._songs

    def __iter__(self) -> Iterable[Song]:
        if self._songs is None:
            self.load()
        return iter(self._songs)

    def __len__(self):
        if self._songs is None:
            self.load()
        return len(self._songs)


SONGS = Songs()


def is_safe_username(url):
    return quote(url, safe="") == url if url else False


def bounded_shuffle(lst, radius=-1):
    """
    Randomly shuffle `lst`, but with the constraint that each element can only
    move at most `radius` positions away from its original position.

    To shuffle with no bounds, set `radius = -1`.
    """
    n = len(lst)
    if radius == -1:
        radius = n
    elif radius == 0:
        return

    index_at = list(range(n))
    for i in range(n - 1, 0, -1):
        j = randint(max(0, index_at[i] - radius), i)
        index_at[j], index_at[i] = index_at[i], index_at[j]
        lst[j], lst[i] = lst[i], lst[j]


def set_timeout(func, timeout, *args, **kwargs):
    def wrapper():
        sleep(timeout)
        func()

    threading.Thread(
        target=wrapper, daemon=True, args=args, kwargs=kwargs
    ).start()


class Scroller:
    def __init__(self, num_lines, win_size):
        self.num_lines = num_lines
        self.win_size = win_size
        self.pos = 0
        self.top = 0

    def scroll_forward(self):
        if self.pos < self.num_lines - 1:
            if (
                self.pos == self.halfway
                and self.top < self.num_lines - self.win_size
            ):
                self.top += 1
            self.pos += 1

    def scroll_backward(self):
        if self.pos > 0:
            if self.pos == self.halfway and self.top > 0:
                self.top -= 1
            self.pos -= 1

    @property
    def halfway(self):
        return self.top + (self.win_size - 1) // 2

    def resize(self, win_size=None):
        if win_size is not None:
            self.win_size = win_size
        self.top = max(0, self.pos - (self.win_size - 1) // 2)
        self.top = max(0, min(self.num_lines - self.win_size, self.top))

    def refresh(self):
        self.resize()


def fit_string_to_width(s, width, length_so_far):
    if length_so_far + len(s) > width:
        remaining_width = width - length_so_far
        if remaining_width >= 3:
            s = s[: (remaining_width - 3)] + "..."
        else:
            s = "." * remaining_width
    length_so_far += len(s)
    return s, length_so_far


def addstr_fit_to_width(stdscr, s, width, length_so_far, *args, **kwargs):
    s, length_so_far = fit_string_to_width(s, width, length_so_far)
    if s:
        if length_so_far <= width:
            stdscr.addstr(s, *args, **kwargs)
        else:
            stdscr.addstr(s[:-1], *args, **kwargs)
            stdscr.insstr(s[-1], *args, **kwargs)
    return length_so_far


class FFmpegProcessHandler:
    def __init__(self, username, password):
        self.process = None
        self.username = username
        self.password = password

    def start(self):
        threading.Thread(target=self._start, daemon=True).start()

    def _start(self):
        from spotdl.utils.ffmpeg import get_ffmpeg_path

        self.process = subprocess.Popen(
            # fmt: off
            [
                str(get_ffmpeg_path()),
                "-re",  # Read input at native frame rate
                "-f", "s16le",  # Raw PCM 16-bit little-endian audio
                "-ar", str(config.STREAM_SAMPLE_RATE),  # Set the audio sample rate
                "-ac", "2",  # Set the number of audio channels to 2 (stereo)
                '-i', 'pipe:',  # Input from stdin
                "-f", "mp3",  # Output format
                # "-report",  # DEBUG
                f"icecast://{self.username}:{self.password}@{config.ICECAST_SERVER}:8000/{self.username}",  # Azure-hosted maestro Icecast URL
            ],
            # fmt: on
            stdin=subprocess.PIPE,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
        )

    def terminate(self):
        if self.process is not None:
            self.process.terminate()
            self.process = None

    def restart(self):
        self.terminate()
        self.start()

    def write(self, chunk):
        if self.process is not None:
            try:
                self.process.stdin.write(chunk)
            except BrokenPipeError as e:  # pylint: disable=unused-variable
                print_to_logfile("FFmpeg processs error:", e)


class PlaybackHandler:
    def __init__(
        self,
        stdscr: "curses._CursesWindow",
        playlist: list[Song],
        clip_mode,
        visualize,
        stream,
        creds,
        want_lyrics,
        want_translated_lyrics,
    ):
        from just_playback import Playback

        self.stdscr = stdscr
        self.scroller = Scroller(
            len(playlist), stdscr.getmaxyx()[0] - 2  # -2 for status bar
        )
        self.playlist = playlist
        self.i = 0
        self._volume = 0
        self.clip_mode = clip_mode
        self.want_discord = False
        self.want_vis = visualize  # want to visualize
        self._want_stream = stream  # want to stream
        self.username, self.password = creds
        self.want_lyrics = want_lyrics
        self.want_translated_lyrics = want_translated_lyrics and want_lyrics

        self.playback = Playback()
        self._paused = False
        self.last_timestamp = 0
        self.looping_current_song = config.LOOP_MODES["none"]
        self.duration = 0
        self.restarting = False
        self.ending = False
        self.prompting: None | tuple = None
        self.clip = (0, 0)

        self.italic = True

        self.can_mac_now_playing = False
        self.mac_now_playing = None
        self.update_now_playing = False

        self.discord_connected = 2
        self.discord_rpc = None
        self.discord_last_update = 0
        self.can_update_discord = True  # pypresence installed
        self.discord_updating = False  # lock

        self._librosa = None
        self.can_visualize = True
        self.can_show_visualization = (
            self.want_vis
            # space to show visualization
            and self.screen_height > config.VISUALIZER_HEIGHT + 5
        )

        self.audio_data = None
        self.audio_data = {}  # dict(song_id: (vis data, stream data))
        self.audio_processing_thread = threading.Thread(
            target=self._audio_processing_loop,
            daemon=True,
        )
        self.audio_processing_thread.start()
        self.compiled = None

        self.ffmpeg_process = FFmpegProcessHandler(self.username, self.password)
        if self.want_stream:
            self.ffmpeg_process.start()
        self.break_stream_loop = False
        self.streaming_thread = threading.Thread(
            target=self._streaming_loop,
            daemon=True,
        )
        self.streaming_thread.start()

        self.lyrics: list | None = None
        self.translated_lyrics: list | None = None
        self.lyrics_scroller = Scroller(0, 0)
        self.lyrics_width = 50
        self.lyric_pos = None

        self.show_help = False
        self.help_pos = 0

        self._focus = 0  # 0: queue, 1: lyrics

    def _load_audio(self, path, sr):
        import numpy as np

        # shape = (# channels, # frames)
        audio_data = self._librosa.load(path, mono=False, sr=sr)[0]

        if len(audio_data.shape) == 1:  # mono -> stereo
            audio_data = np.repeat([audio_data], 2, axis=0)
        elif audio_data.shape[0] == 1:  # mono -> stereo
            audio_data = np.repeat(audio_data, 2, axis=0)
        elif audio_data.shape[0] == 6:  # 5.1 -> stereo
            audio_data = np.delete(audio_data, (1, 3, 4, 5), axis=0)

        return audio_data

    def _audio_processing_loop(self):
        import numpy as np

        try:
            from librosa import load, stft, amplitude_to_db

            self._librosa = type(
                "librosa",
                (),
                {
                    "load": staticmethod(load),
                    "stft": staticmethod(stft),
                    "amplitude_to_db": staticmethod(amplitude_to_db),
                },
            )
        except ImportError:
            self.can_visualize = False
            self.can_show_visualization = False
            print_to_logfile(
                "Librosa not installed. Visualization will be disabled."
            )

        while True:
            try:
                keys_to_delete = []
                for k in self.audio_data:
                    if k not in self.playlist[self.i : self.i + 5]:
                        keys_to_delete.append(k)
                for k in keys_to_delete:
                    del self.audio_data[k]

                for i in range(self.i, min(self.i + 5, len(self.playlist))):
                    processing_song = self.playlist[i]

                    if self.song != processing_song and (
                        self.song not in self.audio_data
                        or (
                            self.want_vis
                            and self.audio_data[self.song][0] is None
                        )
                        or (
                            self.want_stream
                            and self.audio_data[self.song][1] is None
                        )
                    ):
                        break
                    if processing_song in self.audio_data and (
                        (
                            self.audio_data[processing_song][0] is not None
                            or not self.want_vis
                        )
                        and (
                            self.audio_data[processing_song][1] is not None
                            or not self.want_stream
                        )
                        or self._librosa is None
                    ):
                        continue

                    processing_song_path = (
                        os.path.join(  # NOTE: NOT SAME AS self.song_path
                            config.settings["song_directory"],
                            self.playlist[i].song_file,
                        )
                    )

                    if processing_song not in self.audio_data:
                        self.audio_data[processing_song] = [
                            (
                                self._librosa.amplitude_to_db(
                                    np.abs(
                                        self._librosa.stft(
                                            self._load_audio(
                                                processing_song_path,
                                                sr=config.VIS_SAMPLE_RATE,
                                            )
                                        )
                                    ),
                                    ref=np.max,
                                )
                                + 80
                                if self.want_vis and self.can_visualize
                                else None
                            ),
                            (
                                np.int16(
                                    self._load_audio(
                                        processing_song_path,
                                        sr=config.STREAM_SAMPLE_RATE,
                                    )
                                    * (2**15 - 1)
                                    * 0.5  # reduce volume (avoid clipping)
                                )  # convert to 16-bit PCM
                                if self.want_stream
                                else None
                            ),
                        ]
                    else:
                        if (
                            self.audio_data[processing_song][0] is None
                            and self.want_vis
                            and self.can_visualize
                        ):
                            self.audio_data[processing_song][0] = (
                                self._librosa.amplitude_to_db(
                                    np.abs(
                                        self._librosa.stft(
                                            self._load_audio(
                                                processing_song_path,
                                                sr=config.VIS_SAMPLE_RATE,
                                            )
                                        )
                                    ),
                                    ref=np.max,
                                )
                                + 80
                            )
                        if (
                            self.audio_data[processing_song][1] is None
                            and self.want_stream
                        ):
                            self.audio_data[processing_song][1] = np.int16(
                                self._load_audio(
                                    processing_song_path,
                                    sr=config.STREAM_SAMPLE_RATE,
                                )
                                * (2**15 - 1)
                                * 0.5  # reduce volume (avoid clipping)
                            )  # convert to 16-bit PCM
            except:  # hacky fix  # pylint: disable=bare-except
                pass

            sleep(1)

    def _streaming_loop(self):
        while True:
            if (
                self.want_stream
                and self.username is not None
                and self.audio_data is not None
                and self.song in self.audio_data
                and self.audio_data[self.song][1] is not None
                and self.playback is not None
                # is 0 for a while after resuming, and is -1 if playback is
                # inactive or file is not loaded
                and self.playback.curr_pos > 0
            ):
                for fpos in range(
                    int(self.playback.curr_pos * config.STREAM_SAMPLE_RATE),
                    self.audio_data[self.song][1].shape[1],
                    config.STREAM_CHUNK_SIZE,
                ):
                    try:
                        # print_to_logfile(
                        #     self.song_id,
                        #     fpos / config.STREAM_SAMPLE_RATE,
                        #     self.playback.curr_pos,
                        # )  # DEBUG
                        self.ffmpeg_process.write(
                            self.audio_data[self.song][1][
                                :,
                                fpos : fpos + config.STREAM_CHUNK_SIZE,
                            ]
                            .reshape((-1,), order="F")
                            .tobytes()
                            if not self.paused
                            else b"\x00" * 4 * config.STREAM_CHUNK_SIZE
                        )
                    except KeyError as e:
                        print_to_logfile("KeyError in streaming loop:", e)
                        self.update_stream_metadata()

                    if self.break_stream_loop:
                        self.break_stream_loop = False
                        break

            sleep(0.01)

    # region properties

    @property
    def paused(self):
        return self._paused

    @paused.setter
    def paused(self, value):
        self._paused = value
        if self.want_stream:
            self.threaded_update_icecast_metadata()

    @property
    def volume(self):
        return self._volume

    @volume.setter
    def volume(self, v):
        self._volume = v
        if self.playback is not None:
            self.playback.set_volume(v / 100)

    @property
    def want_stream(self):
        return self._want_stream

    @want_stream.setter
    def want_stream(self, value):
        if self._librosa is None:
            value = False
        self._want_stream = value

    @property
    def song(self):
        return self.playlist[self.i]

    @property
    def song_id(self):
        return self.song.song_id

    @property
    def song_file(self):
        return self.song.song_file

    @property
    def song_path(self):
        return self.song.song_path

    @property
    def song_title(self):
        return self.song.song_title

    @property
    def song_artist(self):
        return self.song.artist

    @property
    def song_album(self):
        return self.song.album

    @property
    def album_artist(self):
        return self.song.album_artist

    @property
    def artwork(self):
        return (
            self.song.artwork.raw_thumbnail([1024, 1024])
            if self.song.artwork
            else None
        )

    @property
    def screen_height(self):
        return self.stdscr.getmaxyx()[0]

    @property
    def screen_width(self):
        return self.stdscr.getmaxyx()[1]

    @property
    def can_show_lyrics(self):
        return self.want_lyrics and self.lyrics is not None

    @property
    def can_show_translated_lyrics(self):
        return (
            self.want_translated_lyrics and self.translated_lyrics is not None
        )

    @property
    def focus(self):
        return self._focus if self.can_show_lyrics else 0

    @focus.setter
    def focus(self, value):
        self._focus = value if self.can_show_lyrics else 0

    # endregion

    def seek(self, pos):
        if self.playback is not None:
            pos = max(self.clip[0] if self.clip_mode else 0, pos)
            self.playback.seek(pos)
            self.break_stream_loop = True
            if self.can_mac_now_playing and self.mac_now_playing is not None:
                self.mac_now_playing.pos = round(pos)
                self.update_now_playing = True
            self.last_timestamp = pos

    def scroll_forward(self):
        if self.show_help:
            self.help_pos += 1
        elif self.focus == 1:
            if self.lyric_pos is None:
                self.lyric_pos = self.lyrics_scroller.pos
            self.lyric_pos = min(self.lyric_pos + 1, len(self.lyrics) - 1)
        elif self.focus == 0:
            self.scroller.scroll_forward()

    def scroll_backward(self):
        if self.show_help:
            self.help_pos -= 1
        elif self.focus == 1:
            if self.lyric_pos is None:
                self.lyric_pos = self.lyrics_scroller.pos
            self.lyric_pos = max(self.lyric_pos - 1, 0)
        elif self.focus == 0:
            self.scroller.scroll_backward()

    def snap_back(self):
        if self.focus == 1:
            self.lyric_pos = None
        elif self.focus == 0:
            self.scroller.pos = self.i
            self.scroller.refresh()

    def set_volume(self, v):
        """Set volume w/o changing self.volume."""
        self.playback.set_volume(v / 100)

    def quit(self):
        self.playback.stop()
        if self.ffmpeg_process is not None:
            self.ffmpeg_process.terminate()
        if self.discord_rpc is not None:
            self.discord_rpc.close()

    def prompting_delete_char(self):
        if self.prompting[1] > 0:
            self.prompting = (
                self.prompting[0][: self.prompting[1] - 1]
                + self.prompting[0][self.prompting[1] :],
                self.prompting[1] - 1,
                self.prompting[2],
            )

    def update_screen(self):
        self.output(self.playback.curr_pos)

    def connect_to_discord(self):
        try:
            from pypresence import Client as DiscordRPCClient
        except ImportError:
            print_to_logfile(
                "pypresence not installed. Discord presence will be disabled."
            )
            self.can_update_discord = False
        discord_rpc = DiscordRPCClient(client_id=config.DISCORD_ID)
        discord_rpc.start()
        return discord_rpc

    def update_discord_metadata(self):
        if self.discord_updating:
            return
        if self.can_update_discord and self.want_discord:
            self.discord_updating = True

            t = time()
            if self.discord_last_update + 15 > t:
                sleep(15 - (t - self.discord_last_update))
            song_name, artist_name, album_name = "", "", ""

            # minimum 2 characters (Discord requirement)
            new_song_name = self.song_title.ljust(2)
            new_artist_name = "by " + self.song_artist
            new_album_name = self.song_album.ljust(2)

            if (
                new_song_name != song_name
                or new_artist_name != artist_name
                or new_album_name != album_name
            ):
                song_name = new_song_name
                artist_name = new_artist_name
                album_name = new_album_name

                d = dict(
                    details=song_name,
                    state=artist_name,
                    large_image=(
                        f"{config.IMAGE_URL}/{self.username}?_={time()}"
                        if self.username
                        else "maestro-icon"
                    ),
                    small_image="maestro-icon-small",
                    large_text=album_name,
                    buttons=(
                        [
                            {
                                "label": "Listen Along",
                                "url": f"{config.MAESTRO_SITE}/listen-along/{self.username}",
                            }
                        ]
                        if self.username and self.want_stream
                        else None
                    ),
                )
                d = {k: v for k, v in d.items() if v is not None}

                try:
                    self.discord_rpc.set_activity(**d)
                    self.discord_last_update = time()
                except Exception as e:  # pylint: disable=bare-except
                    print_to_logfile("Discord update error:", e)
                    song_name, artist_name, album_name = "", "", ""
                    self.discord_connected = 2
                    try:
                        self.discord_rpc = self.connect_to_discord()
                        self.discord_connected = 1
                        self.discord_updating = False
                        self.update_discord_metadata()
                    except Exception as err:
                        print_to_logfile("Discord connection error:", err)
                        self.discord_connected = 0
                finally:
                    self.discord_updating = False

    def update_mac_now_playing_metadata(self):
        from maestro.icon import img as default_artwork

        if self.can_mac_now_playing:
            self.mac_now_playing.paused = False
            self.mac_now_playing.pos = 0
            self.mac_now_playing.length = self.duration
            self.mac_now_playing.cover = (
                self.artwork if self.artwork else default_artwork
            )

            multiprocessing_put_word(
                self.mac_now_playing.title_queue,
                self.song_title,
            )
            multiprocessing_put_word(
                self.mac_now_playing.artist_queue,
                self.song_artist,
            )

            self.update_now_playing = True

    def update_icecast_metadata(self):
        import requests

        return requests.post(
            config.UPDATE_METADATA_URL,
            data={
                "mount": self.username,
                "song": quote_plus(self.song_title),
                "artist": quote_plus(self.song_artist),
                "album": quote_plus(self.song_album),
                "albumartist": quote_plus(self.album_artist),
                "paused": int(self.paused),
            },
            auth=(self.username, self.password),
            timeout=5,
        )

    def icecast_metadata_update_loop(self):
        success = False
        last_metadata_update_attempt = 0
        while not success:
            t = time()
            if t - last_metadata_update_attempt > 5:
                try:
                    response = self.update_icecast_metadata()
                    if response.ok:
                        last_metadata_update_attempt = 0
                        success = True
                    else:
                        raise Exception(
                            f"Icecast Server error {response.status_code}: {response.text}"
                        )
                except Exception as e:  # retry in 5 seconds
                    print_to_logfile("Update metadata failed:", e)
                    last_metadata_update_attempt = t
            sleep(0.01)

    def threaded_update_icecast_metadata(self):
        threading.Thread(
            target=self.icecast_metadata_update_loop, daemon=True
        ).start()

    def update_stream_metadata(self):  # artwork + icecast metadata
        import requests

        self.break_stream_loop = True
        if (
            self.discord_connected
            or self.want_stream
            and self.username is not None
        ):
            if not requests.post(
                config.UPDATE_ARTWORK_URL,
                params={"mount": self.username},
                files={"artwork": self.artwork},
                auth=(self.username, self.password),
                timeout=5,
            ).ok:
                print_to_logfile("Failed to update artwork.")

        if self.want_stream:
            self.threaded_update_icecast_metadata()

    def update_metadata(self):
        def f():
            self.update_mac_now_playing_metadata()
            self.update_stream_metadata()
            self.update_discord_metadata()

        threading.Thread(target=f, daemon=True).start()

    def initialize_discord(self):
        self.want_discord = True
        try:
            self.discord_rpc = self.connect_to_discord()
            self.discord_connected = 1
        except Exception as e:  # pylint: disable=broad-except,unused-variable
            self.discord_connected = 0
            print_to_logfile("Discord connection error:", e)

    def threaded_initialize_discord(self):
        threading.Thread(target=self.initialize_discord, daemon=True).start()

    def output(self, pos):
        from maestro.jit_funcs import render

        screen_height = self.screen_height
        if self.want_lyrics:
            screen_width = self.screen_width - self.lyrics_width
        else:
            screen_width = self.screen_width

        self.can_show_visualization = (
            self.want_vis
            and self.can_visualize
            and screen_height > config.VISUALIZER_HEIGHT + 5
        )
        self.scroller.resize(
            screen_height
            - 3  # -3 for status bar
            - 1  # -1 for header
            - (self.prompting != None)  # - add mode
            # - visualizer
            - (config.VISUALIZER_HEIGHT if self.can_show_visualization else 0)
        )

        if self.clip_mode:
            pos -= self.clip[0]

        self.stdscr.erase()

        length_so_far = 0
        if self.want_discord:
            if self.discord_connected == 2:
                length_so_far = addstr_fit_to_width(
                    self.stdscr,
                    "Connecting to Discord ... ",
                    screen_width,
                    length_so_far,
                    curses.color_pair(12),
                )
            elif self.discord_connected == 1:
                length_so_far = addstr_fit_to_width(
                    self.stdscr,
                    "Discord connected! ",
                    screen_width,
                    length_so_far,
                    curses.color_pair(17),
                )
            else:
                length_so_far = addstr_fit_to_width(
                    self.stdscr,
                    "Failed to connect to Discord. ",
                    screen_width,
                    length_so_far,
                    curses.color_pair(14),
                )

        visualize_message = ""
        visualize_color = 12
        if self.want_vis:
            if self.audio_data is None and self.can_visualize:
                self.audio_processing_thread = threading.Thread(
                    target=self._audio_processing_loop,
                    daemon=True,
                )
                self.audio_data = {}
                self.audio_processing_thread.start()

            if not self.can_visualize:
                visualize_message = "Librosa is required for visualization."
                visualize_color = 14
            elif not self.can_show_visualization:
                visualize_message = "Window too small for visualization."
                visualize_color = 14
            elif self.song not in self.audio_data:
                visualize_message = "Loading visualization..."
                visualize_color = 12
            elif not self.compiled:
                visualize_message = "Compiling renderer..."
                visualize_color = 12

        if self.want_stream:
            prefix = "  " if self.want_discord else ""
            if self.username:
                long_stream_message = (
                    prefix
                    + f"Streaming at {config.MAESTRO_SITE}/listen-along/{self.username}"
                )
                short_stream_message = prefix + f"Streaming as {self.username}!"
                if (
                    length_so_far
                    + len(long_stream_message)
                    + 2
                    + (len(visualize_message) if visualize_message else -2)
                    < screen_width
                ):
                    length_so_far = addstr_fit_to_width(
                        self.stdscr,
                        long_stream_message,
                        screen_width,
                        length_so_far,
                        curses.color_pair(16),
                    )
                else:
                    length_so_far = addstr_fit_to_width(
                        self.stdscr,
                        short_stream_message,
                        screen_width,
                        length_so_far,
                        curses.color_pair(16),
                    )
            else:
                length_so_far = addstr_fit_to_width(
                    self.stdscr,
                    prefix + "Please log in to stream.",
                    screen_width,
                    length_so_far,
                    curses.color_pair(14),
                )

        length_so_far = addstr_fit_to_width(
            self.stdscr,
            " " * (screen_width - length_so_far - len(visualize_message))
            + visualize_message,
            screen_width,
            length_so_far,
            curses.color_pair(visualize_color),
        )
        self.stdscr.move(1, 0)

        song_display_color = 5 if self.looping_current_song else 3
        progress_bar_display_color = (
            17 if (self.clip_mode and self.clip != (0, self.duration)) else 15
        )

        # for aligning song names
        longest_song_id_length = max(
            len(str(song.song_id)) for song in self.playlist
        )

        for j in range(
            self.scroller.top, self.scroller.top + self.scroller.win_size
        ):
            if j <= len(self.playlist) - 1:
                length_so_far = 0

                length_so_far = addstr_fit_to_width(
                    self.stdscr,
                    " "
                    * (
                        longest_song_id_length
                        - len(str(self.playlist[j].song_id))
                    )
                    + f"{self.playlist[j].song_id} ",
                    screen_width,
                    length_so_far,
                    curses.color_pair(2),
                )
                if j == self.i:
                    length_so_far = addstr_fit_to_width(
                        self.stdscr,
                        f"{self.playlist[j].song_title} ",
                        screen_width,
                        length_so_far,
                        curses.color_pair(song_display_color) | curses.A_BOLD,
                    )
                else:
                    length_so_far = addstr_fit_to_width(
                        self.stdscr,
                        f"{self.playlist[j].song_title} ",
                        screen_width,
                        length_so_far,
                        (
                            curses.color_pair(4)
                            if (j == self.scroller.pos)
                            else curses.A_NORMAL
                        ),
                    )
                length_so_far = addstr_fit_to_width(
                    self.stdscr,
                    f"{', '.join(self.playlist[j].tags)}",
                    screen_width,
                    length_so_far,
                    curses.color_pair(2),
                )
            self.stdscr.move((j - self.scroller.top) + 2, 0)

        if self.prompting is not None:
            # pylint: disable=unsubscriptable-object
            if self.prompting[2] == config.PROMPT_MODES["tag"]:
                adding_song_length = addstr_fit_to_width(
                    self.stdscr,
                    "Add tag(s) to songs: " + self.prompting[0],
                    screen_width,
                    0,
                )
            else:
                adding_song_length = addstr_fit_to_width(
                    self.stdscr,
                    config.PROMPT_MODES_LIST[self.prompting[2]].capitalize()
                    + " song: "
                    + self.prompting[0],
                    screen_width,
                    0,
                )
            self.stdscr.move(self.stdscr.getyx()[0] + 1, 0)

        length_so_far = 0
        length_so_far = addstr_fit_to_width(
            self.stdscr,
            ("| " if self.paused else "> ") + f"({self.song_id}) ",
            screen_width,
            length_so_far,
            curses.color_pair(song_display_color + 10),
        )
        length_so_far = addstr_fit_to_width(
            self.stdscr,
            f"{self.song_title} ",
            screen_width,
            length_so_far,
            curses.color_pair(song_display_color + 10) | curses.A_BOLD,
        )
        length_so_far = addstr_fit_to_width(
            self.stdscr,
            "%d/%d  " % (self.i + 1, len(self.playlist)),
            screen_width,
            length_so_far,
            curses.color_pair(12),
        )
        length_so_far = addstr_fit_to_width(
            self.stdscr,
            f"{'c' if self.clip_mode else ' '}",
            screen_width,
            length_so_far,
            curses.color_pair(17) | curses.A_BOLD,
        )

        loop_char = " "
        if self.looping_current_song == config.LOOP_MODES["one"]:
            loop_char = "l"
        elif self.looping_current_song == config.LOOP_MODES["inf"]:
            loop_char = "L"
        length_so_far = addstr_fit_to_width(
            self.stdscr,
            loop_char,
            screen_width,
            length_so_far,
            curses.color_pair(15) | curses.A_BOLD,
        )

        volume_line_length_so_far = addstr_fit_to_width(
            self.stdscr,
            f"{'e' if self.ending else ' '}  ",
            screen_width,
            length_so_far,
            curses.color_pair(14) | curses.A_BOLD,
        )
        addstr_fit_to_width(
            self.stdscr,
            " " * (screen_width - volume_line_length_so_far - 1),
            screen_width,
            volume_line_length_so_far,
            curses.color_pair(16),
        )
        self.stdscr.insstr(  # hacky fix for curses bug
            " ",
            curses.color_pair(16),
        )
        self.stdscr.move(
            screen_height
            - 2
            - (config.VISUALIZER_HEIGHT if self.can_show_visualization else 0),
            0,
        )

        addstr_fit_to_width(
            self.stdscr,
            " " * (screen_width - 1),
            screen_width,
            0,
            curses.color_pair(16),
        )
        self.stdscr.insstr(  # hacky fix for curses bug
            " ",
            curses.color_pair(16),
        )
        self.stdscr.move(
            self.stdscr.getyx()[0],
            0,
        )

        song_data_length_so_far = addstr_fit_to_width(
            self.stdscr,
            self.song_artist + " - ",
            screen_width,
            0,
            curses.color_pair(12),
        )

        if self.italic:
            try:
                song_data_length_so_far = addstr_fit_to_width(
                    self.stdscr,
                    self.song_album,
                    screen_width,
                    song_data_length_so_far,
                    curses.color_pair(12) | curses.A_ITALIC,
                )
            except:  # pylint: disable=bare-except
                self.italic = False
                print_to_logfile("Failed to italicize text in curses.")
        if not self.italic:
            song_data_length_so_far = addstr_fit_to_width(
                self.stdscr,
                self.song_album,
                screen_width,
                song_data_length_so_far,
                curses.color_pair(12),
            )

        addstr_fit_to_width(
            self.stdscr,
            f" ({self.album_artist})",
            screen_width,
            song_data_length_so_far,
            curses.color_pair(12),
        )

        self.stdscr.move(
            screen_height
            - (config.VISUALIZER_HEIGHT if self.can_show_visualization else 0)
            - 1,
            0,
        )

        length_so_far = 0
        secs = int(pos)
        length_so_far = addstr_fit_to_width(
            self.stdscr,
            f"{format_seconds(secs)} / {format_seconds(self.duration)}  ",
            screen_width,
            length_so_far,
            curses.color_pair(progress_bar_display_color),
        )
        if not length_so_far >= screen_width:
            if (
                screen_width - length_so_far
                >= config.MIN_PROGRESS_BAR_WIDTH + 2
            ):
                progress_bar_width = screen_width - length_so_far - 2
                bar = "|"
                progress_block_width = (
                    progress_bar_width * 8 * pos
                ) // self.duration
                for _ in range(progress_bar_width):
                    if progress_block_width > 8:
                        bar += config.HORIZONTAL_BLOCKS[8]
                        progress_block_width -= 8
                    elif progress_block_width > 0:
                        bar += config.HORIZONTAL_BLOCKS[progress_block_width]
                        progress_block_width = 0
                    else:
                        bar += " "

                self.stdscr.addstr(
                    bar, curses.color_pair(progress_bar_display_color)
                )
                self.stdscr.insstr(  # hacky fix for curses bug
                    "|", curses.color_pair(progress_bar_display_color)
                )
            else:
                self.stdscr.addstr(
                    " " * (screen_width - length_so_far - 1),
                    curses.color_pair(16),
                )
                self.stdscr.insstr(  # hacky fix for curses bug
                    " ", curses.color_pair(16)
                )

        # right align volume bar
        if not volume_line_length_so_far >= screen_width:
            self.stdscr.move(
                screen_height
                - 3
                - (
                    config.VISUALIZER_HEIGHT
                    if self.can_show_visualization
                    else 0
                ),
                volume_line_length_so_far,
            )
            if (
                screen_width - volume_line_length_so_far
                >= config.MIN_VOLUME_BAR_WIDTH + 10
            ):
                bar = f"{str(int(self.volume)).rjust(3)}/100 |"
                volume_bar_width = min(
                    screen_width - volume_line_length_so_far - (len(bar) + 1),
                    config.MAX_VOLUME_BAR_WIDTH,
                )
                block_width = int(volume_bar_width * 8 * self.volume / 100)
                for _ in range(volume_bar_width):
                    if block_width > 8:
                        bar += config.HORIZONTAL_BLOCKS[8]
                        block_width -= 8
                    elif block_width > 0:
                        bar += config.HORIZONTAL_BLOCKS[block_width]
                        block_width = 0
                    else:
                        bar += " "
                bar += "|"
                bar = bar.rjust(screen_width - volume_line_length_so_far)

                self.stdscr.addstr(bar, curses.color_pair(16))
            elif screen_width - volume_line_length_so_far >= 7:
                self.stdscr.addstr(
                    f"{str(int(self.volume)).rjust(3)}/100".rjust(
                        screen_width - volume_line_length_so_far
                    ),
                    curses.color_pair(16),
                )

        if self.can_show_visualization:
            if self.clip_mode:
                pos += self.clip[0]

            self.stdscr.move(
                screen_height
                - (
                    config.VISUALIZER_HEIGHT
                    if self.can_show_visualization
                    else 0
                ),
                0,
            )
            if (
                self.song not in self.audio_data
                or self.audio_data[self.song][0] is None
            ):
                self.stdscr.addstr(
                    (
                        (" " * (screen_width - 1) + "\n")
                        * config.VISUALIZER_HEIGHT
                    ).rstrip()
                )
            elif not self.compiled:
                if self.compiled is None:
                    self.compiled = False

                    def thread_func():
                        vdata = self.audio_data[self.song][0]
                        render(
                            screen_width,
                            vdata,
                            min(round(pos * config.FPS), vdata.shape[2] - 1),
                            config.VISUALIZER_HEIGHT,
                        )
                        self.compiled = True

                    threading.Thread(target=thread_func, daemon=True).start()
                self.stdscr.addstr(
                    (
                        (" " * (screen_width - 1) + "\n")
                        * config.VISUALIZER_HEIGHT
                    ).rstrip()
                )
            elif self.compiled:
                vdata = self.audio_data[self.song][0]
                rendered_lines = render(
                    screen_width,
                    vdata,
                    min(
                        round(pos * config.FPS),
                        # fmt: off
                        vdata.shape[2] - 1,
                    ),
                    config.VISUALIZER_HEIGHT,
                )
                for i in range(len(rendered_lines)):
                    self.stdscr.addstr(rendered_lines[i][:-1])
                    self.stdscr.insstr(rendered_lines[i][-1])
                    if i < len(rendered_lines) - 1:
                        self.stdscr.move(self.stdscr.getyx()[0] + 1, 0)

        if self.can_show_lyrics:
            from grapheme import graphemes

            # self.stdscr.redrawwin()  # workaround for foreign characters

            num_lines = min(
                len(self.lyrics),
                (
                    screen_height // 2
                    if self.can_show_translated_lyrics
                    else screen_height - 1
                ),
            )

            cur_lyric_i = None
            is_timed = is_timed_lyrics(self.lyrics)
            if is_timed:
                for i, lyric in enumerate(self.lyrics):
                    if lyric.time > pos:
                        cur_lyric_i = i - 1
                        break
                if cur_lyric_i is None:
                    cur_lyric_i = len(self.lyrics) - 1
            self.lyrics_scroller.pos = (
                self.lyric_pos or cur_lyric_i or self.lyrics_scroller.pos
            )
            self.lyrics_scroller.resize(num_lines)
            if not is_timed:
                self.lyrics_scroller.pos = min(
                    max(
                        self.lyrics_scroller.win_size // 2,
                        self.lyrics_scroller.pos,
                    ),
                    self.lyrics_scroller.num_lines
                    - self.lyrics_scroller.win_size // 2,
                )

            self.stdscr.move(0, screen_width)
            lyric_focus_msg = (
                f"Focus: {'lyrics' if self.focus == 1 else 'queue'}"
            )
            addstr_fit_to_width(
                self.stdscr,
                " " * (self.lyrics_width - len(lyric_focus_msg))
                + lyric_focus_msg,
                self.lyrics_width,
                0,
                curses.color_pair(19),
            )

            for i in range(
                self.lyrics_scroller.top, self.lyrics_scroller.top + num_lines
            ):
                vertical_pos = (i - self.lyrics_scroller.top) * (
                    2 if self.can_show_translated_lyrics else 1
                ) + 1

                style = curses.color_pair(9)
                # pylint: disable=unsubscriptable-object
                lyric_text = get_lyric(self.lyrics[i]).strip()
                if cur_lyric_i is not None:
                    if i == cur_lyric_i:
                        style = curses.color_pair(9) | curses.A_BOLD

                        self.stdscr.move(
                            vertical_pos,
                            screen_width + config.LYRIC_PADDING - 2,
                        )
                        self.stdscr.addstr("> ", style)
                    elif i == self.lyric_pos:
                        style = curses.color_pair(4)

                        self.stdscr.move(
                            vertical_pos,
                            screen_width + config.LYRIC_PADDING - 2,
                        )
                        self.stdscr.addstr("> ", style)
                    elif i < cur_lyric_i:
                        style = curses.color_pair(9) | curses.A_DIM

                try:
                    width = 0
                    for g in graphemes(lyric_text):
                        width += 1
                        # NOTE: why -1? No one knows.
                        if width < self.lyrics_width - config.LYRIC_PADDING:
                            self.stdscr.move(
                                vertical_pos,
                                screen_width + config.LYRIC_PADDING + width - 1,
                            )
                            self.stdscr.addstr(g, style)
                        else:
                            break
                except curses.error:  # bottom right corner errors
                    break

                if (
                    self.can_show_translated_lyrics
                    and i < len(self.translated_lyrics)
                    and self.stdscr.getyx()[0] < screen_height
                ):
                    style = curses.A_DIM
                    if i == cur_lyric_i:
                        style |= curses.A_BOLD
                    elif i == self.lyric_pos and is_timed:
                        style |= curses.color_pair(4)

                    try:
                        width = 0
                        for g in graphemes(
                            get_lyric(self.translated_lyrics[i]).strip()
                        ):
                            width += 1
                            if (
                                width
                                < self.lyrics_width - config.LYRIC_PADDING - 1
                            ):
                                self.stdscr.move(
                                    vertical_pos + 1,
                                    screen_width
                                    + config.LYRIC_PADDING
                                    + 1
                                    + width
                                    - 1,
                                )
                                self.stdscr.addstr(g, style)
                            else:
                                break
                    except curses.error:  # bottom right corner errors
                        break
        elif self.want_lyrics:
            self.stdscr.move(0, screen_width + config.LYRIC_PADDING)
            addstr_fit_to_width(
                self.stdscr,
                "No lyrics found.",
                self.lyrics_width - config.LYRIC_PADDING,
                0,
                curses.color_pair(4),
            )

        if self.show_help:
            l = 15
            r = self.screen_width - l
            t = 5
            b = self.screen_height - t

            if l < r and t < b:
                # draw border
                self.stdscr.addch(t, l, curses.ACS_ULCORNER)
                self.stdscr.addch(t, r, curses.ACS_URCORNER)
                self.stdscr.addch(b, l, curses.ACS_LLCORNER)
                self.stdscr.addch(b, r, curses.ACS_LRCORNER)
                for x in range(l + 1, r):
                    self.stdscr.addch(t, x, curses.ACS_HLINE)
                    self.stdscr.addch(b, x, curses.ACS_HLINE)
                for y in range(t + 1, b):
                    self.stdscr.addch(y, l, curses.ACS_VLINE)
                    self.stdscr.addch(y, r, curses.ACS_VLINE)

                # draw text
                self.stdscr.move(t + 1, l + 1)
                i = max(
                    0,
                    min(self.help_pos, len(config.PLAY_CONTROLS) - (b - t - 1)),
                )
                while self.stdscr.getyx()[0] < b:
                    if i < len(config.PLAY_CONTROLS):
                        key, desc = config.PLAY_CONTROLS[i]
                        length_so_far = addstr_fit_to_width(
                            self.stdscr,
                            key
                            + " " * (config.INDENT_CONTROL_DESC - len(key))
                            + " ",
                            r - l - 1,
                            0,
                            curses.color_pair(18) | curses.A_BOLD,
                        )
                        length_so_far = addstr_fit_to_width(
                            self.stdscr,
                            desc
                            + " " * (r - l - 1 - length_so_far - len(desc)),
                            r - l - 1,
                            length_so_far,
                            curses.color_pair(18),
                        )
                        i += 1
                        self.stdscr.move(self.stdscr.getyx()[0] + 1, l + 1)
                    else:
                        self.stdscr.addstr(" " * (r - l - 1))

        if self.prompting is not None:
            # pylint: disable=unsubscriptable-object
            self.stdscr.move(
                screen_height
                - (
                    config.VISUALIZER_HEIGHT
                    if self.can_show_visualization
                    else 0
                )
                - 4,  # 4 lines for status bar + adding entry
                adding_song_length
                + (self.prompting[1] - len(self.prompting[0])),
            )

        self.stdscr.refresh()


def init_curses(stdscr):
    curses.start_color()
    curses.use_default_colors()

    # region colors

    curses.init_pair(1, curses.COLOR_WHITE, -1)
    curses.init_pair(2, curses.COLOR_BLACK + 8, -1)  # bright black
    curses.init_pair(3, curses.COLOR_BLUE, -1)
    curses.init_pair(4, curses.COLOR_RED, -1)
    curses.init_pair(5, curses.COLOR_YELLOW, -1)
    curses.init_pair(6, curses.COLOR_GREEN, -1)
    curses.init_pair(7, curses.COLOR_MAGENTA, -1)
    curses.init_pair(9, curses.COLOR_CYAN, -1)

    curses.init_pair(11, curses.COLOR_WHITE, curses.COLOR_BLACK)
    curses.init_pair(12, curses.COLOR_BLACK + 8, curses.COLOR_BLACK)
    curses.init_pair(13, curses.COLOR_BLUE, curses.COLOR_BLACK)
    curses.init_pair(14, curses.COLOR_RED, curses.COLOR_BLACK)
    curses.init_pair(15, curses.COLOR_YELLOW, curses.COLOR_BLACK)
    curses.init_pair(16, curses.COLOR_GREEN, curses.COLOR_BLACK)
    curses.init_pair(17, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
    curses.init_pair(18, -1, curses.COLOR_BLACK)
    curses.init_pair(19, curses.COLOR_CYAN, curses.COLOR_BLACK)
    # endregion

    curses.curs_set(False)
    stdscr.nodelay(True)
    try:
        curses.set_escdelay(25)  # 25 ms
    except:  # pylint: disable=bare-except
        pass


class SongParamType(click.ParamType):
    name = "song"

    def convert(self, value, param, ctx) -> Song:
        if value.isdecimal():
            value = int(value)

        if type(value) == int:
            song = Song(value)
            if song in SONGS:
                return song
            self.fail(f"No song found with ID {value}.", param, ctx)

        if not value.isdecimal():
            results = search_song(value)
            if not any(results):
                self.fail(f"No song found matching '{value}'.", param, ctx)

            for result in results:
                if len(result) == 1:
                    return result[0]
                if len(result) > 1:
                    break

            if param is not None:  # called by click
                for song in sum(results, []):
                    print_entry(song, value)
            self.fail("Multiple songs found", param, ctx)

        self.fail("Invalid song argument", param, ctx)


CLICK_SONG = SongParamType()


def yt_embed_artwork(yt_dlp_info, crop):
    import music_tag
    import requests

    yt_dlp_info["thumbnails"].sort(key=lambda d: d["preference"])
    best_thumbnail = yt_dlp_info["thumbnails"][-1]  # default thumbnail

    if "width" not in best_thumbnail:
        # different so that any square thumbnail is chosen
        best_thumbnail["width"] = 0
        best_thumbnail["height"] = -1

    for thumbnail in yt_dlp_info["thumbnails"][:-1]:
        # print(thumbnail)
        if "height" in thumbnail and (
            (
                thumbnail["height"] == thumbnail["width"]
                and (
                    (best_thumbnail["width"] != best_thumbnail["height"])
                    or (
                        thumbnail["height"] >= best_thumbnail["height"]
                        and thumbnail["width"] >= best_thumbnail["width"]
                    )
                )
            )
            or (
                thumbnail["height"] >= best_thumbnail["height"]
                and (thumbnail["width"] >= best_thumbnail["width"])
                and (best_thumbnail["width"] != best_thumbnail["height"])
            )
        ):
            best_thumbnail = thumbnail

    response = requests.get(best_thumbnail["url"], timeout=5)
    image_data = response.content
    if best_thumbnail["width"] != best_thumbnail["height"] and crop:
        from io import BytesIO
        from PIL import Image

        image = Image.open(BytesIO(image_data))
        width, height = image.size
        if width > height:
            image = image.crop(
                (
                    (width - height) // 2,
                    0,
                    (width + height) // 2,
                    height,
                )
            )
        elif height > width:
            image = image.crop(
                (
                    0,
                    (height - width) // 2,
                    width,
                    (height + width) // 2,
                )
            )

        image_data = BytesIO()
        image.save(image_data, format="JPEG")
        image_data = image_data.getvalue()

    m = music_tag.load_file(yt_dlp_info["requested_downloads"][0]["filepath"])
    m["artwork"] = image_data
    m.save()


def clip_editor(stdscr, song: Song, name, start=None, end=None):
    from just_playback import Playback

    playback = Playback()
    playback.load_file(song.song_path)

    init_curses(stdscr)

    if name in song.clips:
        clip_start, clip_end = song.clips[name]
    else:
        clip_start, clip_end = 0, playback.duration

    if start is not None:
        clip_start = start
    if end is not None:
        clip_end = end

    editing_start = True
    change_output = True
    playback.play()
    playback.pause()
    playback.seek(clip_start)
    last_timestamp = playback.curr_pos
    while True:
        if playback.curr_pos >= clip_end:
            playback.pause()

        change_output = change_output or (
            (playback.curr_pos - last_timestamp)
            >= (playback.duration / (8 * (stdscr.getmaxyx()[1] - 2)))
        )

        if change_output:
            clip_editor_output(
                stdscr,
                song,
                playback.curr_pos,
                playback.paused,
                playback.duration,
                clip_start,
                clip_end,
                editing_start,
            )

        c = stdscr.getch()
        next_c = stdscr.getch()
        while next_c != -1:
            c, next_c = next_c, stdscr.getch()

        if c == -1:
            continue

        change_output = False
        if editing_start:
            if c == curses.KEY_LEFT:
                change_output = True
                playback.pause()
                clip_start = max(0, clip_start - 0.1)
                playback.seek(clip_start)
            elif c == curses.KEY_SLEFT:
                change_output = True
                playback.pause()
                clip_start = max(0, clip_start - 1)
                playback.seek(clip_start)
            elif c == curses.KEY_RIGHT:
                change_output = True
                playback.pause()
                clip_start = min(clip_start + 0.1, clip_end)
                playback.seek(clip_start)
            elif c == curses.KEY_SRIGHT:
                change_output = True
                playback.pause()
                clip_start = min(clip_start + 1, clip_end)
                playback.seek(clip_start)
            elif c == curses.KEY_ENTER:
                break
            else:
                c = chr(c)
                if c == " ":  # space
                    change_output = True
                    if playback.playing:
                        playback.pause()
                    else:
                        playback.resume()
                elif c in "tT":
                    change_output = True
                    playback.pause()
                    playback.seek(clip_end - 1)
                    editing_start = False
                elif c in "qQ":
                    return (None, None)
                elif c in "\r\n":
                    break
        else:
            if c == curses.KEY_LEFT:
                change_output = True
                playback.pause()
                clip_end = max(clip_end - 0.1, clip_start)
                playback.seek(clip_end - 1)
            elif c == curses.KEY_SLEFT:
                change_output = True
                playback.pause()
                clip_end = max(clip_end - 1, clip_start)
                playback.seek(clip_end - 1)
            elif c == curses.KEY_RIGHT:
                change_output = True
                playback.pause()
                clip_end = min(clip_end + 0.1, playback.duration)
                playback.seek(clip_end - 1)
            elif c == curses.KEY_SRIGHT:
                change_output = True
                playback.pause()
                clip_end = min(clip_end + 1, playback.duration)
                playback.seek(clip_end - 1)
            elif c == curses.KEY_ENTER:
                break
            else:
                c = chr(c)
                if c == " ":
                    change_output = True
                    if playback.playing:
                        playback.pause()
                    else:
                        playback.resume()
                elif c in "tT":
                    change_output = True
                    playback.pause()
                    playback.seek(clip_start)
                    editing_start = True
                elif c in "qQ":
                    return (None, None)
                elif c in "\r\n":
                    break

    return clip_start, clip_end


def clip_editor_output(
    stdscr,
    song: Song,
    pos,
    paused,
    duration,
    clip_start,
    clip_end,
    editing_start,
):
    stdscr.erase()

    if stdscr.getmaxyx()[0] < 3:
        stdscr.addstr("Window too small.", curses.color_pair(4))
        stdscr.refresh()
        return

    screen_width = stdscr.getmaxyx()[1]

    stdscr.insstr(
        f"{format_seconds(clip_start, show_decimal=True)}"
        + (" <" if editing_start else ""),
        curses.color_pair(7),
    )

    end_str = (
        "> " if not editing_start else ""
    ) + f"{format_seconds(clip_end, show_decimal=True)}"
    stdscr.move(0, screen_width - len(end_str))
    stdscr.insstr(end_str, curses.color_pair(7))

    stdscr.move(1, 0)

    clip_bar_width = screen_width - 2
    if clip_bar_width > 0:
        bar = "|"
        before_clip_block_width = round(
            (clip_bar_width * 8 * clip_start) / duration
        )
        clip_block_width = round(
            clip_bar_width * 8 * (clip_end - clip_start) / duration
        )
        num_chars_added = 0
        stdscr.addstr("|", curses.color_pair(7))
        while before_clip_block_width:
            if before_clip_block_width >= 8:
                stdscr.addstr(" ", curses.color_pair(7))
                before_clip_block_width -= 8
            else:
                stdscr.addstr(
                    config.HORIZONTAL_BLOCKS[before_clip_block_width],
                    curses.color_pair(7) | curses.A_REVERSE,
                )
                clip_block_width -= 8 - before_clip_block_width
                before_clip_block_width = 0
            num_chars_added += 1

        while num_chars_added < clip_bar_width:
            if clip_block_width >= 8:
                stdscr.addstr(config.HORIZONTAL_BLOCKS[8], curses.color_pair(7))
                clip_block_width -= 8
            elif clip_block_width > 0:
                stdscr.addstr(
                    config.HORIZONTAL_BLOCKS[clip_block_width],
                    curses.color_pair(7),
                )
                clip_block_width = 0
            else:
                stdscr.addstr(" ", curses.color_pair(7))
            num_chars_added += 1
        stdscr.insstr("|", curses.color_pair(7))
        stdscr.move(stdscr.getyx()[0] + 1, 0)

    progress_bar_width = screen_width - 2
    if progress_bar_width > 0:
        bar = "|"
        progress_block_width = (progress_bar_width * 8 * pos) // duration
        for _ in range(progress_bar_width):
            if progress_block_width > 8:
                bar += config.HORIZONTAL_BLOCKS[8]
                progress_block_width -= 8
            elif progress_block_width > 0:
                bar += config.HORIZONTAL_BLOCKS[progress_block_width]
                progress_block_width = 0
            else:
                bar += " "

        stdscr.addstr(bar, curses.color_pair(5))
        stdscr.insstr("|", curses.color_pair(5))  # hacky fix for curses bug
        stdscr.move(stdscr.getyx()[0] + 1, 0)

    stdscr.move(stdscr.getyx()[0] + 1, 0)  # 1-line spacing

    # region pause indicator, song ID+title, tags
    length_so_far = 0
    length_so_far = addstr_fit_to_width(
        stdscr,
        ("| " if paused else "> ") + f"({song.song_id}) ",
        screen_width,
        length_so_far,
        curses.color_pair(3),
    )
    length_so_far = addstr_fit_to_width(
        stdscr,
        f"{song.song_title} ",
        screen_width,
        length_so_far,
        curses.color_pair(3) | curses.A_BOLD,
    )
    length_so_far = addstr_fit_to_width(
        stdscr,
        f"{', '.join(song.tags)} ",
        screen_width,
        length_so_far,
        curses.color_pair(2),
    )
    stdscr.move(stdscr.getyx()[0] + 1, 0)
    # endregion

    # region credits
    length_so_far = 0
    length_so_far = addstr_fit_to_width(
        stdscr,
        f"{song.artist} - ",
        screen_width,
        length_so_far,
        curses.color_pair(2),
    )
    try:
        length_so_far = addstr_fit_to_width(
            stdscr,
            song.album,
            screen_width,
            length_so_far,
            curses.color_pair(2) | curses.A_ITALIC,
        )
    except:  # pylint: disable=bare-except
        print_to_logfile("Failed to italicize text in curses.")
        length_so_far = addstr_fit_to_width(
            stdscr,
            song.album,
            screen_width,
            length_so_far,
            curses.color_pair(2),
        )
    addstr_fit_to_width(
        stdscr,
        f" ({song.album_artist})",
        screen_width,
        length_so_far,
        curses.color_pair(2),
    )
    stdscr.move(stdscr.getyx()[0] + 1, 0)
    # endregion

    stdscr.move(stdscr.getyx()[0] + 1, 0)  # 1-line spacing

    # region controls
    controls = [
        ("t", "toggle between editing the start and end of the clip"),
        (
            "LEFT/RIGHT",
            "move whichever clip end you are editing by 0.1 seconds",
        ),
        (
            "SHIFT+LEFT/RIGHT",
            "move whichever clip end you are editing by 1 second",
        ),
        ("SPACE", "play/pause"),
        ("ENTER", "exit the editor and save the clip"),
        ("q", "exit the editor without saving the clip"),
    ]
    for control in controls:
        length_so_far = 0
        length_so_far = addstr_fit_to_width(
            stdscr,
            f"{control[0]}: ",
            screen_width,
            length_so_far,
            curses.A_BOLD,
        )
        length_so_far = addstr_fit_to_width(
            stdscr,
            f"{control[1]}",
            screen_width,
            length_so_far,
        )
        stdscr.move(stdscr.getyx()[0] + 1, 0)
    # endregion

    stdscr.refresh()


def get_username():
    import keyring

    return keyring.get_password("maestro-music", "username")


def get_password():
    import keyring

    return keyring.get_password("maestro-music", "password")


def signup(username=None, password=None, login_=True):
    if username is None:
        username = input("Username: ")

    if not username:
        click.secho("Username cannot be empty.", fg="red")
        return
    if not is_safe_username(username):
        click.secho(
            "Username must be URL-safe (no spaces or special characters).",
            fg="red",
        )
        return

    import requests

    response = requests.get(
        config.USER_EXISTS_URL, params={"user": username}, timeout=5
    )
    if response.status_code == 200:
        click.secho(f"Username {username} already exists.", fg="red")
        return

    if password is None:
        password = getpass("Password (8-1024 characters):")
        if len(password) < 8 or len(password) > 1024:
            click.secho("Passwords should be 8-1024 characters long.", fg="red")
            return
    confirm_password = getpass("Confirm password:")
    if password != confirm_password:
        click.secho("Passwords do not match.", fg="red")
        return

    response = requests.post(
        config.SIGNUP_URL, auth=(username, password), timeout=5
    )
    if response.status_code == 201:
        click.secho(f"Successfully signed up user '{username}'!", fg="green")
        if login_:
            login(username, password)
    else:
        click.secho(
            f"Signup failed with status code {response.status_code}: {response.text}",
            fg="red",
        )


def login(username=None, password=None):
    if username is None:
        username = input("Username: ")

    if not username:
        click.secho("Username cannot be empty.", fg="red")
        return
    if not is_safe_username(username):
        click.secho(
            "Username must be URL-safe (no spaces or special characters).",
            fg="red",
        )
        return

    import keyring

    current_username = keyring.get_password("maestro-music", "username")
    if current_username == username:
        click.secho(f"User '{username}' is already logged in.", fg="yellow")
        return
    if current_username is not None:
        click.secho(
            f"Logging in as user '{username}' will log out current user '{current_username}'.",
            fg="yellow",
        )

    import requests

    if password is None:
        password = getpass("Password:")

    response = requests.post(
        config.LOGIN_URL, auth=(username, password), timeout=5
    )
    if response.status_code == 200:
        click.secho(f"Successfully logged in user '{username}'!", fg="green")
        keyring.set_password("maestro-music", "username", username)
        keyring.set_password("maestro-music", "password", password)
    else:
        click.secho(
            f"Login failed with status code {response.status_code}: {response.text}",
            fg="red",
        )


def format_seconds(secs, show_decimal=False, digital=True, include_hours=None):
    """Format seconds into a string.

    show_decimal: whether to show the decimal part of the seconds
    digital: whether to use digital format ([HH]:MM:SS) or words (e.g. 1h 2m 3s)
    include_hours: whether to include hours in the output (e.g. 71:05 vs 1:11:05)
    """
    h = int(secs // 3600)
    if include_hours is None:
        include_hours = h > 0
    m = int(secs // 60)
    if include_hours:
        m %= 60
    s = int(secs % 60)
    if digital:
        return (
            (f"{h}:" if include_hours else "")
            + f"{m}:{s:02}"
            + (f".{secs%1:0.2f}"[2:] if show_decimal else "")
        )
    return (
        (f"{h}h " if include_hours else "")
        + f"{m}m {s}"
        + (f".{secs%1:0.2f}"[2:] if show_decimal else "")
        + "s"
    )


def search_song(phrase):
    """
    CASE INSENSITIVE. Returns a tuple of three lists:
    0: songs that match the phrase exactly
    1: songs that start with the phrase
    1: songs that contain the phrase but do not start with it
    """
    phrase = phrase.lower()

    results = [], [], []  # is, starts, contains but does not start
    for song in SONGS:
        song_title = song.song_title.lower()
        if song_title == phrase:
            results[0].append(song)
        elif song_title.startswith(phrase):
            results[1].append(song)
        elif phrase in song_title:
            results[2].append(song)

    return results


def print_entry(
    song: Song, highlight: str | None = None, year: int | str | None = None
):
    """
    Pretty prints ([] means optional)
        <song ID> <song name> <total duration> <seconds listened> <times listened> <clip> <tags>
            [<artist> - <album> (<album artist>)]

    highlight: a string to highlight (the first occurrence of) in the song name
    """
    click.secho(f"{song.song_id} ", fg="bright_black", nl=False)
    if highlight is None:
        click.secho(song.song_title + " ", fg="blue", nl=False, bold=True)
    else:
        highlight_loc = song.song_title.lower().find(highlight.lower())
        click.secho(
            song.song_title[:highlight_loc],
            fg="blue",
            nl=False,
            bold=True,
        )
        click.secho(
            song.song_title[highlight_loc : highlight_loc + len(highlight)],
            fg="yellow",
            nl=False,
            bold=True,
        )
        click.secho(
            song.song_title[highlight_loc + len(highlight) :] + " ",
            fg="blue",
            nl=False,
            bold=True,
        )

    click.secho(
        format_seconds(
            song.duration,
            show_decimal=True,
            digital=False,
        )
        + " ",
        nl=False,
    )
    if year is not None:
        click.secho(
            format_seconds(
                song.listen_times.get(year, 0),
                show_decimal=True,
                digital=False,
            )
            + " ",
            fg="yellow",
            nl=False,
        )
        click.secho(
            f"{song.listen_times.get(year, 0) / song.duration:0.2f} ",
            fg="green",
            nl=False,
        )

    if song.set_clip in song.clips:
        start, end = map(
            lambda f: format_seconds(f, show_decimal=True),
            song.clips[song.set_clip],
        )
        click.secho(
            f"[{start}, {end}] ",
            fg="magenta",
            nl=False,
        )

    click.secho(", ".join(song.tags), fg="bright_black")

    click.secho(
        f"{(len(str(song.song_id))+1)*' '}{song.artist if song.artist else 'No Artist'} - ",
        fg="bright_black",
        nl=False,
    )
    click.secho(
        (song.album if song.album else "No Album"),
        italic=True,
        fg="bright_black",
        nl=False,
    )
    click.secho(
        f" ({song.album_artist if song.album_artist else 'No Album Artist'})",
        fg="bright_black",
    )


def multiprocessing_put_word(q, word):
    for c in word:
        q.put(c)
    q.put("\n")


def versiontuple(v):
    return tuple(map(int, v.split(".")))


def pluralize(count, word, include_count=True):
    return f"{count} " * include_count + word + ("s" if count != 1 else "")


def is_timed_lyrics(lyrics):
    from pylrc.classes import Lyrics

    return isinstance(lyrics, Lyrics)


def get_lyric(lyric_obj):
    if isinstance(lyric_obj, str):
        return lyric_obj
    return lyric_obj.text  # pylrc.classes.LyricLine


def set_lyric(lyrics, i, val):
    if isinstance(lyrics[i], str):
        lyrics[i] = val
    else:
        lyrics[i].text = val


def display_lyrics(lyrics, song, prefix: str = ""):
    if prefix:
        prefix += " "

    if lyrics is None:
        click.secho(
            f'No {prefix}lyrics found for "{song.song_title}" (ID: {song.song_id}).',
            fg="red",
        )
        return

    if prefix:
        prefix = prefix.capitalize() + "l"
    else:
        prefix = "L"

    click.echo(f"{prefix.capitalize()}yrics for ", nl=False)
    click.secho(song.song_title, fg="blue", bold=True, nl=False)
    click.echo(f" (ID {song.song_id}):")

    if is_timed_lyrics(lyrics):
        for lyric in lyrics:
            click.echo(
                f"\t[{format_seconds(lyric.time, show_decimal=True)}] {lyric.text}"
            )
    else:
        click.echo("\n".join([f"\t{lyric}" for lyric in lyrics]))


def filter_songs(
    tags: set[str],
    exclude_tags,
    artists,
    albums,
    album_artists,
    match_all,
    combine_artists,
):
    songs = []
    for song in SONGS:
        search_criteria = (
            (
                (
                    any(
                        artist.lower()
                        in song.artist.lower()
                        + (
                            f", {song.album_artist.lower()}"
                            if combine_artists
                            else ""
                        )
                        for artist in artists
                    )
                ),
                artists,
            ),
            (
                (any(album.lower() in song.album.lower() for album in albums)),
                albums,
            ),
            (
                (
                    any(
                        album_artist.lower()
                        in song.album_artist.lower()
                        + (
                            f", {song.artist.lower()}"
                            if combine_artists
                            else ""
                        )
                        for album_artist in album_artists
                    )
                ),
                album_artists,
            ),
        )
        search_criteria = tuple(
            c[0] for c in filter(lambda t: t[1], search_criteria)
        )

        if match_all:
            if not search_criteria:
                search_criteria = (True,)

            search_criteria = all(search_criteria) and (
                not tags or (tags <= song.tags)  # subset
            )
        elif any(search_criteria):
            search_criteria = True
        elif tags:
            search_criteria = tags & song.tags
        else:
            search_criteria = not search_criteria

        if search_criteria and not exclude_tags & song.tags:
            songs.append(song)

    return songs


================================================
FILE: maestro/icon.py
================================================
img = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x04\x00\x00\x00\x04\x00\x08\x06\x00\x00\x00\x7f\x1d+\x83\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00DeXIfMM\x00*\x00\x00\x00\x08\x00\x01\x87i\x00\x04\x00\x00\x00\x01\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x03\xa0\x01\x00\x03\x00\x00\x00\x01\x00\x01\x00\x00\xa0\x02\x00\x04\x00\x00\x00\x01\x00\x00\x04\x00\xa0\x03\x00\x04\x00\x00\x00\x01\x00\x00\x04\x00\x00\x00\x00\x00\xd3\xdd\xea\x1d\x00\x00\x00\x1ciDOT\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00(\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00m.{a\xb5\x85\x00\x00@\x00IDATx\x01\xec\xdd\t\x94]U\x99\xb7q\x036\xd8*\x1f*4\xcb\x01m\xdan\xc4V\xcc\x00$L"S\x18\xc2\x1cl\x8d@\x83\x11\x88\x04ADTh%B\xa2\x08\x01T\x94\xb4H0i\x08H0D\x8cH\x18\x12\xc2\x10P\xa2L\x8d\x88\xb4\xda6\xa2\x80\xa8 4C\x90\x0cd\x7fgWSB\x92\xaaJ\x9d[w8g\xef_\xadU+P\xb9u\xef9\xef~\xf6\xde\xef\xff\xc9\xb9\xe7\xbe\xe2\x15\xbeT\xa0\xa8\xc0\xad\xb7\xde\xfa\xfa\x85\x0b\x17\x0e+\xbe\x0f\xbc\xe5\x96[\x8e-\xfe\xff\xd4\xe2\xcf)\xc5\xf7\xcc\xe2\xbf\xe7\x17\x7f\xdeU\xfc\xf9\xdb\xe2\xcfg\x8a\xefe\xc5\x7f\x07\xdfj\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xce0\x10{\xf2\xd8\x9b\x17\xf5\x8f=z\xec\xd5c\xcf>\xb3\xf8\x9eR\xfcw\xec\xe5\x8f\x8d\xbd}\xec\xf1\x8b\xff\x7f\xbd\xd0\xa3\x02*\x90Y\x05\xae\xbb\xee\xba7\xdct\xd3M;\xc5\xc5\xa0\xf8>\xa7\xf8\x9eS,\x06\xf7\x14\xdfO\x15\xdf\x02\xbd\x1a`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` Q\x06\x8a\xde\xff\x7fc\xef\x1f3\xc0\x8bY\xe0\xd8\x98\rbF\xc8,\x169]\x15H\xab\x02\x97_~\xf9\xda\x85\xe9{O\xf1}H1\xb9\xcf,\xbe\xaf-&\xfb\xc3B>\xc9\x81\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\xf4\xc0\xc0\xc313\xc4\xec\x103D\xcc\x121S\xa4\x95\x92\x9c\x8d\n$R\x81+\xaf\xbcr\xbdb\x12\x8f,\xbeO-\xbe\xe7\x17\xdfO\x17\xdf\xec\xad\x1a`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x8d2\x103E\xcc\x16\xa7\x16\xdf#c\xe6H$>9\r\x15\xa8W\x05\xe2\xe4+\xec\xdc\x01\xc5w|\x8f\xfe\xdd\xc5\xf7r\x81\x9f\xf0\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Z\xc5@\xcc\x1c/f\x8f\x98A\x0e \x04\xea\x95!\x1dm\xcd*\x10/\xc3)&\xda\x89\xc5\x84\xbe\xa9\xf8^\xda\xaa\x89\xedym\x1a\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@?\x18\x88\x99\xe4\xa6\x98QbV\xa9Y\xbcr\xb8*P\xad\n\x14\xef\xb9Y\xa7\x98L{\x17\x93jZ\xf1\xed\xfd\xfb.\xddj\xf4\xd2-\xbf\x87\x1d\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81v0\x103\xcb\xb4\x98ab\x96\xa9V\xbar4*P\xc1\n\x14w\xe1|e1i\xf6,&\xcd\x7f\x14\xdfO\x16\xff\xdd\x8e\x89\xea5\xd4\x19\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18h\x1a\x031\xcb\xc4L\x13\xb3M\xcc8\x15\x8c^\x0eI\x05:S\x81\x89\x13\'\xaeUL\x8c\x91\xc5\x04\xb9\xa0\xf8~\\\xe8\'=0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81T\x18\x88\x19\xe7\xc5\xac\xb3k\xcc>\x9dI]^U\x05:\\\x81\xe2}2o-&\xf5\xa9\xc5\xf7oS\x99\xdc\xce\xc3F\x85\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\xf4\xc1@\xcc>\xa7\xc6,\xd4\xe18\xe6\xe5U\xa0\xf5\x15\x88\x97\xbf\x14\xf6+\xde\xbd\xff\xea\x02\xfc\x17\xfa\x98\x18M\xbb\xfc\xc6kX\x801\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x8a1\xf0B\xccD1\x1by\x8b@\xebs\xa8Whs\x05\xe6\xcf\x9f\xff\xe6\x02\xee/\x15\xdf\xbf\xaf\xd8\xc4#\x1a\xbc\xd7\x0b\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xe8\x18\x031#\xc5\xac\x143S\x9bc\x9a\x97S\x81\xe6V\xe0\xc5\x8f\xee\xbb\xa8\x08\xfdK\x04\x7f\xd6\x15\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\xaf\x0c,)D\xc0E>R\xb0\xb9\x99\xd4\xb3\xb5\xa1\x02\xc5\xa4\x8e7\xf5\xbb\xce\xe4\xeeurw\xcc0\x1a\x13c\x82\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81j3\xf0b\x96\x1a\xd9\x86\xe8\xe6%T\xa0\xe1\n\x0c*l\xd5\x98b1\xb9\xc7\x82R\xed\x05\xc5\xf8\x18\x1f\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\xd4\x82\x81{b\xc6*\x12\xda\xa0\x86S\x9a_T\x81fW\xe0\xe6\x9bo\x1e]X\xaa{-"\xb5XD\\}\xe0=n\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xa8\x11\x031k\xc5\xcc\xd5\xec\x1c\xe7\xf9T\xa0T\x05\n\x10\xf7.\xbe\xef\x12\xfc\x05\x7f\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\xade f\xaf\x98\xc1J\x856\x0fV\x81\x81V\xa0\x80n\xd7br/2\xc1[;\xc1\xd5W}1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x1e\x18X\x143\xd9@s\x9d\xdfW\x81>+P\xbc\xffd\xd3\x02\xbe\x1f\xf4\x00\xa0K\x88jt\t\x91\xf1\xb3\x89`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` \t\x06~\x103Z\x9f!\xce_\xaa@\xd9\n\\s\xcd5\xff\xaf0Lg\x17\x8b\x84\x8f\xf3\x13\xf4\xc9\x1e\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0:\x0c\xc4\x8f\x0f<;f\xb6\xb29\xcf\xe3U`\xa5\nL\x9c8q\xad\x02\xa6qE\xf0\xff#C\x98\x84!\xb4PWg\xa16\x16\xc6\x02\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0\x99\x0c\xfc1f\xb7\x98\xe1V\nu\xfeG\x05\xfaS\x81\xe2.\x93\xc3\x8b\xd0\xff\x9f\x82\xbf\xe0\x8f\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa06\x0c\xfcg\xccr\xfd\xc9|\x1e\xa3\x02\xaf\x987o\xdek\ns\xf4\xd5\xe2{\xb9I^\x9bI\xdeLs\xe8\xb9\x98h\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\xd4\x98\x81\x98\xe5b\xa6\x8b\xd9N\xc4U\x81^+P\xdc@b\xf7\x02\x94\xdf\x08\xfe\x82?\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81z3\x10\xb3]\xccx\xbd\x06@\x7f\x91g\x05\x16,X\xb0A1\xb9g\x98\xe0\xf5\x9e\xe0\xc6\xcf\xf8a\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03=00#f\xbe<\xd3\xae\xb3^\xa9\x02\xc5\xfbC\xf6-\x00q\x93\xbf\x1a_\xe2\xd3\xc3\x04w\xc9\x96\xf1\xc4\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06^\xce\xc0\x1fc\xf6[)\x0c\xfa\x9f|*p\xf9\xe5\x97\xffmqI\xc8y\xc2#C\x88\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` \x0f\x06b\x06\x8cY0\x9f\xe4\xebL_q\xd3M7\r-\x06\xfe~\x93<\x8fIn\x9c\x8d3\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\xd0\xcd@\xcc\x821\x13\x8a\xc6\xe9W`Pq\x13\x88O\x17\x03\xbf\xa4{\xf0\xfdi!\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x90\x1d\x03Kb6,"\xf0\xa0\xf4cp\x86gX\x18\x9e\r\x8bI=\xdf\xc4\xcenb\xbf\xfc}?\xfe\xdb\xfb\xc00\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x9730?f\xc5\x0c#r\xba\xa7\\\x0c\xe8VE\xf0\xff\xad\xf0/\xfcc\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\xc0*\x0c\xfc6f\xc6t\x13qFgV\xbc\xbf\xe3\xc8\xe2\xfb\xf9U\x06\xf8\xe5\xc6\xc7\x7f3\x80\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x062f f\xc6\x98\x1d3\x8a\xcai\x9d\xea5\xd7\\\xb3n1\x80\x17\x08\xfe\xec\x1e\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\xf4\x87\x81\x98!c\x96L+\x1d\'~6\xc5\xe5\x1b\x1b\x17\x03w{\x7f\x06\xd8c,\x04\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\xd0\xcd@\xcc\x921S&\x1e\x9b\xd38\xbdb\xa0\x86\x16\x03\xf7H\xf7\xe0\xf9\xd3D\xc6\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x92\x0c<\x12\xb3e\x1a)9\xd1\xb3(>\xc6a\xaf\xc2\xd6<Sr`\xbd\xd7\xa7\x03\xef\xf5\x997o^\x981cF8\xfb\xec\xb3\xc3\x84\t\x13\xc2q\xc7\x1d\x17\x0e;\xec\xb0p\xc0\x01\x07\x84\x9dw\xde9l\xb1\xc5\x16\xe1\x1d\xefxG\xd8d\x93M\xc2\xc6\x1bo\x1c6\xdah\xa3\xf0\x867\xbc!\xac\xb7\xdez\xe1U\xafzUX{\xed\xb5}\xab\x01\x060\x80\x01\x0c`\x00\x03\x18\xc0@\xc7\x18\x88=i\xecMc\x8f\x1a{\xd5\xd8\xb3\xc6\xde5\xf6\xb0\xb1\x97\x8d=m\xecmc\x8f\x1b{\xdd\xd8\xf3\xc6\xde7\xf6\xc0\xb1\x17\x96Y\xaa/$b\xb6\x8c\x193\xd1\xf8\\\xef\xd3*\x06g|\xf1\xbd\xdcD\xaa\xceD\xba\xfa\xea\xab\xc3\xb9\xe7\x9e\x1bN8\xe1\x84p\xd0A\x07u-\x82\x9bm\xb6YX\x7f\xfd\xf5CA\x9bo5\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x96\x81\xd8\x13\xc7\xde8\x8a\x82\xd8+\xc7\x9e9\xf6\xce\xb1\x87\x96i\xaa\x93ib\xc6\x8cY\xb3\xdei9\xad\xa3\x1fT\x0c\xc8Y&I\xe7&Ia\xc5\xbaL\xe6\xe7?\xff\xf9p\xf0\xc1\x07\x87\xad\xb7\xde:l\xb8\xe1\x86\xd9.\xe6\xe4\x06\xb9\x83\x01\x0c`\x00\x03\x18\xc0\x00\x0600\x10\x06b/\x1d{\xea\xd8[\xc7\x1e;^5\x10{n\x99\xa7s\x99\'f\xcebL\x07\x15\xdf\xbe:U\x81\xa9S\xa7\xfeM1\tf\x99\x08\xed\x9d\x08\xf1\x92\xa5s\xce9\'\x1c~\xf8\xe1a\xf8\xf0\xe1\xe1o\xff\xf6o\x85}\xf6\x1e\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03-d \xf6\xdc\xb1\xf7\x8e=x\xec\xc5\xbd\x8d\xa0\xbd\x19\xe8\xc5\xcc9+f\xd0N\xe5\xdf\xac_\xb7\xb8!\xc3\xab\n\x0b3W\xf8o=\xf8\x0b\x16,\xe8z\xbf\xd2\xbf\xfc\xcb\xbft\xbd\xa7i\xad\xb5\xd6\xb2\xb8\xb7pq/&\xb6\xfa\xaa\x01\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x9f\x0c\xc4\x9e<\xdeo \xf6\xe8\xf1\xde\x02\xb1g\x97\x8dZ\x9f\x8db\x06\x8dY4\xeb0\xde\xee\x93/l\xd7k\x8a\xc2\xdf\x00\xf0\xd6\x01>s\xe6\xcc\xae\x9b\x95\xc4K\x8f\xd6Yg\x9d>\x17\x1f\x81U`\xc7\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@g\x19\x88={\xec\xdd\xe3\r\x07c//+\xb5.+\xc5,\x1a3i\xc1\xbc\xafVW\xe0\xfa\xeb\xaf_\xbf(\xf8m\x80n>\xd0\xe7\x9f\x7f~\x183fLx\xf3\x9b\xdf,\xf03\xce\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xa81\x03\xb1\xa7\x8f\xbd}\xec\xf1e\xa7\xe6g\xa7\x98Ic6mu\xfe\xcd\xfa\xf9\x8bK-6,\n}7\x80\x9b\x07\xf0\xb4i\xd3\xba\xee:\xfa\xc67\xbe\xd1\x02_\xe3\x05\xbeX\x18\x8c\x9f\x1a`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\xf4\xc8@\xec\xf5\xe3\'\r\xc4\xde_\x96j^\x96\x8a\xd94f\xd4\xacCz\xabN>\x16\xb6\x80\xf5>\xc0\x0e\x1c\xd8Y\xb3fu}\x1e\xa9\x7f\xe9\x17\x9a\x89\x03\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xbc\x18\x88\x19\xe0\xb0\xc3\x0e\x0b1\x13\xc8V\x03\xcfV1\xa3\x92\x00\xc5*\xd2\xcc\xaf\x17/\xfb\xf7/\xff\xb76\x0e\xe8\x8d7\xde\x18&N\x9c\x18\xb6\xdcr\xcb\x1e\xad`1^~\xae\x06\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x88\x81\x98\rbF\x88Y\x81\x0ch<k\xc5+\x01\xbc\x1d\xa0I\x06\xe0\xc5\x1b\xfey\xcf\x7f\x83\xe1\xff\x92K.\xe9z\xef\xcf\xfa\xeb\xafo1\xcfh1\'t\x08-\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xfe2\x10\xb3B\xbc_@\xcc\x0eD@c" \xde\x13\xc0\x8d\x01\x0b\xe2\x06\xf2U\\J\x11?\xea\xcf\xdd\xfe\x1b\x08\xff_\xf9\xcaW\xc2V[m%\xf4\x0b\xfd\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\xbf\x19\x88\x19"f\t"\xa0\xbc\x08\x88\xd95f\xd8\x81d\xe0l\x7fw\xea\xd4\xa9\x7fS\x14p.\xf0\xfa\x0f^\x01[8\xf9\xe4\x93\xc3?\xfe\xe3?\xf6{\x82\x17\x80y\xac\x1a`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03+1\x103E\xcc\x161c\xc8d\xfd\xcfd1\xc3\xc6,\x9bm\x90o\xf0\xc4\x07\x15\x90\xcd\x02Z\xff@\xbb\xee\xba\xeb\xc2\xf8\xf1\xe3\xc3\x06\x1bl\xb0\xd2\xa4\x15\xee\xc9\r\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`` \x0c\xc4\x8c\x11\xb3F\xcc\x1c\xf2Y\xff\xf2Y\xcc\xb2E\xcd\x07\x15\xdf\xbe\xfaS\x81\xc2\x9a\x9c\r\xae5\xc35\x7f\xfe\xfcp\xd4QG\x85\xf5\xd6[O\xf0gl1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x961\x103G\xcc\x1e1\x83\xc8jk\xcejE\xa6=\xab?\xd97\xfb\xc7\x14\x85\x1a\x0f\xa8\xbe\x81Z\xb0`A8\xe6\x98c\xc2\xeb^\xf7\xba\x96M\xf0\x02D\xcf\xad\x06\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\xc0J\x0c\xc4\x0c\x12\xb3H\xcc$r[\xdf\xb9-f\xdb\xec\x03~_\x05X\xb8p\xe1^E\x91\x96\x03\xa9g\x90\xe2\xc7s\x1c\x7f\xfc\xf1.\xf5\xb7\x08\xaf\xb4\x08\x935d\x15\x060\x80\x01\x0c`\x00\x03\x18\xc0@\xbb\x19\x88o\r\x88\xd9\xc4G\x08\xf6\x9c\xddb\xa6\x8d\xd9\xf6\xe6\x9bo\x1eU\x8c\x8d\xafU+P\xdc\\bhQ\xa0g\x84\xff\x9e\x01:\xe3\x8c3\xc2[\xde\xf2\x16\xc1O\xf8\xc7\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06*\xc3@\xcc(1\xab\xc8q=\xe7\xb8\x98qc\xd6]5\xfff\xfd\xffEA6.\x80y\x044\xabCs\xf1\xc5\x17\xfb8?\x0b|e\x16\xf8b\xa1r,j\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\xb0\x1a\x03\xf1\xe3\x03cv\x91\xe9V\xcft1\xeb\xc6\xcc\x9bu\xe8\xef>\xf9k\xae\xb9f\xdd\xc2\x8a\xdc\x0e\x94\x95A\x99;wn\x18=ztXk\xad\xb5V\x9b\\B\x98\x10\x8a\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0cT\x8d\x81\x98]b\x86\x89YF\xbe[9\xdf\xc5\xcc\x1b\xb3o1fy\x7f\x15\x85\xb8\x00\x1c+\xc3q\xd2I\'\xb9\xb3?\xabJ\xfc`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0\x96\x0c\xc4O\x0c\x88\x99F\xce[9\xe7\xc5\xec\x9bu\xfa/n\xfaw\x04(^\x82\xe2\xb2\xcb.\x0b\xc3\x86\r\xab\xe5$/@v\xdcj\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\xfc\x95\x81\x98mb\xc6\x91\xf9^\xca|1\x03g)\x01\x8a\xf7@lU\x18\x90\xe7\xc1pk(j\x11\xc6\x8f\x1f\x1f\xd6Yg\x9d\xbfN\x16\x81\x9aP\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xea\xce@\xcc81\xeb\xc4\xcc#\xfbu}2\xc0\xf3E-\xb6*\xc65\x9f\xaf\xe2\x847,\x06\xff\xb7\x00\xb85L\x9b6-\xfc\xd3?\xfd\x93\xe0\xcf\x94b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\xc92\xb0\xe9\xa6\x9bve\x1f\x19\xb0\xebj\x80\xdf\xc6L\x9c\x8b\x01\x18T\x0c\xfa\xfc\xdc\x07\xbe\xb8\xf4#|\xf4\xa3\x1f\rk\xaf\xbdv\xb2\x93\xbc\x00\xda\xb9\xa9\x01\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\xd0\xc5@\xcc>1\x03\xc5,\x94{\x1e\x8c\x99\xb8\xc8K\x83\x92\x97\x00\xc5`\x7f:\xf7\xc1\xfe\xeew\xbf\x1b\x86\x0e\x1dj!\xb4\x10b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\xd91\x10\xb3P\xccD\xb9\xe7\xc2"\x1b\x7f*i\x01P\\\xe60\xb4\x18\xe4%9\x0f\xf4\xc4\x89\x13\xc3k_\xfb\xda\xec&\xb9\xab\x01\\\r\x81\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0ct3\x103Q\xccF9g\xc3\x98\x8dcF.j\x92\xde\xd7\xe5\x97_\xfe\xb7\xc5M\xff\xee\xcfu\x80\xe7\xcd\x9b\x17\xf6\xd8c\x0f\xc1\x9f\xe1\xc5\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\xf0"\x031#\xc5\xac\x94kN\x8c\x199f\xe5\xe4\x0c@qb\xe7\xe5:\xa8\x97^zi\xd8d\x93MLr\x0b=\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81U\x18\x88Y)f\xa6\\\xf3b\xf1V\x80o$%\x00\x8a\x81\xdc\'\xd7\xc1\xfc\xd2\x97\xbe\x14^\xfd\xeaW\x9b\xe4\xabL\xf2\x02p5Q\x03\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0\x8b\x81\x98\x99bv\xca57\xc6\xcc\x9c\x84\x04X\xb0`\xc1\x06\xc5\xc9\xfc1\xb7\x81\x8cw\xb6<\xf4\xd0C-h\x164\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\xfdd f\xa8\x98\xa5r\xcb\x8f13\xc7\xec\\{\tP\x9c\xc8\x8c\xdc\x06\xef\xea\xab\xaf\x0e\xc3\x87\x0f7\xc9\xfb9\xc9]\r\xe0j\x08\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0\x9b\x81\x98\xa5b\xa6\xca-G\xc6\xec\\\xd4\xa0\xbe_\x85\xb9\xd9=\xb7A\xbb\xec\xb2\xcb\xc2\xc6\x1bo,\xfc\x0b\xff\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\x1ad f\xaa\x98\xadr\xcb\x931C\xd7\xd2\x00\\u\xd5U\xaf.n\xfc\xf7@N\x03v\xdey\xe7\x85\xf5\xd7_\xdf$op\x92\x17\xa0\xab\x9d\x1a`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03]\x0c\xc4l\x153VN\x992f\xe8\x98\xa5k\'\x01\x8a\x03\xffJN\x035i\xd2\xa4\xb0\xce:\xebX\xac,V\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\x9a\xc4@\xccX1k\xe5\x94-c\x96\xae\x95\x00\xb8\xe9\xa6\x9b\xb6*\x0ezy.\x834~\xfcx\x13\xbcI\x13\xdcU\x00\xae\x82\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Ve f\xae\\\xf2e\xcc\xd21S\x175\xa8\xfe\xd7\xc4\x89\x13\xd7*\x0e\xf8\xee\x1c\x06\xa78\xcfp\xe0\x81\x07\n\xff\xc2?\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x163\x10\xb3W\xcc`\x99d\xcd\xbbc\xb6\xae\xbc\x01(\x06d\\\x0e\x03r\xf3\xcd7\x87Q\xa3F\x99\xe4-\x9e\xe4\x05\xf0j\xac\x06\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x17\x031\x83\xc5,\x96C\xe6\x8c\xd9\xba\xd2\x02\xe0\x9ak\xae\xf9\x7f\xc5@\xfc1\xf5\xc1(.\xc7\x08;\xef\xbc\xb3E\xc8"\x84\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0\xcd\x0c\xc4,\x163Y\xea\xb93f\xeb\x98\xb1++\x01\nCqv\xea\x83\xb0`\xc1\x82\xb0\xddv\xdb\x99\xe4m\x9e\xe4\xae\x04p%\x04\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\xd0\xcd@\xccd1\x9b\xa5\x9e?c\xc6.\xce\xb9z_\x85\x81\xf9\xa7\xa2\xf8KR\x1e\x80\xf9\xf3\xe7\x87-\xb7\xdcR\xf8\x17\xfe1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0ct\x98\x81\x98\xcdbFK9\x83\xc6\x8c\x1d\xb3v\xe5\x0c@a&\xaeL\xb9\xf0\xd1.\t\xff\x8cc1\xf1,\xf4j\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x8a0\x103Z\xeaW\x02\xc4\xac])\x01\xb0p\xe1\xc2]R\x0e\xff\xf1\xfd%.\xfb\x17|\x85\x7f\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0z\x0c\xc4\xac\x163[\xca\x994f\xee\x82\xbdj|\x15\x85^\x94j\xb1\xe3\x1d&\xdd\xf0\xafz\x93\xbc \x9fuU\x03\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0\x8b\x81\x98\xd9\x12\xfft\x80E\x95H\xff\x85\x89\xd8+\xd5\xf0_\\j\xe1\xa3\xfe,(6\x15\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0\x06\x0c\xc4\x8f\x08\x8c\x19.\xd5|\x1a\xb3w\xc7%@Q\xdc;S-\xf0\x81\x07\x1eh\xa2\xd7`\xa2\xbb\x1a\xc0\xd5\x10\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@d f\xb8T\xf3i\xcc\xde\xc59v\xee\xab\xb0+\x07\xa4Z\xdc\xf1\xe3\xc7\x0b\xff\xc2?\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x9a1\x10\xb3\\\xaa95f\xf0N\x19\x80A\xc5\x8b\xdf\x9bba\'M\x9ad\x92\xd7l\x92\x17\x93\xc0\x98\xa9\x01\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\xd0\xc5@\xcct)f\xd5\x98\xc1\x8b\xec3\xa8\xed\x12\xa0x\xff\xc1\x07S,\xe8y\xe7\x9d\x17\xd6Yg\x1d\x0b\x87\x85\x03\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@M\x19\x88\x99.f\xbb\x143k\xcc\xe2m\x17\x00E!\xff3\xb5b^v\xd9ea\xfd\xf5\xd77\xc9k:\xc9]\x05\xe0*\x08\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0\x9b\x81\x98\xedb\xc6K-\xb7\xc6,^\x9cc\xfb\xbe\x8a\x17\x1c\x99Z\x11\xaf\xbe\xfa\xea\xb0\xf1\xc6\x1b\x0b\xff\x1d\x08\xff\x83\x06\r\n\xeb\xae\xbbnXo\xbd\xf5\xc2\x1b\xde\xf0\x86\xf0\xc67\xbe1\xbc\xe5-o\to{\xdb\xdb\xc2\xdb\xdf\xfe\xf6\xb0\xe9\xa6\x9b\x86\xcd6\xdb,\xbc\xf3\x9d\xef\x0c\xefz\xd7\xbb\xc2\xbb\xdf\xfd\xee\xb0\xf9\xe6\x9b\xfbV\x03\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x8e1\x10{\xd2\xd8\x9b\xc6\x1e5\xf6\xaa\xb1g\x8d\xbdk\xecac/\x1b{\xda\xd8\xdb\xc6\x1e7\xf6\xba\xb1\xe7-\x12\x9b\xef6\xd7 f\xbc\x98\xf5R\xcb\xaf1\x93\xb7\xcd\x00\x14\xef;\xb86\xa5\x02\x16\x97P\x84\xe1\xc3\x87\x9b\x8cm\x98\x8cq\xf1{\xdd\xeb^\xd7\xb5 n\xb2\xc9&]\x0b\xe6{\xde\xf3\x9e\xe0[\r0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Rg \xca\x82\xd8\x03G9\x10{\xe2\xd8\x1b\x93\x02\xad\x97"1\xeb\xc5\xcc\x97R\x86\x8d\x99\xbc-\x02\xe0\xa6\x9bn\xda<\xa5\xc2\xc5s9\xf4\xd0CM\xbc\x16\x84\xffh9_\xf3\x9a\xd7\x84\x8d6\xda\xa8k\xa1\x8b\x864\xf5E\xdd\xf9i\\0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xca0\x10{\xe4(\x05b\xcf\x1c{gW\n\xb4F\x08\xc4\xcc\x97Z\x8e\x8d\xd9\xbc\xe5\x12\xa00\r\x17\xa6T\xb8\xd3O?]\xf8ob\xf8\x8f7\xdb\xd8`\x83\r\xba\x16\xb1xYT\x99\xc5\xcfcm\x96\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\xb93\x10{\xe8(\x04bO\xed\xe6\xe4\xcd\x95\x011\xfb\xa5\x94ec6o\xa9\x00(^\xe0ME\xc1\x96\xa4R\xb4K/\xbd4\xbc\xfa\xd5\xaf&\x00\x06(\x00\xe2\xa5K\xd1X\xbe\xe3\x1d\xef\x10\xf8\xbd\x9d\x01\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03Md \xf6\xd8\xb1\xd7\xf6v\x81\x81\xcb\x80\x98\xfdb\x06L%\xcf\xc6l\x1e3z\xcb$@\xf1\xbe\x89\xd3R)\xd6\xfc\xf9\xf3\xbb\xccZQ,\x02\xa0\x81\x1a\xbc\xf2\x95\xaf\x0c\x7f\xf7w\x7f\xd7u\xb3\x93\xdc-\xad\xf3\xf7/\x15\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\xed` \xdeh0\xf6\xe0\xb1\x17\x97c\x1a\xcbq\xf1\xea\x8a\x98\x05S\xc9\xb51\xa3\xb7D\x00\x14\xef/xea\x17~\x9fJ\xa1\xf6\xdcsO\x93\xa6\x81\xe0\x1f\xefb\xfa\xf7\x7f\xff\xf7]w[m\xc7"\xe75l\xa6\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18X\x99\x81\xf8\x89X\xb1\'\x8f\xbd9\x11P^\x04\xc4,\x98J\xae\x8d\x19=f\xf5\xa6K\x80\xe2\x89\xf7O\xa5H\x13\'N4QJ\x84\xff\xb5\xd6Z+l\xb8\xe1\x86]\x1fob\xf1]y\xf1U\x0f\xf5\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xe8$\x03\xf1#\x08c\xaf\x1e{v2\xa0\xff2`\xd2\xa4I)I\x80\xfd[!\x00\xe6\xa6 \x00\xbe\xfb\xdd\xef\x86\xd7\xbe\xf6\xb5&G?\x04@\xbc\xb4(~D\x89\xbb\xf7\xdb\xd4:\xb9\xa9ym\xfca\x00\x03\x18\xc0\x00\x060\x80\x8153\x10{\xf6\xd8\xbb{{@\xff$@\xcc\x841\x1b\xa6\x90q\x8b\x7f\xac\x9f\xdbT\x01P\\R\xb0q\xf1\xa4\xcb\xeb^\x9c\xf8\xd9\x8fC\x87\x0e\x15\xfe\xd7\x10\xfe\xe3\xa2\xf1\xa67\xbd)\xb8\x8b\xff\x9a\x17Z\x9b\x91\x1aa\x00\x03\x18\xc0\x00\x060\x80\x01\x0cT\x89\x81\xd8\xc3\xc7^\x9e\x08X\xb3\x08\x88\xd90f\xc4\xba\xe7\xdc\x98\xd5cfo\x9a\x04(\x9e\xf0\x94\xba\x17%\x1e\xffQG\x1d%\xfc\xf7\x11\xfe\xd7^{\xed.k(\xf8\xdb\xc4\xaa\xb4\x899\x16<b\x00\x03\x18\xc0\x00\x060\x80\x81\xf2\x0c\xc4\x9e>^\x11\x10{|o\r\xe8]\x06\xc4\x8c\x98B\xd6\x8d\x99\xbd)\x02\xa0x\xbf\xfcZEA\x1e\xac{Q\xa6M\x9b\x06\xfe>\xc2\x7f\xfc\x9cQ\x97\xfa\x97_XmFj\x86\x01\x0c`\x00\x03\x18\xc0\x00\x060Pe\x06b\x8f\x1f{}\x12\xa0g\t\x10\x05\xc9\xf4\xe9\xd3S\x90\x00\x0f\xc6\xec>`\tP\x98\x84]\xeb\x1e\xfe\x8b\xcb!\xba>\xae\x0e\xf4\xabC\x1f\xdf\xfb\x12?N\xa4\xca\x8b\x96c\xb3\xa9b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c```\x0c\xc4\x9e\xdf\xbd\xd0V\xcfC1#\xc6\xda\xc4\xccX\xf7\xdc\x1b\xb3{3\x04\xc0\x05u/\xc4\xf8\xf1\xe3\x19\xafU\xfe\xf5?\xbe\'\xe8mo{\x9b\xe0\xff\x9e\x81-\xa46"\xf5\xc3\x00\x060\x80\x01\x0c`\x00\x03\x18\xa8\x13\x031\x03\xb8?\xc0\xea" f\xc6\xba\xe7\xdeB\x00\\0 \x01PX\x90W\x16O\xf2x\x9d\x0bq\xd9e\x97\x85u\xd6Y\x87\x00x\x99\x00x\xfd\xeb_\xefr\x7f\xc1\x9f\xfc\xc1\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x94\x81\xf8\xb6\x80\x98\t\\!\xfd\x92\x08\x88\x991f\xc7:g\xdf\x98\xddc\x86oX\x02\x14O\xb0G\x9d\x0b\x10\x8f}\xd8\xb0a\xc0~1\xfcG\xd3\xf7\x0f\xff\xf0\x0f\x16\xfaL\x17\xfa:\x99i\xc7\xea_R0\x80\x01\x0c`\x00\x03\x18\xc0@\xeb\x19\x88\xd9\xc0\xd5\x00/I\x80\x98\x1d\xeb\x9e\x7fc\x86\x1f\x88\x00\xf8\x8f:\x17\xe0\xa4\x93N\x12\xfe_\x0c\xff\xeb\xaf\xbf\xbe\x7f\xf5\x17\xfc\xc9\x1f\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\xac\xc4@\xbc\x1a f\x05W\x03\xfc\x9f\x08\x88\x19\xb2\xce\x19\xb88\xf6\xe9\r\t\x80\xa9S\xa7\xfeM\xf1\xcbO\xd4\xf5\xe4\xe7\xce\x9d\x1b\xd6[o\xbd\xecA^k\xad\xb5\xc2\xc6\x1bo\xbc\xd2$gS[oS\xd5X\x8d1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xea\xc4@\xcc\x0c\x83\x06\r\xca>?\xc5\x0c\x19\xb3d]sp\xcc\xf01\xcb\x97\x96\x00\xc5\xa5\x03{\xd7\xf8\xa4\xc3\xe8\xd1\xa3\xb3\x877\xbe\x8f\xc5\x1d\xfem<u\xdax\x1c+^1\x80\x01\x0c`\x00\x03\x18\xc0@\xe7\x18\x88\xd9\xc1\xfd\xd3^\xd1\x95%\xeb\x9c\x85c\x96/-\x00\x8a\x13\x9eV\xd7\x93\xbe\xf8\xe2\x8bC\xfc\x97\xef\x9c/c\x89\xe6*^\xcec\x01\xed\xdc\x02\xaa\xf6j\x8f\x01\x0c`\x00\x03\x18\xc0\x00\x060P7\x06b\x86\xc8\xfdJ\xea\x98%c\xa6\xack\x1e.\x04\xc0\xb7\x1a\x11\x00\x0f\xd7\xf5\x84\xb7\xdaj\xab\xac\xc3\xffF\x1bm\x146\xdf|s\xe1\xdf\xfb\xbb0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xd2\x0c\xc4,\x113E\xce\xff\xa0\x1a3e]\xf3p!\x00\x1e*%\x00\x16.\\\xf8\x9e\xba\x9e\xec\x19g\x9c\x91-\xa8\xf1=;o}\xeb[KO\xf0\xbaYI\xc7\xcb\xa4c\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0\xf5\x0c\xc4l\x91\xf3}\x01&O\x9e\\[\t\x103}\xbf%@a\x0c>SG\x01p\xe3\x8d7v\xdd\xf0.GS\xb5\xf6\xdak\x87\xb7\xbf\xfd\xed\xc2?\xc3\x8b\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c4\x8d\x81\x981b\xd6\xc81c\xc5\x1b#\xc6\x8cY\xc7l\x1c3}\xbf\x05@q\x827\xd6\xf1$\x8f?\xfe\xf8,\xc1\x8c\x9f\xdd\xe9f\x7f\xad7\xa0,\xb3\x1ac\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\xe4\xc8@\xcc\x1a1s\xe4(\x01b\xc6\xacc6\x8e\x99\xbe_\x02\xe0\xa6\x9bnzm\xf1\xe0\xa5u;\xc9\x05\x0b\x16\x84\r6\xd8 ;(\xe3]:7\xdbl\xb3\xa6\x19\xbe\x1c\x174\xe7l#\xc7\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\xdf\x0c\xc4\xcc\x91\xe3\'\x04\xc4\x8c\x19\xb3f\xdd\xf2q\xcc\xf41\xdb\xafQ\x02\x14\x97\n\xec_\xc3\x93\x0b\xc7\x1csLv\xe1\x7f\xddu\xd7\r\xef|\xe7;\x85\x7f\x97xa\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03-g f\x8f\x98Ar\xbb\x12 f\xcd:f\xe4\x98\xed\xfb#\x00\xa6\xd4\xed\xe4\xe6\xcf\x9f\x1f^\xf7\xba\xd7e\x05b\x9cx\xff\xfc\xcf\xff\xdc\xf2I\xce\x84\xf6mB\xd5G}0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06rb f\x90\xdc$@\xcc\x9a1s\xd6-\'\x17\x02\xe0\xdc\xfe\x08\x80\xbb\xebvbG\x1duTV\xe1?^z\xe3_\xfem49m4\xce\x15\xef\x18\xc0\x00\x060\x80\x01\x0c`\xa0:\x0c\xc4,\x92\xdb\xdb\x01b\xe6\xac[N.\x04\xc0\xdd}\n\x80\xf8\x1e\x81\xe2A\xcb\xebtb\xd7]w]Xo\xbd\xf5\xb2\x11\x00\xf1\xe6\x1b\xde\xf3_\x9d\xc5\xcfFd,0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06rd f\x92\x9cn\x0c\x183g\xcc\x9eu\xca\xca1\xdb\xf7y\x1f\x80\xe2dF\xd6\xe9\x84\xe2\xb1\x8e\x1f?>\x9b\xf0\x1f?~\xc3\xdd\xfem09n0\xce\x19\xf7\x18\xc0@]\x19\x18<xp\xd8v\xdbm\xbde\xcf{\xb31\x80\x81$\x19\x88\xd9$\xa7\x8f\x08\x8c\xd9\xb3ny9f\xfc^\xaf\x02(\xfe\xf2\xd4:\x9dPa3\xc2\x86\x1bn\x98\x85\x00\x184hP\x88\x9f\xc1Y\xd7\x06\xc8qk\xde1\x80\x01\x0c` \x07\x06\xf6\xdak\xaf\xf0\x99\xcf|&\\t\xd1E\xe1\x8e;\xee\x08\x8b\x17/\x0e\x17\\p\x81\xfd[\xf8\xc3\x00\x06\x92e f\x94\x98U\x8a\x90\x99\xfcw\xcc\x9e1\x83\xd6)3\xc7\x8c\xdf\x97\x00\x98_\xa7\x939\xf9\xe4\x93\x93\x87\xac{"\xbd\xf5\xadoMv\xd1\xc8\xa1!t\x8e\x82\x0f\x060\x80\x81\xf4\x18\xd8}\xf7\xdd\xc3\'?\xf9\xc90m\xda\xb4\xb0h\xd1\xa2\xf0\xf4\xd3O\x87\x9e\xbe\x08\x80\xf4\xc6\xde|6\xa6\x18X\x99\x81\x98U\xbasK\xea\x7f\xc6\x0cZ\xa7\xcc\\\x1c\xeb\xfc\x1e\x05\xc0\xe5\x97_\xbev\xf1\x97O\xd7\xe9d\xfe\xf1\x1f\xff1\x0b\xd06\xdah#\xe1\x9f5\xc6\x00\x060\x80\x01\x0ct\x90\x81]v\xd9%|\xfc\xe3\x1f\x0f\xe7\x9f\x7f~W\xe3\xf7\xe4\x93O\xf6\x94\xf5{\xfc\x19\x01\xb0rP\x10\x9c\xd4\x03\x03i2\x103K\xea\xe1?\x9e_\xcc\xa0u\xca\xcc1\xe3\xc7\xac\xbf\x9a\x04X\xb8p\xe1{\xeat"_\xfd\xeaW\xb3\x00,\xdelb\xf3\xcd7\xd7\xf4u\xb0\xe9\xb3I\xa5\xb9I\x19W\xe3\x8a\x01\x0c\xf4\xc6\xc0\x8e;\xee\x18\x8e>\xfa\xe80e\xca\x94\xaeK=\x1f\x7f\xfc\xf1\x1e\x83}\x7f\x7fH\x00`\xad7\xd6\xfc\x1c\x1b)1\x103K.7g\x8fY\xb4N\xd99f\xfd\x9e\x04\xc0!u:\x89\xe1\xc3\x87\'/\x00\xe2Gk\xbc\xeb]\xef\x12\xfe\x85\x7f\x0c`\x00\x03\x18\xc0@\x8b\x18\xd8~\xfb\xed\xc3\xb8q\xe3\xc2\xd7\xbe\xf6\xb5p\xfd\xf5\xd7\x87\xdf\xff\xfe\xf7\xfd\xcd\xf5\xfd~\x1c\x01 \xe4\xa5\x14\xf2\x9c\x0b\x9e\xfbb f\x97\x1c>\x1e0f\xd1:e\xe7B\x00\x1c\xb2\x9a\x00(>"\xe0\xcc\xba\x9c\xc4\xb7\xbf\xfd\xed\xe4\xc3\x7f\xbc\x91\x86;\xfe[`\xfbZ`\xfd\x1d>0\x80\x01\x0c\x94c`\x9bm\xb6\tc\xc7\x8e\r_\xfe\xf2\x97\xc3\xb5\xd7^\x1b~\xf7\xbb\xdf\xf5;\xc4\x0f\xe4\x81\x04@\xb9q\xc2\xb5za\xa0\xde\x0c\xc4\x0c\x93\xc3M\x01c&\xadK~.\x8es\xf2j\x02\xa0\xf8\xe15u9\x811c\xc6$/\x006\xdexc\xff\xda\xd3\xa2\x7f\xed\xb1\xa9\xd4{S1~\xc6\x0f\x03\x18\xe8\x0f\x03#F\x8c\x08\xff\xfa\xaf\xff\x1a&O\x9e\x1c\xae\xba\xea\xaa\xf0\xc0\x03\x0f\x84\x15+V\x0c$\xc77\xfc\xbb?\xf9\xc9O\xec\xe9\xf6t\x0c` +\x06b\x96)\x02g\xd2\xdf1\x93\xd6%?\xc7\xac\xdf\x93\x00x\xb8\x0e\'p\xe3\x8d7\x86\xf5\xd7_?i\x98\xe2\xf9\xf5\xa7\xb9\xf1\x18M0\x060\x80\x01\x0c`\xe0=a\xcb-\xb7\x0c\x07\x1dtP8\xed\xb4\xd3\xc2\x9c9s\xc2/\x7f\xf9\xcb\xb0|\xf9\xf2\x86\x03{\xb3\x7f\xf1\xee\xbb\xef\xb6\xaf\x0b\x7f\x18\xc0@v\x0c\xe4\x90\xd9b6\xadC\x86.\x8e\xf1\xe1\x95\x04\xc0u\xd7]\xf7\x86\x9a\x1cx\x988qb\xd2\xe1\xff\x95\xaf|\xa5\xf7\xfd\xdb \xb2\xdb \x04\x18!\x16\x03\x18\xe8/\x03\xc3\x86\r\x0b\x1f\xf8\xc0\x07\xba\xfa\x81\xd9\xb3g\x87\x9f\xff\xfc\xe7a\xd9\xb2e\xcd\xce\xecM}>\x02\x00\xdf\xfd\xe5\xdb\xe3\xb0\x92\x12\x03\xf1~\x001\xdb\xa4|%@\xcc\xa6u\xc9\xd11\xf3\xffU\x02\x14\xef\xff\xdf\xb1.\x07\x1e-\x7f\xca\x10\xfd\xc3?\xfc\x83\xf0G\x00`\x00\x03\x18\xc0\x00\x06\n\x06\x86\x0c\x19\x12F\x8f\x1e\x1d&L\x98\x10f\xce\x9c\x19~\xfa\xd3\x9f\x86%K\x9645\x9c\xb7\xe3\xc9\x08\x00\xa1.\xa5P\xe7\\\xf0\\\x86\x81\x98mR\xcen[m\xb5Um\x04@\xcc\xfc/\x17\x00\xc7\xd6A\x00\xcc\x9a5+i\x80^\xff\xfa\xd7k\xf84\xfd\x18\xc0\x00\x060\x90-\x03\xfb\xee\xbbo8\xe9\xa4\x93\xc2\xc5\x17_\x1c\xee\xba\xeb\xae\xf0\xdcs\xcf\xb5#\x9f\xb7\xfc5\x08\x00\x81\xa9L`\xf2X\xbc\xa4\xc6@\xcc8)K\x80\x98Q\xeb\x90\xa5\x0b\x01p\xec\xcb\x05\xc09u8\xe8\xc3\x0e;,Yx\\\xfao\xb1Om\xb1w>\x98\xc6\x00\x06\xfab`\xd4\xa8Q\xe1S\x9f\xfaT\xf8\x8f\xff\xf8\x8f\x10o\x92\xf7\xec\xb3\xcf\xb6<\x88w\xea\x05\x08\x00s\xa1\xaf\xb9\xe0\xef\xf0\x91:\x03\xa9\xbf\x15 f\xd4:d\xe9B\x00\x9c\xf3r\x010\xa7\x0e\x07\xfd\xe67\xbf9Y\x01\xf0\xb6\xb7\xbd-\xdb\x7f\xf1I}\xd1s~6v\x0c` w\x06F\x8e\x1c\x19>\xf1\x89O\x84\xf8qx?\xfa\xd1\x8f\xc2SO=\xd5\xa9,\xde\x91\xd7%\x00\xac\x01\xb9\xaf\x01\xce\xdf\x1c\x88Y\'\xd5\xab\x00bF\xadC\x96.\x04\xc0\x9c\xbf\n\x80\xe2\x80\xef\xa9\xfaAO\x9b6-Yh^\xfb\xda\xd7\n\xff.\xf9\xc5\x00\x060\x80\x81$\x18\xd8i\xa7\x9d\xc21\xc7\x1c\x13\xce;\xef\xbc\xb0p\xe1\xc2\xf0\xe7?\xff\xb9#\xa1\xbbJ/J\x00\x08?\x020\x060\xf0\x9e\x103O\xaa\x12 f\xd5\xaa\xe7\xe9\x98\xf9\xff*\x00\n\x1b\xf0\xbfU?\xe0\x83\x0f>8Y`6\xddt\xd3$\x9a>\x0b\x9b\xcd\r\x03\x18\xc0@^\x0c\xec\xb0\xc3\x0e\xe1\xa8\xa3\x8e\n\xe7\x9e{n\xb8\xe1\x86\x1b\xc2\x1f\xff\xf8\xc7*\xe5\xee\xca\x1c\x0b\x01\x90\xd7\xbc\xb0\x0e\x1ao\x0c\xf4\xcc@\xcc<\xa9\n\x80\x98U\xab\x9e\xa7c\xe6\xef\x12\x00\xc5\x81\xbe\xbe\xea\x07\x1b\x8f\xef\x8do|c\x92\xc0l\xb0\xc1\x06\xc2\xbf\x7f\xf5\xc3\x00\x060\x80\x81\xca3\xb0\xddv\xdb\x85#\x8e8"|\xf5\xab_\r\xf3\xe6\xcd\x0b\x0f?\xfcpe\x02v\xd5\x0f\x84\x00\xe89\x0c\x08I\xea\x82\x81\xfc\x18\x88\xd9\'E\t\x10\xb3j\x1d2u\xcc\xfe\xaf(.\xcf\x1bV\xf5\x83\x9d:uj\x92\xa0\xac\xbd\xf6\xda!\xde\x14\xc3\xe2\x97\xdf\xe2g\xcc\x8d9\x060Pe\x06F\x8c\x18\x11>\xfc\xe1\x0f\x87\xb3\xce:+\\}\xf5\xd5\xe1\xc1\x07\x1f\x0c+V\xac\xa8z\xce\xae\xec\xf1\x11\x00\xe6{\x95\xe7\xbbc\xc3g;\x19\x88\xd9\'f\xa0\x14%@\xcc\xacU\xcf\xd51\xfbG\x01p`\xd5\x0ft\xcc\x981IB\x12MQ;\'\x9c\xd7\xb2\xc0c\x00\x03\x18\xc0\xc0\xaa\x0c\xc4\xcf0>\xe4\x90C\xc2\xe9\xa7\x9f\x1e\xae\xbc\xf2\xca\xf0\xeb_\xff:\xbc\xf0\xc2\x0b\x95\r\xd3u<0\x02\xc0\xbc[u\xde\xf9\x7fL\xe4\xcc@\xaaWv\xc7\xccZ\xf5\\\x1d\xb3\xff+\x8a\xf7\x02\x1c[\xf5\x03M\xf1\xee\xff\xf1c\xff\xde\xfd\xeew\x13\x00.\xfb\xc5\x00\x060\x80\x81\xb610l\xd8\xb0\x10\x1b\x94/|\xe1\x0b\xe1\x8a+\xae\x08\xbf\xf8\xc5/\xc2\xb2e\xcb\xea\x98\xa9ku\xcc\x04\x80\xb0\x97s\xd8s\xee\xf8_\x95\x81\x98\x81b\x16J\xed*\x80:|\x1a@\xcc\xfe\xaf(\xc2\xff\xa9U\x16\x003g\xceL\x0e\x8e\x08\xfb\x9b\xde\xf4\xa6\xb65|\xabN:\xffo!\xc6\x00\x060\x90>\x03C\x87\x0e\r\x07\x1ex`8\xe5\x94S\xc2\xacY\xb3\xc2}\xf7\xdd\x17\x96.]Z\xab\xe0\x9c\xca\xc1\x12\x00\xe9\xcf7k\xaa1\xc6@9\x06b\x16JM\x00\xc4\xf3\x89\xd9\xb5\xca\xd9:f\xffx\x05\xc0\x94*\x1f\xe4q\xc7\x1d\x97\x1c\x1c\xfe\xf5\xbf\xdc\x02aAU/\x0c`\x00\x03}30x\xf0\xe0\xb0\xff\xfe\xfb\x87\xcf}\xees\xe1\xd2K/\r\xf7\xdcsOx\xfe\xf9\xe7S\xc9\xcf\xb5?\x0f\x02\xa0o~\xcdo\xf5\xc1@~\x0c\xa4z\x15@\xcc\xaeU\xce\xd61\xfbG\x010\xb3\xca\x07\xb9\xf5\xd6[\'\'\x00\xbc\xf7?\xbfE\xce\xc6f\xcc1\x80\x81f2\xb0\xf7\xde{\x87\x13O<1\xcc\x981#\xdcq\xc7\x1da\xf1\xe2\xc5\xb5\x0f\xc9)\x9f\x00\x01`\xfe7s\xfe{.<\xa5\xc2@\x8a\xf7\x02\x88\xd9\xb5\xca\xd9:f\xff\xf8\x16\x80\xf9U=\xc8\x05\x0b\x16\x84u\xd6Y\')\x01\xb0\xd6Zk\xb9\xf3\xbf\xf7\xfbz\xfb\x07\x060\x80\x81~3\xb0\xc7\x1e{\x84\x13N8!L\x9f>=,Z\xb4(<\xfd\xf4\xd3)g\xe5$\xcf\x8d\x00\x10\xd8R\tl\xce\x03\xcb\xcdd ~"@\xccF)\xbd\x15 f\xd7\x98a\xab\x9a\xafc\xf6\x8fW\x00\xdcU\xd5\x03<\xfb\xec\xb3\x93\x02"\xc2\x1d?\xfb\xb2\x99\x13\xc7sY\x881\x80\x01\x0c\xa4\xc3\xc0\xae\xbb\xee\x1a\xe2\xe5\x83\xe7\x9f\x7f~W\xf3\xf0\xe4\x93O&\x19\x88s;)\x02 \x9d9j\xbd5\x96\x18h.\x031\x1b\xa5$\x00\xe2\xb9\xc4\x0c[\xd5|\x1d\xb3\x7f\xbc\x02\xe0\xc1\xaa\x1e\xe0\x07>\xf0\x81\xe4\x80\xd8l\xb3\xcd\x08\x00\xff\xf2\x87\x01\x0c`\x00\x03a\xc7\x1dw\x0cG\x1f}t\xf8\xf7\x7f\xff\xf7p\xd3M7\x85\xc7\x1e{,\xb7\\\x9c\xcd\xf9\x12\x00\xcd\r\x0c\x02\x98zb \x1d\x06b6JM\x00\xc4\x0c[\xd5|\x1d\xb3\x7f\xbc\x02\xe0\x99\xaa\x1e\xe0;\xde\xf1\x8e\xa4\x80Xo\xbd\xf54\xfd\x9a~\x0c`\x00\x03\x192\xb0\xfd\xf6\xdb\x87q\xe3\xc6\x85\xaf}\xedk\xe1\xfa\xeb\xaf\x0f\x8f>\xfah6\xe1\xd7\x89\x86@\x00\xa4\x13V\x04Oc\x89\x81\xe63\x103RJ\x12 f\xd8\xaa\xe6\xeb\xe2\xb8\x9e\x8e\x02`Y\x15\x0fp\xde\xbcy\xc9\xbd\'\xe4\xef\xff\xfe\xef5\xfe\x196\xfe6\x8a\xe6o\x14j\xaa\xa6Uf`\x9bm\xb6\t\x1f\xf9\xc8G\xc2\x97\xbf\xfc\xe5p\xed\xb5\xd7\x86\x87\x1ezH\x06\xce\xbc\x02\x04\x805\xab\xcak\x96c\xc3g\xa7\x19\x88\x19)%\x01\x10\xefk\x10\xb3l\x153v\xcc\xfe\xf1-\x00\x95<\xb8s\xce9\')\x10\xe2G\xffm\xbe\xf9\xe6\x04\x00\x01\x80\x01\x0c` !\x06F\x8c\x18\x11\x0e=\xf4\xd00y\xf2\xe40w\xee\xdc\xf0\xc0\x03\x0f\x84\x15+Vd\x1ew\x9d\xfe\xaa\x15 \x00\x04\xacN\x07,\xaf\x8f\xc1*3\x103R\xccJ)I\x80\x98e\xab\x9a\xb3++\x00\x0e?\xfc\xf0\xa4 \xf8\xbb\xbf\xfb;M\x7fBM\x7f\x95\x17Q\xc7f\x93\xc7@k\x18\xd8r\xcb-\xc3A\x07\x1d\x14N;\xed\xb40g\xce\x9c\xf0\xab_\xfd*,_\xbe|\xd5\xac\xe7\xffU`\xb5\n\x10\x00\xad\x99\x93\xd6:u\xc5@:\x0c\xc4\xac\x94\x92\x00\x88Y\x96\x00(y\xa5\xc1\xf0\xe1\xc3\x93\x82`\xd3M7%\x00\x08\x00\x0c`\x00\x035a`\xd8\xb0a!\xde\xc4g\xe2\xc4\x89a\xf6\xec\xd9\xe1\xfe\xfb\xef\x0fK\x97.]-\xd8\xf9\x81\n\xf4\xa7\x02\x04@:!E\xe04\x96\x18h\r\x031+\xa5$\x00b\x96%\x00J\x08\x80\x85\x0b\x17\x86W\xbf\xfa\xd5\xc9@\xb0\xee\xba\xebj\xfak\xd2\xf4[\xd4[\xb3\xa8\xab\xab\xbaV\x99\x81!C\x86\x84\xd1\xa3G\x87\t\x13&\x84\xcb.\xbb,\xdc{\xef\xbda\xc9\x92%\xfd\xc9u\x1e\xa3\x02\xfd\xaa\x00\x01`\r\xac\xf2\x1a\xe8\xd8\xf0Y\x15\x06bfJE\x02\xc4,\x1b3m\x15%@%\xdf\x020c\xc6\x8cd\x06?B\xbc\xd1F\x1b\x11\x00\x04\x00\x060\x80\x81\x8a0\xb0\xef\xbe\xfb\x86\x7f\xfb\xb7\x7f\x0b\x97\\rI\xd7\xdd\xd9\x9f{\xee\xb9~\x858\x0fR\x81F+@\x00\x08XU\tX\x8e\x03\x8bUf f\xa6T\x04@<\x8f\x98i\t\x80~^\x05\xf0\xf9\xcf\x7f>\xa9\xc1\x8f\x1f\x05Q\xe5\xc9\xe6\xd8l\x06\x18\xc0@\xaa\x0c\x8c\x1a5*|\xfa\xd3\x9f\x0e\x17^xa\xb8\xfd\xf6\xdb\xc3\xb3\xcf>\xdbh\x86\xf3{*\xd0p\x05\x08\x00kl\xaak\xac\xf3\xc2v3\x19H\xedm\x001\xd3\x12\x00\xfd\x14\x00\x87\x1crH2\x02`\x9du\xd6\x11\xfe+\xf2\xaf~\xcd\\\xa0<\x97\r\x0f\x03\xd5c`\xb7\xddv\x0b\xc7\x1f\x7f|\xf8\xd6\xb7\xbe\x15n\xbb\xed\xb6\xf0\xd4SO5\x1c\xd8\xfc\xa2\n4\xb3\x02\x04@\xf5\xd6\x0bk\xb81\xc1@5\x19\x88\xd9)\x95\xab\x00b\xa6%\x00\xfa)\x00\xb6\xdez\xebd\x06~\x83\r6 \x00\x08\x00\x0c`\x00\x03Mf`\xe7\x9dw\x0e\xc7\x1e{l\xf8\xe67\xbf\x19\x8a\xcf\xb4\r\x8f?\xfex3\xf3\x9a\xe7R\x81\xa6V\x80\x00\xa8f\xd0\x10\x00\x8d\x0b\x06\xaa\xc7@\xccN\xa9\x08\x80\x98i\t\x80~\n\x80\r7\xdc0\x99\x81\xdfd\x93M4\xfeMn\xfc-\xd6\xd5[\xac\x8d\x891i%\x03;\xec\xb0C\x18?~|\x982eJ\xb8\xe1\x86\x1b\xc2\x1f\xfe\xf0\x87\xa6\x863O\xa6\x02\xad\xae\x00\x01`\x8dl\xe5\x1a\xe9\xb9\xf1\x95\x12\x031;\xa5"\x00b\xa6%\x00\xfa!\x00\xae\xbe\xfa\xead\x06}\xd0\xa0A\xe1\xdd\xef~7\x01@\x00`\x00\x03\x18\xe8\'\x03\xdbm\xb7]8\xf2\xc8#\xc39\xe7\x9c\x13\xe6\xcd\x9b\x17\x1e~\xf8\xe1Vg3\xcf\xaf\x02-\xaf\x00\x01 \xa0\xa5\x14\xd0\x9c\x0b\x9e[\xc9@\xccN1C\xa5"\x01b\xb6\xad\x9a\x04\xa8\xdc\xa7\x00\x9c{\xee\xb9\xc9\x0c\xf8k^\xf3\x1aM\x7f?\x9b\xfeV.$\x9e\xdbF\x85\x81j20b\xc4\x880v\xec\xd8p\xd6Yg\x85\xb8A\xfe\xf6\xb7\xbf\r+V\xachy\x18\xf3\x02*\xd0\xee\n\x10\x00\xd5\\\x83\xec\r\xc6\x05\x03\xd5d f\xa8T\x04@\xcc\xb6\x04\xc0\x1a\xae\x028\xe1\x84\x13\x92\x19p\x1f\xffW\xcdE\xc5bo\\0\xd0y\x06.\xbf\xfcra\xbf\xdd)\xd4\xebu\xac\x02\x04@\xe7\xd7\x1c\xeb\xbe1\xc0@}\x18H\xe9\xe3\x00c\xb6%\x00\xd6 \x00\x0e:\xe8\xa0d\x04\x80\xf7\xff\xd7g\xa1\xb1)\x18+\x0c\xb4\x97\x81\xf8/\xfe\xbeT \x97\n\x10\x00\xed]_\xac\xe7\xea\x8d\x81z3\x90\xd2}\x00b\xb6%\x00\xd6 \x00\xe2\x9d\x9dS\xb9\xe4\xe3]\xefz\x97\xb7\x00x\x0b\x00\x060\x80\x81\x1e\x18 \x00r\x89\xbe\xce3V\x80\x00\xa8w\x18\x11&\x8d\x1f\x06\xda\xcb@\xccP\xa9\xe4\xc1\x98m\t\x805\x08\x80\xcd6\xdb,\x89\x01_w\xddu5\xfd=4\xfd\x16\xd0\xf6.\xa0\xea\xad\xdeUe\x80\x00\x10\x8cs\xaa\x00\x01`-\xae\xeaZ\xec\xb8\xb0YU\x06b\x96JA\x02\xc4lK\x00\xacA\x00\xac\xbf\xfe\xfaI\x0c\xf6\xeb^\xf7:\x02\x80\x00\xc0\x00\x060\xd0\x0b\x03\x04@N\xf1\xd7\xb9\x12\x00BVUC\x96\xe3\xc2fU\x19\x88Y*\x05\x01\x10\xb3-\x01\xd0\x87\x00\x88\x1f\xf9\x94\xc2@\xc7sx\xe3\x1b\xdf\xa8\xf1\xef\xa5\xf1\xaf\xeaB\xe3\xb8l\x82\x18h\x1f\x03\x04\x80P\x9cS\x05\x08\x80\xf6\xad-\xd6q\xb5\xc6@\x1a\x0c\xc4,\x95J.\x8c\x19\xb7J\x12\xa0R\x1f\x038c\xc6\x8cd\x06\xda\r\x00\xd3X|l"\xc6\x11\x03\xada\x80\x00\xc8)\xfe:W\x02\xa05\xeb\x88\xf5Y]1\x90.\x03)\xdd\x080f\\\x02\xa0\x97\xab\x00\xce>\xfb\xecd\x04\xc0;\xdf\xf9NW\x00\xb8\x02\x00\x03\x18\xc0@/\x0c\x10\x00BqN\x15 \x00\xd2\r)\x02\xa8\xb1\xc5@k\x18\x88Y*\x95+\x00b\xc6%\x00z\x11\x00\x13&LHb\xa0\x07\r\x1a\xa4\xe9\xef\xa5\xe9\xb7H\xb6f\x91TWu\xad\x1b\x03\x04@N\xf1\xd7\xb9\x12\x00\xd6\xe8\xba\xad\xd1\x8e\x17\xb3U` f\xaa\x14$@\xcc\xb8\x04@/\x02\xe0\xb8\xe3\x8eKb\x90}\x02\x80E\xb3\n\x8b\xa6c\xc0a\x95\x19 \x00\x84\xe2\x9c*@\x00X\x8f\xab\xbc\x1e;6|V\x95\x81T>\t f\\\x02\xa0\x17\x01p\xd8a\x87%!\x00\xd6[o=W\x00\xb8\x02\x00\x03\x18\xc0@\x1f\x0c\x10\x009\xc5_\xe7J\x00\x08XU\rX\x8e\x0b\x9bUf f\xaa\x14\xae\x00\x88\x19\x97\x00\xe8E\x00\x1cp\xc0\x01I\x0c\xf2\x1b\xde\xf0\x06\x8d\x7f\x1f\x8d\x7f\x95\x17\x1a\xc7f#\xc4@{\x18 \x00\x84\xe2\x9c*@\x00\xb4g]\xb1~\xab3\x06\xd2b f\xaa\x14\x04@\xcc\xb8\x04@/\x02`\xe7\x9dwNb\x90}\x04`Z\x8b\x8f\xcd\xc4xb\xa0\xf9\x0c\x10\x009\xc5_\xe7J\x004\x7f\r\xb1.\xab)\x06\xd2g \x95\x8f\x02\x8c\x19\x97\x00\xe8E\x00l\xb1\xc5\x16I\x08\x80\xb7\xbc\xe5-\xae\x00p\x05\x00\x060\x80\x81>\x18 \x00\x84\xe2\x9c*@\x00\xa4\x1fT\x84Qc\x8c\x81\xe63\x103U\nW\x00\xc4\x8cK\x00\xf4"\x00\xde\xf1\x8ew$1\xc8o{\xdb\xdb4\xfe}4\xfe\x16\xc8\xe6/\x90j\xaa\xa6uc\x80\x00\xc8)\xfe:\xd7Y\xb3f\xe9\x0b\xf4\x05\x18\xc0\x00\x06J2\x103U\n\x02 f\\\x02\xa0\x17\x01\xb0\xc9&\x9b$1\xc8o\x7f\xfb\xdbM\xf0\x92\x13\xbcn\xe1\xc5\xf1\n\xdc\x18\x18\x18\x03\x04\x80P\x9cS\x05.\xb8\xe0\x02}\x81\xbe\x00\x03\x18\xc0@I\x06b\xa6JA\x00\xc4\x8cK\x00\xf4"\x00R\xb9\xccc\xd3M75\xc1KNpaj`aJ\xfd\xd4\xafn\x0c\x10\x009\xc5_\xe7J\x00X\xa3\xeb\xb6F;^\xccV\x81\x81\x98\xa9R\x10\x001\xe3\x12\x00\xbd\x08\x80\x8d6\xda(\x89A\xdel\xb3\xcd\x08\x00\x02\x00\x03\x18\xc0@\x1f\x0c\x10\x00BqN\x15 \x00\x84\xa9*\x84)\xc7\x80\xc3\xba1\x103U\n\x02 f\\\x02\xa0\x17\x01\xf0\xfa\xd7\xbf>\x89A~\xe7;\xdf\xa9\xf1\xef\xa3\xf1\xaf\xdb\xe2\xe3xm\x98\x18h>\x03\x04@N\xf1\xd7\xb9\x12\x00\xcd_C\xac\xcbj\x8a\x81\xf4\x19\x88\x99*\x05\x01\x10?\xce\x90\x00\xe8E\x00\xac\xb7\xdezI\x0c\xf2\xbb\xde\xf5.\x02\x80\x00\xc0\x00\x060\xd0\x07\x03\x04\x80P\x9cS\x05\x08\x80\xf4\x83\x8a0j\x8c1\xd0|\x06b\xa6JA\x00\xc4\x8cK\x00\xf4"\x00^\xf5\xaaW%1\xc8\xef~\xf7\xbb5\xfe}4\xfe\x16\xc8\xe6/\x90j\xaa\xa6uc\x80\x00\xc8)\xfev\xf6\\\x17/^\x1c\xe2\xc7\xf0]r\xc9%\xe1\xbf\xff\xfb\xbf;r0\x04\x805\xbank\xb4\xe3\xc5l\x15\x18\x88\x99*\x05\x01\x103.\x01\xd0\x8b\x00X{\xed\xb5\x93\x18\xe4\xcd7\xdf\x9c\x00 \x000\x80\x01\x0c\xf4\xc1\x00\x01\xd0\x91\x1c\x9a\xfc\x8b>\xff\xfc\xf3\xe1\xde{\xef\r\x97]vY8\xf9\xe4\x93\xc3\x81\x07\x1e\x18\x06\x0f\x1e\xfc\xd7\xb9\xb8h\xd1\xa2\x8e\xd4\x80\x00\x10\xa6\xaa\x10\xa6\x1c\x03\x0e\xeb\xc6@\xccT)\x08\x80\x98q\t\x00\x02\xe0\xaf\xcdH\xdd&\xa2\xe3\xb5y`\x00\x03\xcd`\x80\x00\xe8H\x0eM\xeaE\x97.]\x1a\xee\xbf\xff\xfe0{\xf6\xec0i\xd2\xa4\xf0\xc1\x0f~0\x0c\x1d:\xb4\xcf\xfd\x95\x00\xb0~5c\xfd\xf2\x1c8\xc2@{\x18 \x00nm\x898xE\x95l\x84+\x00\xda3\x99,Z\xea\x8c\x01\x0ct\x9a\x01\x02 \xa9,\xde\xf2\x93Y\xbe|y\xf8\xd5\xaf~\x15\xe6\xcc\x99\x13\xbe\xf4\xa5/\x85\x83\x0f>8l\xb1\xc5\x16}\x86\xfd\x9e\x18\'\x00\xac}=q\xe1g\xb8\xc0@5\x19 \x00\x08\x80\xda\\\x02\xe2-\x00\xd5\\D,\xee\xc6\x05\x03\xd5a\x80\x00hyf\xae\xed\x0b\xbc\xf0\xc2\x0b\xe1\x81\x07\x1e\x08s\xe7\xce\rg\x9eyf8\xec\xb0\xc3\xc2\xf0\xe1\xc3K\x87\xfd\x9e\xe6;\x01P\x9d5\xa0\xa7\xf1\xf13\xe3\x83\x01\x0c\xbc\x9c\x01\x02\x80\x00 \x00\xfax?\xed\xcb\'\x8b\xff\xb6xb\x00\x03Ug\x80\x00\xa8m>o\xfa\x81?\xf4\xd0C\xe1\xba\xeb\xae\x0b_\xf9\xcaW\xc2\xe1\x87\x1f\x1e\xb6\xdez\xeb\xa6\x84\xfd\x9e\xe6\x00\x01`m\xec\x89\x0b?\xc3\x05\x06\xaa\xc9\x00\x01@\x00\x10\x00\x04@\xcb\x9aB\x0b\x7f5\x17~\xe3\x92\xee\xb8\x10\x00M\xcf\xd1\xb5x\xc2G\x1f}4,X\xb0 |\xfd\xeb_\x0f\xe3\xc6\x8d\x0b\xdbo\xbf}[\xd7u\x02 \xdd5\xc5~al1\x90\x1e\x03\x04\x00\x01@\x00\x10\x00mm\x14m$\xe9m$\xc6\xb4:cJ\x00\xd4"\xaf\x0f\xe8 \x1f{\xec\xb1p\xf3\xcd7\x87o|\xe3\x1b\xe1\xe8\xa3\x8f\x0e\xef{\xdf\xfb:\xbe\x86\x13\x00\xd5Y\x03\xac\xc7\xc6\x02\x03\x18X\x13\x03\x04\x00\x01@\x00\x10\x00\x1do\x1e\xd7\xb4P\xf9{\x9b\x19\x06\xfa\xc7\x00\x010\xa0l]\xb9_~\xe2\x89\'\xc2\x0f\x7f\xf8\xc30u\xea\xd4p\xdcq\xc7\x85]v\xd9\xa5\x92\xeb5\x01\xd0\xbf\xf9i\x1dS\'\x0c`\xa0\n\x0c\x10\x00\x04\x00\x01@\x00T\xb2\xa1\xac\xc2\x02\xe9\x18l\xd4uc\x80\x00\xa8\\\x86\xef\xf7\x01=\xf5\xd4S\xe1\xc7?\xfeq\x98>}z8\xe1\x84\x13\xc2\x1e{\xecQ\x9b\xb5\x99\x00\xb0V\xd6m\xadt\xbc\x98\xcd\x99\x01\x02\x80\x00 \x00\x08\x80\xda4\x999/\xd6\xce]\xb3\xd2\x1f\x06\x08\x80~\xe7\xed\x8e>\xf0\xd9g\x9f\rw\xdeyg\x981cF8\xf1\xc4\x13\xc3\xde{\xef]\xebu\x98\x00\xb0>\xf5g}\xf2\x18\x9c`\xa0\x1a\x0c\x10\x00\x04\x00\x01@\x00\xd4\xba\xf1\xb4\x99Tc31\x0e\xd5\x18\x07\x02\xa0\xa3\xb9\xbe\xc7\x17\xff\xcb_\xfe\x12\xee\xb9\xe7\x9ep\xe9\xa5\x97\x86\xcf}\xeesa\xbf\xfd\xf6\x0b\x83\x07\x0fNj\xdd%\x00\xaa1\xff\xad\xc3\xc6\x01\x03\x18\xe8\x0f\x03\x04\x00\x01@\x00\x10\x00I5\xa2\xfdY\xf8<\xc6\x06\x99*\x03\x04@\x8f\x19\xbcm?\\\xbati\xb8\xef\xbe\xfb\xc2\xacY\xb3\xc2)\xa7\x9c\x12\xde\xff\xfe\xf7\x87!C\x86$\xbf\xc6\x12\x00\xd6\xd4T\xd7T\xe7\x85\xed\x14\x19 \x00\x08\x00\x02\x80\x00H\xbe9Mq\xf1vN\x9a\x92\x9e\x18 \x00\xda\x96\xf5\xc3\xb2e\xcb\xc2/~\xf1\x8bp\xc5\x15W\x84/|\xe1\x0ba\xcc\x981a\xd8\xb0aY\xae\xa7\x04\x80\xf5\xa8\xa7\xf5\xc8\xcfp\x81\x81j2@\x00\x10\x00\x04\x00\x01\x90e\xc3jS\xaa\xe6\xa6d\\\x066.\x04@k\x04\xc0\x0b/\xbc\x10~\xfd\xeb_\x87+\xaf\xbc2\x9c~\xfa\xe9\xe1\x90C\x0e\t[n\xb9\xa5\xb5\xf3\xc5\xfd\x93\x00\x18\xd8\xbc\xb5\xee\xa9\x1f\x060\xd0N\x06\x08\x00\x02\x80\x00 \x004\xb1\x18\xc0@"\x0c\x10\x00\x03\x17\x00+V\xac\x08\x0f>\xf8`\x88\xb5<\xeb\xac\xb3\xc2\xd8\xb1c\xc3\x88\x11#\xcc\x91>\xe6\x08\x01 \xbc\xb43\xbcx-\xbca``\x0c\x10\x00\x04\x00\x01\xd0GSc\x81\x19\xd8\x02\xa3~\xea\x87\x81\xf62@\x00\x94\x17\x00\x0f?\xfcp\x987o^8\xe7\x9cs\xc2\x91G\x1e\x19\xb6\xddv[a\xbf\xe4\xbeH\x00\xb4w\x9e[W\xd5\x1b\x03\x18\x18\x08\x03\x04\x00\x01@\x00\x94lt\x062\xe1\xfc\xae\x05\x1b\x03\x18h%\x03\x04@\xdf\x02\xe0\x0f\x7f\xf8C\xb8\xe1\x86\x1b\xc2\x94)S\xc2\xf8\xf1\xe3\xc3{\xdf\xfb^a\xbf\t{ \x01`]k\xe5\xba\xe6\xb9\xf1\x85\x81\xe62@\x00\x10\x00\x04@\x13\x9a\x1f\x0bSs\x17&\xf5TO\x0c4\xc6\x00\x01\xf0\x92\x00x\xfc\xf1\xc7\xc3-\xb7\xdc\x12\xbe\xf9\xcdo\x86c\x8e9&\xec\xb4\xd3N\xc2~\x8b\xf6;\x02\xa0\xb1\xf9j\x9dS7\x0c`\xa0\x13\x0c\x10\x00\x04\x00\x01\xd0\xa2\x86\xa8\x13\x13\xdak\xdaH0\x907\x03\xb9\n\x80\xff\xfd\xdf\xff\r\xb7\xddv[\xf8\xd6\xb7\xbe\x15>\xf1\x89O\x84\x91#G\n\xfbm\xdc\xdb\x08\x80\xbc\xd7\x1d\xfb\x8e\xf1\xc7@\xbd\x18 \x00\x08\x00\x02\xa0\x8dM\x92\x05\xb2^\x0b\xa4\xf12^uc \x07\x01\xf0\xcc3\xcf\x84\xdbo\xbf=\\x\xe1\x85\xe1S\x9f\xfaT\x185j\x94\xb0\xdf\xe1}\x8c\x00\xb0V\xd6m\xadt\xbc\x98\xcd\x99\x01\x02\x80\x00 \x00:\xdc8\xe5\xbc\x009w\x1b0\x06\x9a\xcb@j\x02`\xf1\xe2\xc5\xe1\xee\xbb\xef\x0e\x97\\rI8\xe9\xa4\x93\xc2\xbe\xfb\xee+\xecWp\xcf"\x00\x9a;\x8f\xad\x8b\xea\x89\x01\x0c\xb4\x92\x01\x02\x80\x00 \x00*\xd8L\xb5r\xd2{n\x9b\n\x06\xd2e\xa0\xce\x02\xe0\xf9\xe7\x9f\x0f\xf7\xde{o\x989sf\x980aB\x18=zt\x182d\x88\xc0_\x83=\x8a\x00HwM\xb1_\x18[\x0c\xa4\xc7\x00\x01@\x00\x10\x005h\xae,\xbe\xe9-\xbe\xc6\xd4\x98\xb6\x82\x81\xba\x08\x80\xa5K\x97\x86\xfb\xef\xbf?\xcc\x9e=;L\x9c81|\xe0\x03\x1f\x08\xc3\x86\r\x13\xf6k\xba\x1f\x11\x00\xd6\xb3V\xacg\x9e\x13W\x18h\r\x03\x04\x00\x01@\x00\xd4\xb4\xe1\xb2(\xb6fQTWu\xad3\x03U\x14\x00\xcb\x97/\x0f\xbf\xfa\xd5\xaf\xc2\x9c9s\xc2i\xa7\x9d\x16\x0e:\xe8\xa0\xb0\xc5\x16[\x08\xfb\t\xed=\x04\x80u\xb3\xce\xeb\xa6c\xc7on\x0c\x10\x00\x04\x00\x01\x90P\x13\x96\xdb\x02\xe6|m\xda\x18X\x99\x81N\x0b\x80\x15+V\x84\x07\x1ex \\u\xd5Ua\xf2\xe4\xc9\xe1\xd0C\x0f\r\xc3\x87\x0f\x17\xf6\x13\xdfg\x08\x80\x95\xe7\xa1uI=0\x80\x81*3@\x00\x10\x00\x04@\xe2\x8dY\x95\x17 \xc7f\x83\xc4@s\x19\xe8\x94\x00\xf8\xf3\x9f\xff\x1c>\xf2\x91\x8f\x84\xad\xb7\xdeZ\xd8\xcfpO!\x00\x9a;\x8f\xad\x8b\xea\x89\x01\x0c\xb4\x92\x01\x02\x80\x00 \x002l\xd6Z\xb9\xa8xn\x9b\x16\x06:\xc7@\xa7\x04@\xfcW\x7f\xe3\xde\xb9q\xeft\xed\t\x80|\xc7\xbe\xd3\xecy}\xeca\xa0<\x03\x04\x00\x01@\x00$&\x00\xf6\xdcs\xcf\xf0\xcdo~3\xdcy\xe7\x9d\xe1\xd1G\x1f\r\x7f\xf8\xc3\x1f\xc2=\xf7\xdc\x13\xa6O\x9f\x1e\xf6\xdf\x7f\x7fMzb\xe3m\xe3+\xbf\xf1\xa5\\3\x02\x00\x0f\x9d\xe0\x9b\x00\xc0]\'\xb8\xf3\x9a\xb8\xc3@c\x0c\x10\x00\x04\x00\x01\x90H \xdcj\xab\xad\xba>+{\xd9\xb2e\xa1\xaf\xaf\x1bn\xb8\xa1\xeb\x8e\xdb\x16\xcd\xc6\x16MuS\xb7*3@\x00\xe0\xb3\x13|\x12\x00\xb8\xeb\x04w^\x13w\x18h\x8c\x01\x02\x80\x00 \x00\x12\x10\x00\xdbm\xb7]\xf8\xe9O\x7f\xdaW\xee_\xed\xef\x88\x80\xc6\x16M\x9b\x8d\xbaU\x99\x01\x02\x00\x9f\x9d\xe0\x93\x00\xc0]\'\xb8\xf3\x9a\xb8\xc3@c\x0c\x10\x00\x04\x00\x01\x90\x80\x00\x18H\xf3E\x044\xb6x\xdat\xd4\xad\x8a\x0c\x10\x00\xb8\xec\x04\x97\x03\xd9\x83V\xb3\xd3%~p\xc1\x05\x17x[[\x02=L\'\x98\xf5\x9a\xd6\xca\x9c\x19 \x00\x08\x00\x02\xa0\xe6\x9b\xe7)\xa7\x9cR\xa2]\xea\xfd\xa1D\x80\xcd0\xe7\xcd0\x95s\x9f6mZ\xef\x93\xbc\x85\x7f\xe3&\x80y\xaf\x1f\x04@\xde\xe3\x9f\xca\xfa\xe9<p\x9c\x0b\x03\x04\x00\x01@\x00\xd4\\\x00<\xf8\xe0\x83Mm\xeb\x89\x00\x1b`.\x1b`\x8a\xe7\xf9\x85/|\xa1\xa9\xebA\x7f\x9f\x8c\x00\xc8{\xdd \x00\xf2\x1e\xff\x14\xd7R\xe7\x84\xe9\x94\x19 \x00\x08\x00\x02\xa0\xc6\x02`\xf4\xe8\xd1\xfd\xed\xcfK?\x8e\x08\xb0\xf9\xa5\xbc\xf9\xa5zn\x04\x80y\xdb\t\xb6\t\x00\xdcu\x82;\xaf\x89;\x0c4\xc6\x00\x01@\x00\x10\x005\x16\x00\xff\xf6o\xffV:\xd8\x97\xfd\x05"\xa0\xb1\xc5\xd5\xa6\xa4n\x9d`\x80\x00\xc0]\'\xb8#\x00p\xd7\t\xee\xbc&\xee0\xd0\x18\x03\x04\x00\x01@\x00\xd4X\x00L\x9e<\xb9l\x9eo\xf8\xf1D@c\x8b\xac\xcdI\xdd\xda\xc9\x00\x01\x80\xb7v\xf2\xd6\xfdZ\x04\x00\xee\xbaY\xf0\'\x160P}\x06\x08\x00\x02\x80\x00\xa8\xb1\x008\xeb\xac\xb3\x1a\x0e\xf4\x8d\xfe"\x11P\xfd\x85\xdd\xe6\x9b\xef\x18\x11\x00\xf9\x8e}\'\xe7=\x01\x80\xbbN\xf2\xe7\xb5\xf1\x87\x81r\x0c\x10\x00\x04\x00\x01@\x004\xe4\x02\x88\x80r\x8b\xad\xcdI\xbd\xda\xc1\x00\x01\x80\xb3vp\xb6\xeak\x10\x00\xb8[\x95\t\xff\x8f\t\x0cT\x97\x01\x02\x80\x00 \x00\x08\x80\x86\x04@\xf7/\x11\x01\xd5]\xe0m\xbe\xf9\x8d\r\x01\x90\xdf\x98Wa\x9e\x13\x00\xb8\xab\x02\x87\x8e\x01\x87\x18\xe8\x1f\x03\x04\x00\x01@\x00\x10\x00\xddY~@\x7f\x12\x01\xfd[tmN\xea\xd4J\x06\x08\x00|\xb5\x92\xaf\xde\x9e\x9b\x00\xc0]ol\xf8960P=\x06\x08\x00\x02\x80\x00 \x00\x06\x14\xfcW\xfde"\xa0z\x0b\xbd\xcd7\x9f1!\x00\xf2\x19\xeb*\xcdk\x02\x00wU\xe2\xd1\xb1\xe0\x11\x03}3@\x00\x10\x00\x04\x00\x01\xb0j\x86o\xca\xff\x13\x01}/\xbe6\'\xf5i\x05\x03\x04\x00\xaeZ\xc1\xd5\x9a\x9e\x93\x00\xc0\xdd\x9a\x18\xf1\xf7\x18\xc1@u\x18 \x00\x08\x00\x02\x80\x00hJ\xe0\xef\xedI\x88\x80\xea,\xf86\xdf\xf4\xc7\x82\x00H\x7f\x8c\xab8\x8f\t\x00\xdcU\x91K\xc7\x84K\x0c\xf4\xcc\x00\x01@\x00\x10\x00\x04@o\xd9\xbd\xa9?\'\x02z^\x84mN\xea\xd2L\x06\x08\x00<5\x93\xa7\xfe>\x17\x01\x80\xbb\xfe\xb2\xe2qX\xc1@\xe7\x19 \x00\x08\x00\x02\x80\x00hj\xd0_\xd3\x93\x11\x01\x9d_\xf8m\xbe\xe9\x8e\x01\x01\x90\xee\xd8Vy\xde\x12\x00\xb8\xab2\x9f\x8e\r\x9f\x18X\x99\x01\x02\x80\x00 \x00\x08\x805e\xf6\x96\xfc=\x11\xb0\xf2blsR\x8ff0@\x00\xe0\xa8\x19\x1c\x95}\x0e\x02\x00we\x99\xf1x\xcc`\xa0s\x0c\x10\x00\x04\x00\x01@\x00\xb4$\xe0\xf7\xf7I\x89\x80\xcem\x006\xdf\xf4jO\x00\xa47\xa6u\x98\xa7\x04\x00\xee\xea\xc0\xa9c\xc4)\x06\xfe\x8f\x01\x02\x80\x00 \x00\x08\x80\xfef\xf5\x96>\x8e\x08\xb01\xdb\x98\x07\xce\x00\x010\xf0\x1a\xe2\xb0|\r\t\x80\xf25\xc3\x99\x9aa\x00\x03\x9db\x80\x00 \x00\x08\x00\x02\xa0\xa5\xc1\xbe\xec\x93\x13\x016\xc4Nm\x88)\xbc.\x01`\xfet\x82c\x02\x00w\x9d\xe0\xcek\xe2\x0e\x03\x8d1@\x00\x10\x00\x04\x00\x01P6\xa3\xb7\xe5\xf1D@c\x8b\xba\xcd0\xef\xba\x11\x00y\x8f\x7f\xa7\xe6?\x01\x80\xbbN\xb1\xe7u\xb1\x87\x81\xf2\x0c\x10\x00\x04\x00\x01@\x00\xb4%\xd07\xfa"D@\xf9\x85\xddf\x98o\xcd\x08\x80|\xc7\xbe\x93\xf3\x9e\x00\xc0]\'\xf9\xf3\xda\xf8\xc3@9\x06\x08\x00\x02\x80\x00 \x00\x1a\xcd\xe6m\xfd="\xa0\xdc\xe2n3\xcc\xb3^\x04@\x9e\xe3\xde\xe9\xf9N\x00\xe0\xae\xd3\x0cz}\x0cb\xa0\xff\x0c\x10\x00\x04\x00\x01@\x00\xb45\xc8\x0f\xf4\xc5\x88\x80\xfe/\xf06\xc3\xfcjE\x00\xe47\xe6U\x98\xe7\x04\x00\xee\xaa\xc0\xa1c\xc0!\x06\xfa\xc7\x00\x01@\x00\x10\x00\x04\xc0@3yG~\x9f\x08\xe8\xdf"o3\xcc\xabN\x04@^\xe3]\x95\xf9M\x00\xe0\xae*,:\x0e,b`\xcd\x0c\x10\x00\x04\x00\x01@\x00t$\xc07\xebE\x89\x805/\xf46\xc3|jD\x00\xe43\xd6U\x9a\xd7\x04\x00\xee\xaa\xc4\xa3c\xc1#\x06\xfaf\x80\x00 \x00\x08\x80\x1a\x0b\x80\xd3O?\xbdY9\xba\xf6\xcfC\x04\xf4\xbd\xd8\xdb\x0c\xf3\xa8\x0f\x01\x90\xc78Wm>\x13\x00\xb8\xab\x1a\x93\x8e\x07\x93\x18\xe8\x9d\x01\x02\x80\x00 \x00j,\x00>\xf3\x99\xcf\xd4>\xb87\xfb\x04\x88\x80\xde\x17|\x9ba\xfa\xb5!\x00\xd2\x1f\xe3*\xcec\x02\x00wU\xe4\xd21\xe1\x12\x03=3@\x00\x10\x00\x04@\x8d\x05\xc0^{\xed\xd5\xec\xfc\x9c\xcc\xf3\x11\x01=/\xfa6\xc3\xb4\xebB\x00\xa4=\xbeU\x9d\xbf\x04\x00\xee\xaa\xca\xa6\xe3\xc2&\x06Vg\x80\x00 \x00\x08\x80\x1a\x0b\x80\xb8\xa8\xdd\x7f\xff\xfd\xc9\x84\xf6V\x9c\x08\x11\xb0\xfa\xc2o3L\xb7&\x04@\xbac[\xe5yK\x00\xe0\xae\xca|:6|b`e\x06\x08\x00\x02\x80\x00\xa8\xb9\x008\xfe\xf8\xe3[\x91\x9b\x93{N"`\xe5\xc5\xdff\x98f=\x08\x804\xc7\xb5\xea\xf3\x95\x00\xc0]\xd5\x19u|\x18\xc5\xc0K\x0c\x10\x00\x04\x00\x01Ps\x01\x10\x17\xb4k\xae\xb9&\xb9\xc0\xde\xaa\x13"\x02^\xda\x00l\x86\xe9\xd5\x82\x00HoL\xeb0O\t\x00\xdc\xd5\x81S\xc7\x88S\x0c\xfc\x1f\x03\x04\x00\x01@\x00$ \x00\xb6\xd8b\x8bp\xcb-\xb7\xb4*3\'\xf9\xbcD\x80F \xc5F\x80\x00\xc0u\'\xb8&\x00p\xd7\t\xee\xbc&\xee0\xd0\x18\x03\x04\x00\x01@\x00$ \x00\xe2\x028x\xf0\xe0p\xce9\xe7\x84\xc5\x8b\x17\'\x19\xd8[uRD@c\x9b\x87M\xb7\x9au#\x00\xaa9.\xa9\xcf\x17\x02\x00w\xa93\xee\xfc0\x9e\x12\x03\x04\x00\x01@\x00$"\x00\xba\x17\xa6\x1dw\xdc1\\t\xd1E\xe1\xb9\xe7\x9ekUfN\xf2y\x89\x00\x9b{\xf7\x1c\xaa\xf3\x9f\x04\x00\x8e;\xc1/\x01\x80\xbbNp\xe75q\x87\x81\xc6\x18 \x00\x08\x00\x02 1\x01\xd0\xbd\x18F\x110c\xc6\x0c"\xa0\xa4\xae \x02\x1a\xdbL\xba\xb9\xf3gg\xebG\x00t\xb6\xfe\xb9\xf2O\x00\xe0.W\xf6\x9d7\xf6\xeb\xc8\x00\x01@\x00\x10\x00\x89\n\x80\xee\x05\xa9[\x04\xfc\xe5/\x7f)\x19\x85\xf3~8\x11`S\xef\x9eCu\xfa\x93\x00\xc0m\'x%\x00p\xd7\t\xee\xbc&\xee0\xd0\x18\x03\x04\x00\x01@\x00$.\x00\xba\x17\xc7(\x02.\xbe\xf8\xe2@\x04\x94\x13\x1bD@c\x9bK7w\xfelo\xfd\x08\x80\xf6\xd6\x1b\xdf\xffWo\x02\x00w\xe6\x02\x060P\x1f\x06\x08\x00\x02\x80\x00\xc8D\x00t/\xcc;\xed\xb4S\xb8\xe4\x92K\xc2\xf3\xcf?_.\tg\xfeh"\xa0>\x1b[7\xeb9\xfeI\x00\xe0\xb4\x13\xdc\x13\x00\xb8\xeb\x04w^\x13w\x18h\x8c\x01\x02\x80\x00 \x002\x13\x00\xdd\x8b\xe5\xce;\xef\x1c\xbe\xfd\xedo\x13\x01%\xc5\x06\x11\xd0\xd8f\xd3\xcd\x9d?[[?\x02\xa0\xb5\xf5\xc5o\xcf\xf5%\x00z\xae\x0b^\xd4\x05\x03\x18\xa8"\x03\x04\x00\x01@\x00d*\x00\xba\x17\xa4]v\xd9%\\z\xe9\xa5D\x00\x11\x10\xba\x99\xf0g}\x1b\x16\x02\xa0\xbecW\xe7yG\x00\xe0\xae\xce\xfc:v\xfc\xe6\xc6\x00\x01@\x00\x10\x00\x99\x0b\x80\xeeE/\x8a\x80\x993g\x86%K\x96\x94\x8c\xc2y?\xdc\x15\x01\x1a\x87\xee9T\x85?\t\x00<v\x82C\x02\x00w\x9d\xe0\xcek\xe2\x0e\x03\x8d1@\x00\x10\x00\x04\x00\x01\xb0\xd2\xbf\xfc\xee\xba\xeb\xaeD@\x03N\x83\x08hl\x13\xb2y7\xb7n\x04@s\xeb\x89\xcf\xfe\xd5\x93\x00\xe8_\x9d\xf0\xa4N\x18\xc0@\x15\x18 \x00\x08\x00\x02\x80\x00XI\x00t/L#G\x8e$\x02\x88\x80\x1e\xd9\xe8f\xc4\x9f\xd5kd\x08\x80\xea\x8dI\x0e\xf3\x84\x00\xc0]\x0e\x9c;G\x9c\xa7\xc2\x00\x01@\x00\x10\x00\x04@\x9f!/\x8a\x80\xef|\xe7;a\xe9\xd2\xa5\r\xc4\xe1|\x7f\xc5\x15\x01\x1a\x85N4\n\x04\x00\xee:\xc1\x1d\x01\x80\xbbNp\xe75q\x87\x81\xc6\x18 \x00\x08\x00\x02\x80\x00\xe8S\x00t/\xaeQ\x04\xcc\x9a5\x8b\x08(\xe94\x88\x80\xc66\xa7n\xee\xfcY\xae~\x04@\xb9z\xe1\xab9\xf5"\x00\x9aSG<\xaa#\x060\xd0\x0e\x06\x08\x00\x02\x80\x00 \x00\xfa%\x00\xba\x17\xa4\xddv\xdb-\\~\xf9\xe5D\x00\x11P\x8a\x9bn~\xfc\xd9\xda\xe6\x86\x00hm}\xf1\xdbs}\t\x80\x9e\xeb\x82\x17u\xc1\x00\x06\xaa\xc8\x00\x01@\x00\x10\x00\x04@CA.\x8a\x80\xd9\xb3g\x13\x01D@C\xfcTqCL\xe1\x98\x08\x00\xcdf\'8&\x00p\xd7\t\xee\xbc&\xee0\xd0\x18\x03\x04\x00\x01@\x00\x10\x00\x03\np\xbb\xef\xbe;\x11PR\x02\xc4\x87{k@c\x9b\x96\xcd\xbe\xef\xba\x11\x00}\xd7\x07?\xad\xa9\x0f\x01\xd0\x9a\xba\xe2U]1\x80\x81V0@\x00\x10\x00\x04\x00\x010 \x01\xd0\xbd0\xed\xb1\xc7\x1e\xe1\xbb\xdf\xfd\xae+\x02J\xca\x00"\xc0\xe6\xde=\x87\x9a\xf1\'\x01\x80\xa7fpT\xf69\x08\x00\xdc\x95e\xc6\xe31\x83\x81\xce1@\x00\x10\x00\x04\x00\x01\xd0\x14\x01\xd0\xbd\x90G\x11p\xc5\x15W\x84e\xcb\x96\x95\x8c\xc2y?\x9c\x08\xe8\xdcF\xd8\xcdn\n\x7f\x12\x008\xea\x04\xc7\x04\x00\xee:\xc1\x9d\xd7\xc4\x1d\x06\x1ac\x80\x00 \x00\x08\x00\x02\xa0\xa9\x02\xa0{1\xdes\xcf=\xc3\xf7\xbe\xf7="\xa0\xa4\xd7 \x02\x1a\xdb\xcc\xba\xb9\xcb\xfdO\x02\x00?\x9d\x98\x03\x04\x00\xee:\xc1\x9d\xd7\xc4\x1d\x06\x1ac\x80\x00 \x00\x08\x00\x02\xa0%\x02\xa0{Q\x1e5jT\x983g\x0e\x11@\x04\xb4\x94\xb3n\xder\xff\x93\x00h\xac\x19\xca\x9d\x9b\x81\x9e?\x01\x80\xbb\x812\xe4\xf71\x84\x81\xf61@\x00\x10\x00\x04\x00\x01\xd0\x96`F\x04\x944\x00/>\xdc\x15\x01\xed\xdb\x10Sh>\x08\x00\xbct\x82c\x02\x00w\x9d\xe0\xcek\xe2\x0e\x03\x8d1@\x00\x10\x00\x04\x00\x01\xd0\x16\x01\xd0\xbdHG\x11\xf0\xfd\xef\x7f?,_\xbe\xbc\xb1D\x9c\xe9o\x11\x01\x8dmr\xdd\xdc\xe5\xf2\'\x01\x80\x93N\xb0N\x00\xe0\xae\x13\xdcyM\xdca\xa01\x06\x08\x00\x02\x80\x00 \x00\xda*\x00\xba\x17\xeb\xbd\xf6\xda+\\y\xe5\x95D@I\xa1A\x044\xb6\xd9us\x97\xfa\x9f\x04\x00>:\xc18\x01\x80\xbbNp\xe75q\x87\x81\xc6\x18 \x00\x08\x00\x02\x80\x00\xe8\x88\x00\xe8^\xb4\xf7\xde{\xef\xf0\x83\x1f\xfc\x80\x08 \x02:\xcaa7\x8fu\xff\x93\x00h\xac\x19\xaa\xfb\xb8w\xfa\xf8\t\x00\xdcu\x9aA\xaf\x8fA\x0c\xf4\x9f\x01\x02\x80\x00 \x00\x08\x80J\x04\xaf}\xf6\xd9\'\\u\xd5UD\x00\x11P\t\x1e\xeb\xdaH\x10\x00\xfdo\x80\xea:\xc6U<n\x02\x00wU\xe4\xd21\xe1\x12\x03=3@\x00\x10\x00\x04\x00\x01P\xa9\xc0\xb5\xef\xbe\xfb\x86\xb9s\xe7\x86\x17^x\xa1d\x14\xce\xfb\xe1\xde\x1a\xd0\xf3&\x97\xdb\xe6O\x00\xe0\xa0\x13\xcc\x13\x00\xb8\xeb\x04w^\x13w\x18h\x8c\x01\x02\x80\x00 \x00\x08\x80J\t\x80\xee\xc5\x9c\x08hLh\x10\x01\x8dm\x86\xdd\xdc\xd5\xfdO\x02 \xef\xf1\xef\x14\xbf\x04\x00\xee:\xc5\x9e\xd7\xc5\x1e\x06\xca3@\x00\x10\x00\x04\x00\x01PI\x01\xd0\xbd\xa0G\x11p\xf5\xd5W\xbb"\xa0\xa4\x0f \x02\xcao\x88\xdd\xcc\xd5\xf9O\x02 \xcfq\xef4\xb3\x04\x00\xee:\xcd\xa0\xd7\xc7 \x06\xfa\xcf\x00\x01@\x00\x10\x00\x04@\xa5\x05@\xf7\x82\xbe\xdf~\xfb\x85k\xae\xb9\x86\x08h@\x04\x8c\x1e=\xba\x16c\xdc=\xd6\xfe\xec\xff&\xbej\xad\x08\x80\xc6k\xb7j-\xfd\x7f\xffkI\x00\xf4\xbfV\xb8R+\x0c`\xa0\xd3\x0c\x10\x00\x04\x00\x01@\x00\xd4*\x1c\xee\xbf\xff\xfe\xe1\xdak\xaf%\x02J\x88\x80e\xcb\x96\x85\xf3\xce;\xafV\xe3\xdc\xe9\xcd\xb1\xae\xafO\x00h,;\xc1.\x01\x80\xbbNp\xe75q\x87\x81\xc6\x18 \x00\x08\x00\x02\x80\x00\xa8\xd5\x03\xa6\xa3\x00\x00,\xeeIDATe0<\xe0\x80\x03\xc2u\xd7]\x17V\xacXQ"\n\xe7\xfd\xd0\xd9\xb3g\xd7r\xacm\xf0\xfd\xdf\xe0\t\x80\xfe\xd7\nW\xcd\xab\x15\x01\xd0\xbcZ\xe2R-1\x80\x81V3@\x00\x10\x00\x04\x00\x01P\xebP\x18/o\x9f7o\x1e\x11\xd0O\xb71q\xe2\xc4Z\x8fw\xab7\xc5\xba??\x01\xa0q\xec\x04\xc3\x04\x00\xee:\xc1\x9d\xd7\xc4\x1d\x06\x1ac\x80\x00 \x00\x08\x00\x02 \x89@H\x04\xf4\xcf\x00<\xf1\xc4\x13a\xc4\x88\x11I\x8c\xb9\x8d\x7f\xf5\x8d\x9f\x00X\xbd&8i}M\x08\x80\xd6\xd7\x18\xc7j\x8c\x01\x0c4\x8b\x01\x02\x80\x00 \x00\x08\x80\xa4\xc2\xe0\x81\x07\x1e\x18\xe6\xcf\x9f\xef\x8a\x80>|\xc0\x84\t\x13\x92\x1a\xf3fm\x88)<\x0f\x01\xa0A\xec\x04\xc7\x04\x00\xee:\xc1\x9d\xd7\xc4\x1d\x06\x1ac\x80\x00 \x00\x08\x00\x02 \xc90\x18E\xc0\xf5\xd7_O\x04\xf4 \x02\xe6\xce\x9d\x9b\xe4\x98k\x04\xde\x13\x08\x80\xc6\x9a!\xec\x0c\xacn\x04\xc0\xc0\xea\x87?\xf5\xc3\x00\x06\xda\xc9\x00\x01@\x00\x10\x00\x04@\xd2a\xf0\xfd\xef\x7f\x7fX\xb0`\x01\x11\xf02\x11p\xef\xbd\xf7&=\xe6\xed\xdcD\xab\xf6Z\x04\x80&\xb2\x13L\x12\x00\xb8\xeb\x04w^\x13w\x18h\x8c\x01\x02\x80\x00 \x00\x08\x80\xe4\xc3\xe0\xae\xbb\xee\x1a:\xd5\xa0\xbe,wW\xe6?\x7f\xf1\x8b_$?\xe6\xb96\x05\x04@c\xcdP\xae\xbc4\xeb\xbc;\xb5\xbe^p\xc1\x05\xd62=\x0c\x060\x80\x81\x92\x0c\x10\x00\x04\x00\x01Pr\xd24\xaba\xf2<\xado\xd4G\x8e\x1c\x19.\xbb\xec\xb2\xb0d\xc9\x92\xca\x84\xef*\x1c\xc8\x8f~\xf4#\x9be\xa2\xf3\x9e\x00h\xfd\xbab\xed^\xbd\xc6\x04\xc0\xea5\xc1\x89\x9a`\x00\x03Ue\x80\x00 \x00\x08\x80D\x83@U\x17\x9dv\x1c\xd7n\xbb\xed\x16\xbe\xf3\x9d\xef\x84\xa5K\x97V!oW\xee\x18\xa6N\x9dJ\x00$:\xef\t\x00\rg;\xd6\xd8U_\x83\x00\xc0\xdd\xaaL\xf8\x7fL`\xa0\xba\x0c\x10\x00\x04\x00\x01\x90h\x10\xc8q\xe1\xdd}\xf7\xdd\xc3\xe5\x97_.\xf8\xafA9\xc4\x1b$\xe6\xc8G\x0e\xe7L\x00T\xb7\xe1J\x99?\x02\x00w)\xf3\xed\xdc\xf0\x9d\x1a\x03\x04\x00\x01@\x00\x10\x00\xb5\x0f\x83{\xec\xb1G\x98={\xb6\xe0\xbf\x86\xe0\x1f\xff:\xde\x101\xb5\x8d\xcc\xf9\xbc\xd4\x9c\x11\x00/\xd5\x02\x17\xed\xab\x05\x01\xd0\xbeZ\xe3Z\xad1\x80\x81\x812@\x00\x10\x00\x04\x00\x01P\xdb@\xb8\xe7\x9e{\x86+\xae\xb8B\xf0\xefG\xf0\x8f\x0f\xf9\xd3\x9f\xfe\x14v\xdey\xe7\xda\x8e\xf7@7\xbc\x1c~\x9f\x00\xd0\x18v\x82s\x02\x00w\x9d\xe0\xcek\xe2\x0e\x03\x8d1@\x00\x10\x00\x04\x00\x01P\xbb@8j\xd4\xa8\xf0\xbd\xef}/,[\xb6\xac\x9f\xd1\xd7\xc3\x1e}\xf4\xd1p\xc0\x01\x07\xd4n\xacm\xee\xe56w\x02\xa0\\\xbd\xf0\xd5\x9cz\x11\x00\xcd\xa9#\x1e\xd5\x11\x03\x18h\x07\x03\x04\x00\x01@\x00\x10\x00\xb5\t\x85{\xed\xb5W\x983g\x8e\xe0_\xc2g\xbc\xf0\xc2\x0b]\xb2d\x87\x1dv\xa8\xcd8\xb7c\xf3K\xf55\x08\x00\xcdc\'\xd8&\x00p\xd7\t\xee\xbc&\xee0\xd0\x18\x03\x04\x00\x01@\x00\x10\x00\x95\x0f\x86{\xef\xbdw\xf8\xfe\xf7\xbf\x1f\x96/_^"\xfa\xe6\xfd\xd0\x18\xfc\xaf\xbe\xfa\xea\xb0\xef\xbe\xfbV~|m\xe0\x8dm\xe0=\xd5\x8d\x00h^-{\xaa\xaf\x9f\xf5\\_\x02\xa0\xe7\xba\xe0E]0\x80\x81*2@\x00\x10\x00\x04\x00\x01P\xd9\x80\xb8\xcf>\xfb\x84\x1f\xfc\xe0\x07\x82\x7f\t\x97!\xf8\xe7\xddl\x10\x00y\x8f\x7f\xa7\x1aM\x02\x00w\x9db\xcf\xebb\x0f\x03\xe5\x19 \x00\x08\x00\x02\x80\x00\xa8\x9c\x00\x88\xffj}\xd5UW\t\xfe\x82\x7f\xe5\xd8\xacz\xa3A\x00\x94o\x84\xaa>\xa6u8>\x02\x00wu\xe0\xd41\xe2\x14\x03\xff\xc7\x00\x01@\x00\x10\x00\x04@eB\xd6~\xfb\xed\x17\xe6\xce\x9d\x1b\xe2\xbfb\xfb\xea_\x05\xfc\x8b\xbf\x86\xe6\xe5\r\r\x01\x80\x87\x97\xf3\xd0\xdb\x7fo\xb5\xd5V!\xdeL\xf5C\x1f\xfaP8\xea\xa8\xa3\xc2I\'\x9d\x14\xbe\xf4\xa5/\x85\xb3\xcf>;\x9cu\xd6Y\xe1\xcc3\xcf\x0c\x93\'O\x0e\xa7\x9f~z\xf8\xecg?\xdb\xf5\x98\x0f|\xe0\x03a\xe4\xc8\x91a\xd8\xb0a\xab\xed\x19\x04\x00\xeezc\xcd\xcf\xb1\x81\x81\xea1@\x00\x10\x00\x04\x00\x01\xb0Z3\xd7\xee\xc5z\xff\xfd\xf7\x0f\xd7\\s\x8d\xe0\xdf\xbf\xcc\xdf\xf5(\xc1\xbfz\x1bj\xbb\xe7MO\xafG\x00\xe0\xe2\xe5\\\x8c\x181"\x8c\x1d;\xb6+\xcc\xcf\x9e=;\xdc}\xf7\xdd\xe1\xb1\xc7\x1e+\xb1\xd2\xac\xfe\xd0x/\x96\x07\x1f|0\xdcp\xc3\ra\xea\xd4\xa9\xe1\xc4\x13O\xecz\xde\xd5\x1f\xd9\xfa\x9f\\p\xc1\x05\x1d\xdf\xbf^^o\xffm\xfea\x00\x03u`\x80\x00 \x00\x08\x00\x02\xa0c\r\xd4\xe8\xd1\xa3\xc3u\xd7]\'\xf8\x97\xe8\x93\x05\x7f\xcdE_\xcd\x05\x01\x907\x1f\xdbo\xbf}8\xe1\x84\x13\xc2\xcc\x993\xc3/\x7f\xf9\xcb\xe4\xd7V\x02 o\xde\xfbZ\x0b\xfd\x1d60\xd0;\x03\x04\x00\x01@\x00\x10\x00m\x17\x00\x07\x1ex`\x987o^X\xb1bE\x89\xe8\x9b\xf7C\x05\xff\xde72\x9b\xfcK\xb5!\x00^\xaaE.\\\xec\xb1\xc7\x1e\xe1\x8c3\xce\x08?\xfe\xf1\x8f\xb3\xfb\x88T\x02 ?\xdes\x99\xd7\xce\x13\xdb\xadd\x80\x00 \x00\x08\x00\x02\xa0m\x02\xe0\xfd\xef\x7f\x7f\x98?\x7f\xbe\xe0_\xc2e\x08\xfe\x9a\x802M\x00\x01\x90\x07/\xef{\xdf\xfb\xc2\x97\xbf\xfc\xe5\xf0\x8b_\xfc\xa2\xc4j\x92\xdeC\t\x80<x/\xb3\x06z,&0\xb0f\x06\x08\x00\x02\x80\x00 \x00Z.\x00\xfe\xe5_\xfe%,X\xb0@\xf0/\xd1\x7f\x0b\xfek\xde\xc0l\xf2\xab\xd7\x88\x00X\xbd&)q2n\xdc\xb8\xae\xab\xa7\x96.]Zb5I\xf7\xa1\x04@\xda\xbc\xa74w\x9d\x0bV\xab\xc4\x00\x01@\x00\x10\x00\x04@\xcb\x04\xc0\x07?\xf8\xc1\xae\x1bE\xa5\xdb~6\xff\xcc\x04\x7fM\xc2@\x9a\x04\x02 =~\xe2]\xf7\'N\x9c\x18~\xf7\xbb\xdf5\x7f\xc1\xa9\xf93\x12\x00\xe9\xf1>\x90\xf5\xcf\xef\xe2\x01\x03\xfdc\x80\x00 \x00\x08\x00\x02\xa0\xe9\x02`\xcc\x981\xe1\xc6\x1bo\xacyk\xd9\xde\xc3\x17\xfc\xfb\xb7i\xd9\xdc\xfb\xae\x13\x01\xd0w}\xea\xc4\xcf\x96[n\xd9\xf5\xd1|\x8f>\xfah{\x17\xa3\x1a\xbd\x1a\x01\x90\x0e\xefu\x9a\x9b\x8e\x15wug\x80\x00 \x00\x08\x00\x02\xa0i\x02 ~\xa6\xf4\xcd7\xdf\\\xa3\xf6\xb1\xf3\x87*\xf8k$\x9a\xd9H\x10\x00\xf5\xe7i\xf0\xe0\xc1a\xd2\xa4I\x03\xfe\xb8\xbe\xce\xafn\xad?\x02\x02\xa0\xfe\xbc7s\xfd\xf3\\x\xc0@\xff\x18 \x00\x08\x00\x02\x80\x00\x18\xb0\x008\xe8\xa0\x83\xc2-\xb7\xdc\xd2\xfan/\xa1W\x10\xfc\xfb\xb7I\xd9\xcc\xcb\xd5\x89\x00(W\xaf\xaa\xf1u\xd8a\x87\x85\xfb\xef\xbf?\xa1\x95\xae\xb5\xa7B\x00\xd4\x9b\xf7\xaa\xcd?\xc7\x83\xa7\\\x18 \x00\x08\x00\x02\x80\x00hX\x00\x1cr\xc8!\xe1\xd6[omm\x87\x97\xd8\xb3\x0b\xfe\x1a\x8cV6\x18\x04@=\xf9\xday\xe7\x9d\xc3\xdc\xb9s\x13[\xedZ\x7f:\x04@=yo\xe5\x1a\xe8\xb91\x81\x8153@\x00\x10\x00\x04\x00\x01PZ\x00\xfc\xeb\xbf\xfek\xf8\xe1\x0f\x7f\xd8\xfa\xee.\xa1W\x10\xfc\xd7\xbc!\xd9\xb4\x07^#\x02`\xe05l7\x87\x9f\xfc\xe4\'\xc3\x93O>\x99\xd0j\xd7\xbeS!\x00\xea\xc7{\xbb\xe7\x97\xd7\xc3\x08\x06Vg\x80\x00 \x00\x08\x00\x02\xa0\xdf\x02\xe0\xd0C\x0f\r\xb7\xddv[\xfb\xba\xbb\x04^I\xf0_}\xe3\xb1\x19\xb7\xae&\x04@\xebj\xdbln\xb7\xd9f\x9bp\xe5\x95W&\xb0\xcau\xee\x14\x08\x80\xfa\xf0\xde\xec\xf9\xe3\xf9\x8c=\x06\x1ag\x80\x00 \x00\x08\x00\x02`\x8d\x02\xe0\xc3\x1f\xfepX\xb4hQ\xe7\xba\xbc\x1a\xbe\xb2\xe0\xdf\xf8\xc6dSo\xbcv\x04@\xe3\xb5k\'w\xf1\x93R\x1ey\xe4\x91\x1a\xael\xd5:d\x02\xa0\x1e\xbc\xb7sny-L``\xcd\x0c\x10\x00\x04\x00\x01@\x00\xf4*\x00\xc6\x8e\x1d\x1b~\xf2\x93\x9fT\xab\xe3\xab\xf8\xd1\x08\xfek\xdexl\xce\xad\xab\x11\x01\xd0\xba\xda6\x8b\xdb\x13O<1<\xff\xfc\xf3\x15_\xc9\xeaqx\x04@\xf5yo\xd6\xbc\xf1<\xc6\x1a\x03\xcdc\x80\x00 \x00\x08\x00\x02`5\x01\xf0\x91\x8f|$\xdc~\xfb\xed\xf5\xe8\x00+r\x94\x82\x7f\xf36&\x9b|\xe3\xb5$\x00\x1a\xaf];\xb8\x8b\x81\xd5W\xf3*@\x00T\x9b\xf7v\xcc)\xaf\x81\x01\x0c\x94g\x80\x00 \x00\x08\x00\x02\xe0\xaf\x02\xe0\x88#\x8e\x08w\xdcqG\xf3\xba\xb3\x0c\x9eI\xf0/\xbf\xf1\xd8\xac[W3\x02\xa0u\xb5\x1d\x08\xb7\xc3\x86\r\x0b\xf3\xe7\xcf\xcf`El\xef)\x12\x00\xd5\xe4} s\xc5\xef\x1aS\x0c\xb4\x9e\x01\x02\x80\x00 \x00\x08\x800n\xdc\xb8p\xd7]w\xb5\xb7s\xab\xf9\xab\t\xfe\xad\xdf\xa04\x01\xe5kL\x00\x94\xafY\xab9\xdbb\x8b-\xc2\xc2\x85\x0bk\xbe\xe2U\xf3\xf0\t\x80\xea\xf1\xde\xea\xf9\xe4\xf9\x8d9\x06\x06\xce\x00\x01@\x00\x10\x00\x19\x0b\x80\x8f~\xf4\xa3\xe1?\xff\xf3?\xab\xd9\xd9U\xf4\xa8\x04\xff\x81o<6\xef\xd6\xd5\x90\x00h]m\x1b\xe1v\xab\xad\xb6\xf2\x91\xa9-\\\xcb\t\x80j\xf1\xde\xc8\x1c\xf1;\xc6\x10\x03\xedg\x80\x00 \x00\x08\x80\x0c\x05\xc0QG\x1d\x15\xee\xb9\xe7\x9e\x16\xb6e\xe9=\xb5\xe0\xdf\xfe\rJSP\xbe\xe6\x04@\xf9\x9a\xb5\x8a\xb3x\xd9\xbfOOi\xed^@\x00T\x87\xf7V\xcd#\xcfk\x8c1\xd0|\x06\x08\x00\x02\x80\x00\xc8H\x00\x1c}\xf4\xd1\xe1\xa7?\xfdik;\xb2\xc4\x9e]\xf0o\xfe\xc6c3o]M\t\x80\xd6\xd5\xb6,\xb7W^yeb\xaba\xf5N\x87\x00\xa8\x0e\xefe\xe7\x87\xc7\x1b;\x0ct\x8e\x01\x02\x80\x00 \x002\x10\x00\x1f\xfb\xd8\xc7\xc2\xcf~\xf6\xb3\xeauo\x15>"\xc1\xbfs\x1b\x93\xa6\xa0\xf1\xda\x13\x00\x8d\xd7\xae\x99\xdc\xfd\xfb\xbf\xff{\x85W\xb7t\x0e\x8d\x00\xa8\x06\xef\xcd\x9c;\x9e\xcb\x98b\xa0\xf5\x0c\x10\x00\x04\x00\x01\x90\xb0\x008\xf6\xd8c\xc3}\xf7\xdd\x97N\xb7\xd7\x863\x11\xfc[\xbf\xf1\xd8\xdc[Wc\x02\xa0u\xb5\xed/\xb7\'\x9exb\x1bV*/\x11+@\x00t\x9e\xf7\xfe\xce\x0b\x8f3V\x18\xa8\x0e\x03\x04\x00\x01@\x00$(\x00>\xfe\xf1\x8f\x87\x9f\xff\xfc\xe7:\xc4\x12\x15\x10\xfc\xab\xb31i\x12\x1a\x1f\x0b\x02\xa0\xf1\xda5\x83\xbb\xbd\xf6\xda+,^\xbc\xb8\xc4\xca\xe3\xa1\x03\xa9\x00\x01\xd0Y\xde\x9b1g<\x871\xc4@\xfb\x19 \x00\x08\x00\x02 !\x010v\xecX\xc1\xbfd7)\xf8\xb7\x7f\xe3\xb1\xd9\xb7\xae\xe6\x04@\xebj\xbb&n\x87\x0e\x1d\xea\xadV%\xd7\xdf\x81>\x9c\x00\xe8\x1c\xefk\x9a\x0f\xfe\xde\xd8`\xa0\xba\x0c\x10\x00\x04\x00\x01\x90\x88\x00\x982eJX\xb1b\xc5@\xfb\xa9l~_\xf0\xaf\xee\xc6\xa4ih|l\x08\x80\xc6k7P\xeeb\x18\xf5\xd5\xde\n\x10\x00\x9d\xe3}\xa0\xf3\xc5\xef\x1b;\x0ct\x8e\x01\x02\x80\x00 \x00\x12\x10\x00\xe7\x9csN{\xbb\xae\x1a\xbf\x9a\xe0\xdf\xb9\r\xc7f\xdf\xfa\xda\x13\x00\xad\xafqO\x1c\x7f\xe8C\x1f\nqm\xf1\xd5\xde\n\x10\x00\x9d\xe1\xbd\xa79\xe0g\xc6\x02\x03\xf5a\x80\x00 \x00\x08\x80\x9a\x0b\x801c\xc6\x84\xe5\xcb\x97\xb7\xb7\xeb\xaa\xe1\xab\t\xfe\xf5\xd9\x984\x11\x8d\x8f\x15\x01\xd0x\xed\x1a\xe5n\xf0\xe0\xc1n\xb6\xda\xa1=\x81\x00h?\xef\x8d\xce\x13\xbfg\xac0P\x1d\x06\x08\x00\x02\x80\x00\xa8\xb9\x00\xb8\xe5\x96[:\xd4z\xd5\xe3e\x05\xff\xeal86\xff\xd6\x8f\x05\x01\xd0\xfa\x1a\xaf\xca\xf1\x84\t\x13\xea\xb1\x18&x\x94\xd3\xa7O\x0f\xab\x8e\x87\xffo\xff\x1cPs5\xc7@\xbd\x18 \x00\x08\x00\x02\xa0\xc6\x02`\xc7\x1dwt\xd9i/M\xad\xe0_\xaf\xcdH\xf3\xd0\x9c\xf1"\x00\x9aS\xc7\xfe\xf2\xb8\xc5\x16[\x84G\x1f}\xb4\x97U\xc8\x8f[]\x81+\xae\xb8\x82\x00\xa8q\x0f\xd3\xdfy\xe6q\xed]\xd7\xd4;\xfdz\x13\x00\x04\x00\x01P\xe3\xcd3~\xdc\x9f\xaf\x95+\xd0\x1d\xfc\xf7\xdbo?\x8da\x8d\xd9\xd6\x804\xd6\x80\x10\x00\x8d\xd5\xadQ\xde\xbe\xf8\xc5/\xae\xbc\x00\xf9\xbf\xb6V`\xfe\xfc\xf9\xd6y\xeb<\x060\x80\x81\x92\x0c\x10\x00\x04\x00\x01Pr\xd24\xda(\xb6\xe2\xf7N;\xed\xb4\xb66[U~1\xc1\xbf\xbd\xc1\xa7\x15<{\xce\x81\x8f!\x010\xf0\x1a\xf6\x97\xc3\xf8\xde\xff\x87\x1ez\xa8\xca\xcbb\xf2\xc7\xf6\xc3\x1f\xfeP\xe3_\xe3\x1e\xa6\xbfs\xcd\xe3\xda\xb7\xae\xa9u\x1e\xb5&\x00\x08\x00\x02\xa0\xc6\x9b\xe7Yg\x9d\x95|\x83\xb7\xa6\x13\x14\xfc\xf3\xd8\xacRmJb\x88\xdcu\xd7]\xc3\x81\x07\x1e\x18\x0e?\xfc\xf0\xf0\xc9O~2L\x9c81\xc4\xb9\x1d\xbf\xcf<\xf3\xcc0y\xf2\xe4p\xfa\xe9\xa7\x87SN9%\x1c{\xec\xb1\xe1\x90C\x0e\t\xa3F\x8d\n#F\x8cX-\xfc\x10\x00\xed\x9b\x0fq,|u\xb6\x02\xb7\xdf~\xfbjs \xd5\xb5\xc2y\xb5on\xab\xb5Z\xa7\xce\x00\x01@\x00\x10\x00\x04@g;\xb8\x06_]\xf0\xb7A\xd7i\x83\x1e2dH\x18=zt\x887\x8c\xbb\xf0\xc2\x0b\xc3\xad\xb7\xde\x1a~\xf7\xbb\xdf\x85\xa5K\x9768\x03\xfe\xef\xd7\x1e{\xec\xb1\xb0h\xd1\xa2\xf0\xedo\x7f;L\x9a4)L\x9b6m@\xcf\xd7\xe8/?\xf0\xc0\x03\xd9\x05\xb1\x85\x0b\x176Z.\xbf\xd7\xa4\n\xdcu\xd7]\xd9qW\xa7u\xcf\xb1\xda\xa71PM\x06\x08\x00\x02\x80\x00 \x00\x9a\xd4\x8a\xb5\xe7i\x04\xffjn&6\xf9\x95\xc7e\xe8\xd0\xa1a\xec\xd8\xb1\xe1\x1b\xdf\xf8F\xf8\xc9O~\x12\x16/^\xdc\x9e\t\xd2\xa1W\xc9M\x00\xec\xb0\xc3\x0ea\xd9\xb2e\x1d\xaa\xb6\x97\xed\xae@\x9c_\xd6\x9e\x95\xd7\x1e\xf5P\x0f\x0c``M\x0c\x10\x00\x04\x00\x01@\x00t\xf7R\x95\xfeS\xf0\xb7\xa1\xadiC\xeb\xf4\xdf\xc7K\xf2O8\xe1\x84p\xd5UW\x85\xa7\x9ez\xaa\xd2\xf3\xa9\xd9\x07\x97\x9b\x008\xf5\xd4S\x9b]B\xcf\xd7@\x05\xbe\xf2\x95\xaf\x10\x005\xeea:\xbdf{}}E\xae\x0c\x10\x00\x04\x00\x01P\xe3\xcd3\x87{\x00\x08\xfe6\xe8*o\xd0\xc3\x86\r\x0b\xc7\x1f\x7f|\x88\x97\x83/Y\xb2\xa4\x81\x08\x93\xc6\xaf\xe4&\x00\x16,X\x90\xc6\xc0\xd5\xfc,\xe2[j\xaa\xbc>86\xfb\x17\x060PE\x06\x08\x00\x02\x80\x00 \x00*\xd9\x02v\x07\xff}\xf7\xddW\x83WcF\xab\xb8\xf15\xe3\x98\xf6\xdak\xaf0}\xfa\xf4\xf0\xf8\xe3\x8fWr\xfe\xb4\xfb\xa0r\x12\x00\xf1\xc6\x8dO?\xfdt\xbbK\xec\xf5z\xa8\xc0G?\xfaQ\xfb\x83\xfd\x01\x03\x18\xc0@I\x06\x08\x00\x02\x80\x00(9i\x9a\x11\x1e\x9a\xf5\x1c)^\x01\xd0\x1d\xfc\xf7\xdbo?\x0bz\x8d\xd9l\x16\xe3U{\x9e\xc3\x0e;,\xc4\x8f\x1e\xf3\xb5r\x05r\x12\x00\xf1f\x8e\xbe\xaaQ\x81=\xf6\xd8\xc3>a\x9f\xc0\x00\x060P\x92\x01\x02\x80\x00 \x00JN\x9a*\x05\x92\x94\x04\x80\xe0\xef2\xb9*\xcd\xadU\x8f\xe5\xc8#\x8f\x0cw\xdcqG5RO\x05\x8f"\'\x01\xf0\xb9\xcf}\xae\x82#\x90\xdf!\xc5\x1bk\xae:O\xfd\xbf}\x04\x03\x18\xc0\xc0\x9a\x19 \x00\x08\x00\x02\x80\x00\xe8h\xe7(\xf8\xafy\xa1\xb6\x99u\xaeF\x07\x1f|p\xb8\xe7\x9e{::G\xea\xf0\xe29\t\x80\xf8\xd6\x0f_\x9d\xaf\xc0\xed\xb7\xdfN\x00\xd4\xb8\x7f\xb1\xafun_S{\xb5\'\x00\x08\x00\x02\xa0\xc6\x1bh\x9d\xaf\x00\x10\xfcm@UnBv\xdai\xa7\xf0\xfd\xef\x7f?\xacX\xb1\xa2\xf3I\xa7\x06G\x90\x93\x00\xb8\xf6\xdakk0"\xe9\x1f\xe2\xf9\xe7\x9fO\x00\xd4\xb8\x7f\xa9\xf2\xfa\xef\xd8\xf4\'\xa93@\x00\x10\x00\x04@\x8d7\xd0:\n\x00\xc1\xdf\xc6Z\xe5\x8du\xc8\x90!\xe1\xec\xb3\xcf\x0e\xcf>\xfbl\xfa\t\xaa\x89g\x98\x93\x00\xb8\xf3\xce;\x9bX9O\xd5h\x05\x0e=\xf4P\x02\xa0\xc6\xfdK\x95\xf7\x01\xc7\xa6OI\x9d\x01\x02\x80\x00 \x00j\xbc\x81\xd6I\x00\x08\xfe6\xd4\xaao\xa8\xf1\xce\xfe.\xf7o,\x8e\xe5$\x00~\xf9\xcb_6V$\xbf\xd5\xb4\n<\xf1\xc4\x13!\xca\xba\xaa\xaf)\x8e\xcf\xbe\x87\x01\x0cT\x91\x01\x02\x80\x00 \x00\x08\x80\xa65e==\x91\xe0o\xf3\xab\xe2\xe6\xb7\xea1M\x9a4)\xc4\x9b\x8a\xf9j\xac\x029\t\x80\x07\x1f|\xb0\xb1"\xf9\xad\xa6U\xe0;\xdf\xf9\x8e\xf0_\xe3\xdee\xd5\xf5\xd7\xff\xeb\x130\xd0^\x06\x08\x00\x02\x80\x00\xa8\xf1&Z\xe5+\x00\x04\xff\xf6.\xe66\xcf\xc6\xea\xbd\xed\xb6\xdb\x86\x1bo\xbc\xb1i\xc1$\xd7\'\xcaI\x00\xc4s\xf5\xd5\xd9\n\xc4\x9bsZ\xf3\x1a[\xf3\xd4M\xdd0\x80\x01\x02\x80\x00 \x00\x08\x80\xa6vr\x82\xbf\x8d\xa5.\xcd\xc5\xde{\xef\x1d\x84\xb9\xe6L\xff\x9c\x04\xc0\x7f\xfd\xd7\x7f5\xa7h\x9e\xa5\xa1\n\xdc\x7f\xff\xfd\xc2\x7f\x8d\xfb\x96\xba\xec\x0f\x8eS/\x932\x03\x04\x00\x01@\x00\xd4x#\xad\xd2\x15\x00\x82\xbf\xcd\xb2N\x9b\xe5\xb8q\xe3\xc2SO=\xd5P\x00\xf1K\xabW \'\x01p\xdbm\xb7\xad^\x00?i[\x05>\xfb\xd9\xcf\x12\x005\xee[\xea\xb4O8V}M\xaa\x0c\x10\x00\x04\x00\x01P\xe3\x8d\xb4\n\x02@\xf0\xb7A\xd6m\x83\xfc\xfc\xe7?\x1f\x96/_\xde\xb6\xc0\x92\xc3\x0b\xe5$\x00\xe2\xc7C\xfa\xeaL\x05\x1e~\xf8\xe10t\xe8P\x02\xa0\xc6}K\xdd\xf6\x0b\xc7\xab\xc7I\x91\x01\x02\x80\x00 \x00j\xbc\x91vR\x00\x08\xfe6\xc5:n\x8a_\xfc\xe2\x17\xc3\x8a\x15+:\x93^\x12~\xd5\x9c\x04\xc0\xd7\xbe\xf6\xb5\x84G\xb2\xda\xa7v\xf2\xc9\'\x0b\xff5\xeeY\xea\xb8g8f\xbdN\x8a\x0c\x10\x00\x04\x00\x01P\xe3\xcd\xb4\x13\x02@\xf0\xb7\x19\xd6u3<\xe3\x8c3\xaa\x9dnj|t9\t\x80c\x8f=\xb6\xc6#U\xdfC\x8f\x1f\xbf8x\xf0`\x02\xa0\xc6=K]\xf7\x0e\xc7\xad\xefI\x8d\x01\x02\x80\x00 \x00j\xbc\x99\xb6S\x00\x08\xfe6\xc0:o\x80\xc2\x7fk\x83_N\x02\xe0}\xef{_k\x8b\xe9\xd9{\xac\xc0\x87?\xfca\xe1\xbf\xc6\xfdJ\x9d\xf7\x0f\xc7\xae\xffI\x8d\x01\x02\x80\x00 \x00j\xbc\xa1\xc6\xcf.o\xf5\x97\xe0o\xe3\xab\xfb\xc6w\xdcq\xc7\x85\xc8\xb1\xaf\xd6U \'\x01\x10\xe7\xc3\xaf\x7f\xfd\xeb\xd6\x15\xd33\xafV\x81\xef}\xef{\xc2\x7f\x8d{\x95\xba\xef!\x8e_\x1f\x94\x1a\x03\x04\x00\x01@\x00\xd4xS\xfd\xe8G?\xbaZ\xa3\xd4\xac\x1f\x08\xfe6\xbc\x146\xbc1c\xc6\x84\xe7\x9e{\xaeY\xd3\xc2\xf3\xf4R\x81\xdc\x04\xc0\xf4\xe9\xd3{\xa9\x84\x1f7\xbb\x02\xbf\xfb\xdd\xef\xc2\xd6[oM\x00\xd4\xb8WIa/q\x0ez\xa2\x94\x18 \x00\x08\x00\x02\xa0\xc6\x9bjl\x8a\x9e\x7f\xfe\xf9\xa6\xf6[\x82\xbfM.\x95Mn\xe4\xc8\x91\xe1\xb1\xc7\x1ek\xea\xfc\xf0d=W 7\x01\x10\xc5\x92\xaf\xd6W`\xd9\xb2e\xe1\xe0\x83\x0f\x16\xfek\xdc\xa7\xa4\xb2\x9f8\x0f\xbdQJ\x0c\x10\x00\x04\x00\x01P\xf3\x8du\xce\x9c9M\xe9\xc2\x04\x7f\x9b[J\x9b[\xbcY\xd8\x9dw\xde\xd9\x94\xb9\xe1I\xd6\\\x81\xdc\x04@\x9c+\xff\xfd\xdf\xff\xbd\xe6\xc2x\xc4\x80*0e\xca\x14\xe1\xbf\xe6=JJ\xfb\x8as\xd1\'\xa5\xc2\x00\x01@\x00\x10\x005\xdf\\w\xdbm\xb7\xf0\xec\xb3\xcf6\xdcd\t\xfe6\xb4T6\xb4\x97\x9f\xc7\xb9\xe7\x9e\xdb\xf0\x9c\xf0\x8b\xe5+\x90\xa3\x00\x88\x1f)\xe9\xabu\x15X\xb0`\x81\xbb\xfe\xd7\xbc?y\xf9\x9a\xec\xbf\xf5\x1a\x18\xa8\x0e\x03\x04\x00\x01@\x00$\xb0\xc1\xc6\x9b\x9c-_\xbe\xbcT\'&\xf8Wg!\xb6)6w,\x0e:\xe8\xa0\x10/\x1d\xf6\xd5\xbe\n\xe4(\x00\xb6\xdaj\xab\xf0\xe7?\xff\xb9}E\xce\xe8\x95~\xf6\xb3\x9f\x85X_kcs\xd7F\xf5TO\x0c` 2@\x00\x10\x00\x04@\x02\x02 N\xe6\x8f}\xecc\xe1\xa9\xa7\x9eZc\x8b(\xf8[\xfcSn\x00\x86\r\x1b\x16b\x18\xf5\xd5\xde\n\xe4(\x00\xe2<\x9a<yr{\x0b\x9d\xc1\xab=\xf2\xc8#a\xc7\x1dw\x14\xfe\x13\xe9MR\xdeo\x9c\x9b~\xaa\xae\x0c\x10\x00\x04\x00\x01\x90\xd0&\xbb\xd3N;\x85\x993g\x86g\x9eyf\xb56\xf1/\x7f\xf9K\x88\xf7\x0b\xd8o\xbf\xfd4V\t\x8dy]7\x9fV\x1d\xf7\xd7\xbf\xfe\xf5\xd5\xd8\xf7\x83\xd6W \xde\xa9\xbdUcZ\xe5\xe7\x8d\xc2\xe9\xb7\xbf\xfdm\xeb\x0b\x9c\xc9+\xfc\xe9O\x7f\n\xfb\xee\xbbo\x96,U\x99s\xc7&\xe8b -\x06\x08\x00\x02\x80\x00H0\x0c\xc6\xa6\xf4\x90C\x0e\t\x9f\xf9\xccg\xc2I\'\x9d\x14>\xfc\xe1\x0f\x87-\xb7\xdcRS\x95\xe0X\xdb\x94_\xda\x94\xa3\x00[\xbcxq&Q\xa9Z\xa7\xf9\xc7?\xfe1\xdb\xf5e\xdc\xb8q\xd5\x1a\x8c\x9a\x1e\xcd\xa3\x8f>\x1a\xf6\xde{\xefl9\xb2\x96\xbf\xb4\x96\xab\x85Z`\xa0\xb5\x0c\x10\x00\x04\x00\x01 \x14j\xb80\x90\x04\x03\xb3f\xcd\xaai\xf4\xa9\xffa?\xf9\xe4\x93I0\xd4h\xd39{\xf6\xec\xfa\x0fb\x07\xcf ^\xf6\xbf\xe7\x9e{f\xcdP\xa3\xec\xf9\xbd\xd6\x06%\xf5U\xdf\x14\x19 \x00\x08\x00\x02@\xf8\xd3ta\xa0\xf6\x0c\xec\xba\xeb\xaea\xe9\xd2\xa5\x1d\x8c0y\xbft|\xdbQ\x8aMR\x7f\xcfi\xf8\xf0\xe1>\x16\xb0\xc1)\xf0\xcb_\xfe2\x8c\x1c92k~\xfa\xcb\x99\xc7\t\xa3\x18\xc0@3\x18 \x00\x08\x00\x02@\xf8\xd3xa\xa0\xf6\x0c\xcc\x981\xa3\xc1\xf8\xe1\xd7\x9aQ\x81\xe7\x9e{\xae\xf6\x0c\r\xb4\xa9\xda}\xf7\xdd\xc3\xe3\x8f?\xde\x8crf\xf3\x1c7\xdcpC\x181bD\xf6\xec\x0c\x94=\xbf/\x14b\x00\x03e\x18 \x00\x08\x00\x02@\xf8\xd3|a\xa0\xd6\x0c\xc4\x7f}\xed\xcf\'`d\x93\xaa:p\xa2\xcf?\xff|\xad\x19*\xd38\xf5\xf5\xd81c\xc6\x84(C|\xad\xb9\x02S\xa7N\xc5\x8c\xbd\x07\x03\x18\xc0@\x07\x18 \x00\x08\x00\x02\xa0\x03\x13\xaf\xaf\x06\xd2\xdf\xb1\xb8\x18(\xc7\xc0\x84\t\x13\xd6\x9c6<\xa2\xa5\x15X\xb2d\x89&\xee\xc5\xbd\xe4\x98c\x8e\xf1v\x94>h{\xe2\x89\'\xc2q\xc7\x1d\x87\x17\xbd\x07\x060\x80\x81\x0e1@\x00\x10\x00\x04@\x87&\x9f\x90W.\xe4\xa9\x97z\xf5\xc6\xc0\xa2E\x8b\xfa\x88\x1b\xfe\xaa\x1d\x15\x88\xef\xe3\xeem|r\xfc\xf9\x11G\x1c\x11\x9e}\xf6\xd9v\x94\xbeV\xafq\xcb-\xb7\x84\xf8i\x1d92\xe1\x9c\xeda\x18\xc0@U\x18 \x00\x08\x00\x02\x80\x00\xd0\x8ca\xa0\xb6\x0c\xec\xb0\xc3\x0e\xe1\x85\x17^\xa8U\x08J\xf1`\x7f\xfa\xd3\x9f\xd6\x96\xa1V5d\x1f\xfc\xe0\x07\xdd\x13\xe0E\xd8\xe3\xc7s~\xf1\x8b_\xc4\x88\xbd\x06\x03\x18\xc0@\x05\x18 \x00\x08\x00\x02\xa0\x02\x13\xb1U\r\xa8\xe7e\x9bSg\xe0\xa4\x93NJ1O\xd7\xee\x9c\x16.\\\xa8\xa9\xeba/\x89\x1fmw\xef\xbd\xf7\xd6n<\x9by\xc0\xd7^{\xad\xbb\xfc\xf7\xc0F\xeak\xb3\xf3\xd3\x7f`\xa0\xba\x0c\x10\x00\x04\x00\x01`c\xd6\xb8c\xa0\xb6\x0c\xcc\x993\xa7\x99Y\xc5s5X\x81\xd9\xb3g\xd7\x96\xa1V7\xa9\xc3\x86\r\x0b\xd3\xa7O\x0f+V\xach\xb0\xba\xf5\xfc\xb5\xf8\xb6\x90\xb1c\xc7\xe2\xc2\xfe\x82\x01\x0c`\xa0b\x0c\x10\x00\x04\x00\x01P\xb1I\xd9\xeaf\xd4\xf3W\xd7\xc8\x1a\x9b\xf2c\xf3\x9b\xdf\xfc\xa6\x9e\xe9(\xb1\xa3\xfe\xf2\x97\xbf\xac\xc1[\xc3^2n\xdc\xb8\xf0\xd0C\x0f%6\xf2\xab\x9fN<\xc7SO=5\x0c\x192\x04\x13k`\xc2\x9a_~\xcdW35\xc3\xc0\xc0\x19 \x00\x08\x00\x02\xc0\x06\xadI\xc3@-\x19\x88\x9f\x1f\x9e\xdb\xbf\xaa\xae\x1e\xb7\xaa\xf1\x93\xa3\x8f>\xba\x96\x0c\xb5\xbb\x91\xdcb\x8b-\xc2\xd7\xbf\xfe\xf5\x10\xdf\x13\x9f\xda\xd7\x03\x0f<\x10N>\xf9\xe40t\xe8P,\xd8S0\x80\x01\x0cT\x98\x01\x02\x80\x00 \x00*<A\xdb\xdd\x9cz\xbd\x81[U5l_\r\xe3g\xae\xfb\xaaF\x05\xde\xfb\xde\xf7j\xf6J\xec%\xbb\xec\xb2K\x88o_Y\xbati5\x06p\x00Gq\xd7]w\x85O\x7f\xfa\xd3a\xf0\xe0\xc1\x18(\xc1\x80\xbd\xa2}{\x85Z\xab5\x06Vf\x80\x00 \x00\x08\x00\x1b\xb6\xa6\r\x03\xb5d\xe0S\x9f\xfa\xd4\x00b\x8b_mV\x05\xfe\xe7\x7f\xfe\xa7\x96\xfcT\xa1!\x1c9rd\xb8\xe8\xa2\x8b\xc23\xcf<\xd3\xac\xe1h\xcb\xf3<\xf1\xc4\x13a\xc6\x8c\x19a\xdf}\xf75\xf6\xf6\x0f\x0c`\x00\x035c\x80\x00 \x00\x08\x80\x9aM\xda*4\xad\x8eae\x93\xaa\x1e\x9d\xa9\xc7\x19g\x9c\xd1\x96\xb0\xe3E\xfa\xae\xc0\xc5\x17_\xac\xf9\x1b\xe0>\xb2\xed\xb6\xdb\x86\xc9\x93\'\x87\x9f\xfd\xecg}\x17\xbb\x83\x7f\xfb\xd4SO\x85\xb9s\xe7\x86O~\xf2\x93!\xde\xd8\xd0\xba\xd7\x99uO\xdd\xd5\x1d\x03\x18\x18(\x03\x04\x00\x01@\x00\x0c\xb0q\x1b\xe8$\xf4\xfb\x16r\x0c4\xc6\xc0\x94)S:\x18\x87\xbctw\x05\x8e8\xe2\x08a\xb0\x89\xfb\xc8^{\xed\x15"\xdb\xf1.\xfa\x9d\xfe\x8a7\xf4\xbb\xf4\xd2K\xc3\x91G\x1e\xe9\xbd\xfdM\x1cck~ck\xbe\xba\xa9\x1b\x06\x9a\xc3\x00\x01@\x00\x10\x006u\xcd;\x06j\xc9\xc0\xb7\xbe\xf5\xadN\xe7\xa3\xec_\xffO\x7f\xfa\x93\xf7~\xb7p\xfd\xd8q\xc7\x1dC|\xab\xcb\xacY\xb3B\xbc\xc9^+oz\x19\xefG\xf0\xf3\x9f\xff\xbc\xeb\xb5>\xf7\xb9\xcf\x85\xddv\xdb\xad\x96\xeb\x82\x80\xd0\x9c\x80\xa0\x8e\xea\x88\x81t\x19 \x00\x08\x00\x02\xa0\x85\xcd\x9b\xc53\xdd\xc5\xd3\xd8v~l\xa7N\x9d\x9a}\x00\xeft\x01.\xbc\xf0B!\xb1\x8d{\xc8\xf0\xe1\xc3C\xbc\xf9e\x0c\xe8\xd3\xa7O\x0f\xd7_\x7f}\x887\xe1\x8b\x1f\x87\x19/\xd1\xef\xeb+~\xf2\xc0\xef\x7f\xff\xfb\xf0_\xff\xf5_a\xd1\xa2E\xe1{\xdf\xfb^\xd7\x95\x06\xf1\x06~\xa3G\x8fvY\x7f\x1b\xc7\xd1\xfe\xd1\xf9\xfd\xc3\x18\x18\x83\x9c\x19 \x00\x08\x00\x02\xc0\xa6\xaf\x81\xc7@-\x198\xf7\xdcs\xfb\xca;\xfe\xae\xc5\x15\x88\xff\x1a=j\xd4\xa8Z\xb2\x93j\xe3\x17\xdf\x9b\xbf\xcd6\xdb\x84\xed\xb6\xdb.l\xbf\xfd\xf6!~:C\xfc\xf6\x9e}a\'U\xe6\x9d\x17\xb61P\x9e\x01\x02\x80\x00 \x00\x84?\r<\x06j\xc9\xc0\xa4I\x93Z\x1cq=}_\x15\xb8\xf1\xc6\x1bk\xc9\x8df\xb1|\xb3\xa8fj\x86\x01\x0c` \x1d\x06\x08\x00\x02\x80\x00\x10\xfe4\xf1\x18\xa8%\x03\xc7\x1e{l_\xf9\xd4\xdf\xb5\xb8\x02\x07\x1f|p-\xb9\xd1\xc4\xa6\xd3\xc4\x1aKc\x89\x01\x0c`\xa0<\x03\x04\x00\x01@\x00\x08\x7f\x9ax\x0c\xd4\x92\x81}\xf6\xd9\xa7\xc5\x11\xd7\xd3\xf7V\x81\x9bo\xbe\xb9\x96\xcch\x14\xcb7\x8aj\xa6f\x18\xc0\x00\x06\xd2b\x80\x00 \x00\x08\x00\xe1O#\x8f\x81Z20d\xc8\x90\xf0\x97\xbf\xfc\xa5\xb7\x8c\xea\xe7-\xaa\xc0\xf2\xe5\xcb\xc3\x01\x07\x1cPKf4\xb1i5\xb1\xc6\xd3xb\x00\x03\x18(\xcf\x00\x01@\x00\x10\x00\xc2\x9fF\x1e\x03\xb5e\xe0\x8e;\xeehQ\xcc\xf5\xb4\xbdU\xe0\x92K.\xa9-/\x1a\xc5\xf2\x8d\xa2\x9a\xa9\x19\x060\x80\x81\xb4\x18 \x00\x08\x00\x02@\xf8\xd3\xccc\xa0\xb6\x0c\x9c\x7f\xfe\xf9\xbd\xe5T?oA\x05\x1e}\xf4\xd1\xae\xbb\xcck\x06\xd3j\x06\x8d\xa7\xf1\xc4\x00\x060\x90\x0f\x03\x04\x00\x01@\x00\x08\x7f\xb5\r\x7f6\xab|6\xab\xde\xc6\xfaC\x1f\xfaP\x0bb\xae\xa7\xec\xa9\x02/\xbc\xf0B\x18;v\xac\xf5\xc2\x9e\x81\x01\x0c`\x00\x03\x18\xa81\x03\x04\x00\x01@\x00\xd4x\x02\xf7\x16\x8a\xfc\\0\xce\x89\x81G\x1ey\xa4\xa7\xbc\xeagM\xae\xc0\xb4i\xd34|\xf6\x0b\x0c`\x00\x03\x18\xc0@\xcd\x19 \x00\x08\x00\x02\xa0\xe6\x938\xa7\xa0\xe7\\\x89\x8d\x9e\x18\xf8\xe67\xbf\xd9\xe4\xa8\xeb\xe9V\xad\xc0}\xf7\xdd\x17\x86\r\x1b\xa6\xe9\xb3_`\x00\x03\x18\xc0\x00\x06j\xce\x00\x01@\x00\x10\x005\x9f\xc4=\x05"?\x13\x94sb`\xe4\xc8\x91a\xe9\xd2\xa5\xabfV\xff\xdf\xa4\n\xfc\xe9O\x7f\n\xb1\xc691\xe5\\\xad\xa1\x18\xc0\x00\x060\x90*\x03\x04\x00\x01@\x00\x10\x00\x1a{\x0c\xd4\x9e\x81\xef\x7f\xff\xfbM\x8a\xbb\x9e\xe6\xe5\x15X\xbcxq\x183fL\xed\xf9H\xb5\x89s^\x02\n\x060\x80\x01\x0c\x94e\x80\x00 \x00\x08\x00\xe1Os\x8f\x81\xda30j\xd4(W\x01\xbc<\xb97\xe1\xbf\xe3M\xff>\xfe\xf1\x8f\xd7\x9e\x8d\xb2\x8d\x91\xc7k\xa61\x80\x01\x0c` e\x06\x08\x00\x02\x80\x00\x10\xfe4\xf8\x18H\x82\x81\x8b/\xbe\xb8\t\xb1\xd7S\xc4\n\xc4\xf0\x7f\xca)\xa7$\xc1E\xcaM\x9cs\x13R0\x80\x01\x0c`\xa0,\x03\x04\x00\x01@\x00\x08\x7f\x9a|\x0c$\xc1\xc0\xd6[o\x1d\xe2\xe7\xd4\xfb\x1aX\x05b\xf8?\xf9\xe4\x93\x93`\xa2lS\xe4\xf1\x1ai\x0c`\x00\x03\x18H\x9d\x01\x02\x80\x00 \x00\x84?\x8d>\x06\x92a\xe0\xa8\xa3\x8e\n+V\xac\x18X\x02\xce\xf8\xb7\x97/_\x1eN<\xf1\xc4dxH\xbd\x89s~\x82\n\x060\x80\x01\x0c\x94e\x80\x00 \x00\x08\x00\xe1O\xb3\x8f\x81\xa4\x18\xb8\xe8\xa2\x8b2\x8e\xf0\x8d\x9fz\xbc\xe1\xdfq\xc7\x1d\x97\x14\x0be\x9b"\x8f\xd7Hc\x00\x03\x18\xc0@\xea\x0c\x10\x00\x04\x00\x01 \xfci\xf81\x90\x14\x03C\x86\x0c\t7\xddtS\xe3I8\xc3\xdf|\xe4\x91G\xc2\xfb\xdf\xff\xfe\xa48H\xbd\x81s~B\n\x060\x80\x01\x0c4\xc2\x00\x01@\x00\x10\x00\xc2\x9f\xa6\x1f\x03\xc910b\xc4\x88p\xdf}\xf7e\x18\xe5\xcb\x9f\xf2]w\xdd\x15\xde\xf7\xbe\xf7%\xc7@#M\x91\xdf\xd1Lc\x00\x03\x18\xc0@\xea\x0c\x10\x00\x04\x00\x01 \xfci\xfc1\x90$\x03;\xed\xb4Sx\xf0\xc1\x07\xcb\'\xe2L~#\xde+!\xbe]b\xd8\xb0aI\x8e\x7f\xea\r\x9c\xf3\x13R0\x80\x01\x0c`\xa0\x11\x06\x08\x00\x02\x80\x00\x10\xfe4\xff\x18H\x96\x81\xf8/\xdb?\xff\xf9\xcf3\x89\xf4\xfd?\xcd\xdf\xff\xfe\xf7\xe1\xf0\xc3\x0fOv\xdc\x1bi\x88\xfc\x8eF\x1a\x03\x18\xc0\x00\x06r`\x80\x00 \x00\x08\x00\xe1O\x08\xc0@\xd2\x0c\xc4\x8f\x07\\\xb4hQ\xff\xd3q\xe2\x8f\xbc\xea\xaa\xab\xc2\xb6\xdbn\x9b\xf4\x98\xe7\xd0\xc09GA\x05\x03\x18\xc0\x00\x06\x1aa\x80\x00 \x00\x08\x00\xe1O\x10\xc0@\xf2\x0c\xc4\xcb\xdcg\xce\x9c\x99x\xb4\xef\xfb\xf4~\xfd\xeb_\x87q\xe3\xc6%?\xd6\x8d4C~G\x13\x8d\x01\x0c`\x00\x03\xb90@\x00\x10\x00\x04\x80\xf0\'\x10` \x1b\x06\x8e=\xf6\xd8\xf0\xe4\x93O\xf6\x9d\x94\x13\xfb\xdb\xa7\x9f~:\x9cy\xe6\x99a\xe8\xd0\xa1\xd9\x8cs.M\x9c\xf3\x14X0\x80\x01\x0c`\xa0,\x03\x04\x00\x01@\x00\x08\x7fB\x01\x06\xb2b`\x97]v\t\xb7\xdcrKb1\x7f\xf5\xd3y\xee\xb9\xe7\xc2\xc5\x17_\xec\x0e\xff\xe6wV\xf3\xbbl#\xec\xf1\xc2\x13\x060\x90\x1b\x03\x04\x00\x01@\x00h\x0e5\x87\x18\xc8\x92\x81\x8f}\xecc\xe17\xbf\xf9\xcd\xea\xc9\xb9\xe6?y\xf6\xd9g\xc3\xf4\xe9\xd3\x05\x7f\xf3:\xcby\x9d[#\xef|\x85W\x0c`\xa0,\x03\x04\x00\x01@\x00h\x125\x89\x18\xc8\x96\x81xo\x80\xb3\xcf>;<\xfe\xf8\xe35\x8f\xfd!\xc4;\xfbO\x992%l\xbf\xfd\xf6\xd9\x8eg\xd9&\xc8\xe35\xce\x18\xc0\x00\x060\x90\x1b\x03\x04\x00\x01@\x00\x08\x7f\xc2\x02\x06\xb2g`\xcb-\xb7\x0c\x93&M\xaa\xdd\x15\x01\xcb\x96-\x0b\x0b\x16,\x08G\x1f}t\x18<xp\xf6\xe3\x98[\x13\xe7|\x05\x17\x0c`\x00\x03\x18(\xcb\x00\x01@\x00\x10\x00\xc2\x9f\xd0\x80\x01\x0c\xbc\xc8@\x0c\xd1\x1f\xff\xf8\xc7\xc3\xf5\xd7_\x1f\x96,YR\xc9\xab\x02\x96/_\x1en\xbf\xfd\xf6\xae\x1b\xfb\xed\xb4\xd3N\xc6\xce\xfc\xc5\x00\x060\x80\x01\x0c`\xa0\xdf\x0c\x10\x00\x04\x00\x01`\xc1\xe8\xf7\x82Q\xd60z<+]g\x06\xb6\xddv\xdb0a\xc2\x84\xf0\xa3\x1f\xfd\xa8\xe32\xe0\x99g\x9e\t\xf3\xe7\xcf\x0f\x9f\xfd\xecg]\xe2o\xcd\xb6fc\x00\x03\x18\xc0\x00\x06\x1af\x80\x00 \x00\x08\x00\x0bH\xc3\x0bH\x9d\xc3\x9dc\'\'\xca0\x10\xdf"p\xc4\x11G\x84\xf3\xcf??\xdc}\xf7\xdd-\x17\x02\x0f=\xf4P\xb8\xee\xba\xeb\xc2Yg\x9d\x15\xc6\x8c\x19\x13\x86\x0c\x19b\x9eZ\xab1\x80\x01\x0c`\x00\x03\x18\x180\x03\x04\x00\x01@\x00XH\x06\xbc\x90\x94\tR\x1e+x\xa7\xc0@\x0c\xe4\xfb\xec\xb3O\xf8\xc4\'>\x11\xce=\xf7\xdc0w\xee\xdc\xf0\xe3\x1f\xff8\xfc\xeaW\xbf\xea\xba\xa1`\xbcL\xbf\xb7\xaf\xa5K\x97v=\xe6\x7f\xfe\xe7\x7f\xc2]w\xdd\xd5\x15\xf4\xe3]\xfb\'N\x9c\x18\x0e=\xf4\xd0\xb0\xddv\xdb\x99\x93\xd6e\x0c`\x00\x03\x18\xc0\x00\x06Z\xc2\x00\x01@\x00\x10\x00\x16\x97\x96,.)\x84<\xe7@V4\xca@\xbc\x97\xc06\xdbl\xd3\x15\xe6\xe3]\xf9\xdf\xfb\xde\xf7v},\xdf\xd6[om\xbeYs1\x80\x01\x0c`\x00\x03\x18\xe8\x18\x03\x04\x00\x01@\x00X\x80:\xb6\x005\x1a\xae\xfc\x9e`\x8e\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xf2\x0c\x10\x00\x04\x00\x01@\x00\x10\x00\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x06\x0c\x10\x00\x04\x00\x01\x90\xc1DgG\xcb\xdbQ5S3\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xd4\x18 \x00\x08\x00\x02\x80\x00`{1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x0c\x18 \x00\x08\x00\x02 \x83\x89\x9e\x9a\xb9t>l<\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xca3@\x00\x10\x00\x04\x00\x01\xc0\xf6b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x190@\x00\x10\x00\x04@\x06\x13\x9d\x1d-oG\xd5L\xcd0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Rc\x80\x00 \x00\x08\x00\x02\x80\xed\xc5\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x062`\x80\x00 \x00\x08\x80\x0c&zj\xe6\xd2\xf9\xb0\xf1\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18(\xcf\x00\x01@\x00\x10\x00\x04\x00\xdb\x8b\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0cd\xc0\x00\x01@\x00\x10\x00\x19Ltv\xb4\xbc\x1dU35\xc3\x00\x060\x80\x01\x0c`\x00\x03\x18H\x8d\x01\x02\x80\x00 \x00\x08\x00\xb6\x17\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x80\x01\x02\x80\x00 \x002\x98\xe8\xa9\x99K\xe7\xc3\xc6c\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0<\x03\x04\x00\x01@\x00\x10\x00l/\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x90\x01\x03\x04\x00\x01@\x00d0\xd1\xd9\xd1\xf2vT\xcd\xd4\x0c\x03\x18\xc0\x00\x060\x80\x01\x0c` 5\x06\x08\x00\x02\x80\x00 \x00\xd8^\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` \x03\x06\x08\x00\x02\x80\x00\xc8`\xa2\xa7f.\x9d\x0f\x1b\x8f\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xf2\x0c\x10\x00\x04\x00\x01@\x00\xb0\xbd\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x06\x0c\x10\x00\x04\x00\x01\x90\xc1DgG\xcb\xdbQ5S3\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xd4\x18 \x00\x08\x00\x02\x80\x00`{1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x0c\x18 \x00\x08\x00\x02 \x83\x89\x9e\x9a\xb9t>l<\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xca3@\x00\x10\x00\x04\x00\x01\xc0\xf6b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x190@\x00\x10\x00\x04@\x06\x13\x9d\x1d-oG\xd5L\xcd0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Rc\x80\x00 \x00\x08\x00\x02\x80\xed\xc5\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x062`\x80\x00 \x00\x08\x80\x0c&zj\xe6\xd2\xf9\xb0\xf1\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18(\xcf\x00\x01@\x00\x10\x00\x04\x00\xdb\x8b\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0cd\xc0\x00\x01@\x00\x10\x00\x19Ltv\xb4\xbc\x1dU35\xc3\x00\x060\x80\x01\x0c`\x00\x03\x18H\x8d\x01\x02\x80\x00 \x00\x08\x00\xb6\x17\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x80\x01\x02\x80\x00 \x002\x98\xe8\xa9\x99K\xe7\xc3\xc6c\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0<\x03\x04\x00\x01@\x00\x10\x00l/\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x90\x01\x03\x04\x00\x01@\x00d0\xd1\xd9\xd1\xf2vT\xcd\xd4\x0c\x03\x18\xc0\x00\x060\x80\x01\x0c` 5\x06\x08\x00\x02\x80\x00 \x00\xd8^\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` \x03\x06\x08\x00\x02\x80\x00\xc8`\xa2\xa7f.\x9d\x0f\x1b\x8f\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xf2\x0c\x10\x00\x04\x00\x01@\x00\xb0\xbd\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x06\x0c\x10\x00\x04\x00\x01\x90\xc1DgG\xcb\xdbQ5S3\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xd4\x18 \x00\x08\x00\x02\x80\x00`{1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x0c\x18 \x00\x08\x00\x02 \x83\x89\x9e\x9a\xb9t>l<\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xca3@\x00\x10\x00\x04\x00\x01\xc0\xf6b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x190@\x00\x10\x00\x04@\x06\x13\x9d\x1d-oG\xd5L\xcd0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Rc\x80\x00 \x00\x08\x00\x02\x80\xed\xc5\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x062`\x80\x00 \x00\x08\x80\x0c&zj\xe6\xd2\xf9\xb0\xf1\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18(\xcf\x00\x01@\x00\x10\x00\x04\x00\xdb\x8b\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0cd\xc0\x00\x01@\x00\x10\x00\x19Ltv\xb4\xbc\x1dU35\xc3\x00\x060\x80\x01\x0c`\x00\x03\x18H\x8d\x01\x02\x80\x00 \x00\x08\x00\xb6\x17\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x80\x01\x02\x80\x00 \x002\x98\xe8\xa9\x99K\xe7\xc3\xc6c\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0<\x03\x04\x00\x01@\x00\x10\x00l/\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x90\x01\x03\x04\x00\x01@\x00d0\xd1\xd9\xd1\xf2vT\xcd\xd4\x0c\x03\x18\xc0\x00\x060\x80\x01\x0c` 5\x06\x08\x00\x02\x80\x00 \x00\xd8^\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` \x03\x06\x08\x00\x02\x80\x00\xc8`\xa2\xa7f.\x9d\x0f\x1b\x8f\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xf2\x0c\x10\x00\x04\x00\x01@\x00\xb0\xbd\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x06\x0c\x10\x00\x04\x00\x01\x90\xc1DgG\xcb\xdbQ5S3\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xd4\x18 \x00\x08\x00\x02\x80\x00`{1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x0c\x18 \x00\x08\x00\x02 \x83\x89\x9e\x9a\xb9t>l<\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xca3@\x00\x10\x00\x04\x00\x01\xc0\xf6b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x190@\x00\x10\x00\x04@\x06\x13\x9d\x1d-oG\xd5L\xcd0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Rc\x80\x00 \x00\x08\x00\x02\x80\xed\xc5\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x062`\x80\x00 \x00\x08\x80\x0c&zj\xe6\xd2\xf9\xb0\xf1\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18(\xcf\x00\x01@\x00\x10\x00\x04\x00\xdb\x8b\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0cd\xc0\x00\x01@\x00\x10\x00\x19Ltv\xb4\xbc\x1dU35\xc3\x00\x060\x80\x01\x0c`\x00\x03\x18H\x8d\x01\x02\x80\x00 \x00\x08\x00\xb6\x17\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x80\x01\x02\x80\x00 \x002\x98\xe8\xa9\x99K\xe7\xc3\xc6c\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0<\x03\x04\x00\x01@\x00\x10\x00l/\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x90\x01\x03\x04\x00\x01@\x00d0\xd1\xd9\xd1\xf2vT\xcd\xd4\x0c\x03\x18\xc0\x00\x060\x80\x01\x0c` 5\x06\x08\x00\x02\x80\x00 \x00\xd8^\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` \x03\x06\x08\x00\x02\x80\x00\xc8`\xa2\xa7f.\x9d\x0f\x1b\x8f\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xf2\x0c\x10\x00\x04\x00\x01@\x00\xb0\xbd\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x06\x0c\x10\x00\x04\x00\x01\x90\xc1DgG\xcb\xdbQ5S3\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xd4\x18 \x00\x08\x00\x02\x80\x00`{1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x0c\x18 \x00\x08\x00\x02 \x83\x89\x9e\x9a\xb9t>l<\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xca3@\x00\x10\x00\x04\x00\x01\xc0\xf6b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x190@\x00\x10\x00\x04@\x06\x13\x9d\x1d-oG\xd5L\xcd0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Rc\x80\x00 \x00\x08\x00\x02\x80\xed\xc5\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x062`\x80\x00 \x00\x08\x80\x0c&zj\xe6\xd2\xf9\xb0\xf1\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18(\xcf\x00\x01@\x00\x10\x00\x04\x00\xdb\x8b\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0cd\xc0\x00\x01@\x00\x10\x00\x19Ltv\xb4\xbc\x1dU35\xc3\x00\x060\x80\x01\x0c`\x00\x03\x18H\x8d\x01\x02\x80\x00 \x00\x08\x00\xb6\x17\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x80\x01\x02\x80\x00 \x002\x98\xe8\xa9\x99K\xe7\xc3\xc6c\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0<\x03\x04\x00\x01@\x00\x10\x00l/\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x90\x01\x03\x04\x00\x01@\x00d0\xd1\xd9\xd1\xf2vT\xcd\xd4\x0c\x03\x18\xc0\x00\x060\x80\x01\x0c` 5\x06\x08\x00\x02\x80\x00 \x00\xd8^\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` \x03\x06\x08\x00\x02\x80\x00\xc8`\xa2\xa7f.\x9d\x0f\x1b\x8f\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xf2\x0c\x10\x00\x04\x00\x01@\x00\xb0\xbd\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x06\x0c\x10\x00\x04\x00\x01\x90\xc1DgG\xcb\xdbQ5S3\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xd4\x18 \x00\x08\x00\x02\x80\x00`{1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x0c\x18 \x00\x08\x00\x02 \x83\x89\x9e\x9a\xb9t>l<\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xca3@\x00\x10\x00\x04\x00\x01\xc0\xf6b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x190@\x00\x10\x00\x04@\x06\x13\x9d\x1d-oG\xd5L\xcd0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Rc\x80\x00 \x00\x08\x00\x02\x80\xed\xc5\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x062`\x80\x00 \x00\x08\x80\x0c&zj\xe6\xd2\xf9\xb0\xf1\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18(\xcf\x00\x01@\x00\x10\x00\x04\x00\xdb\x8b\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0cd\xc0\x00\x01@\x00\x10\x00\x19Ltv\xb4\xbc\x1dU35\xc3\x00\x060\x80\x01\x0c`\x00\x03\x18H\x8d\x01\x02\x80\x00 \x00\x08\x00\xb6\x17\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x80\x01\x02\x80\x00 \x002\x98\xe8\xa9\x99K\xe7\xc3\xc6c\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0<\x03\x04\x00\x01@\x00\x10\x00l/\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x90\x01\x03\x04\x00\x01@\x00d0\xd1\xd9\xd1\xf2vT\xcd\xd4\x0c\x03\x18\xc0\x00\x060\x80\x01\x0c` 5\x06\x08\x00\x02\x80\x00 \x00\xd8^\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` \x03\x06\x08\x00\x02\x80\x00\xc8`\xa2\xa7f.\x9d\x0f\x1b\x8f\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xf2\x0c\x10\x00\x04\x00\x01@\x00\xb0\xbd\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x06\x0c\x10\x00\x04\x00\x01\x90\xc1DgG\xcb\xdbQ5S3\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xd4\x18 \x00\x08\x00\x02\x80\x00`{1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x0c\x18 \x00\x08\x00\x02 \x83\x89\x9e\x9a\xb9t>l<\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xca3@\x00\x10\x00\x04\x00\x01\xc0\xf6b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x190@\x00\x10\x00\x04@\x06\x13\x9d\x1d-oG\xd5L\xcd0\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06Rc\x80\x00 \x00\x08\x00\x02\x80\xed\xc5\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x062`\x80\x00 \x00\x08\x80\x0c&zj\xe6\xd2\xf9\xb0\xf1\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18(\xcf\x00\x01@\x00\x10\x00\x04\x00\xdb\x8b\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0cd\xc0\x00\x01@\x00\x10\x00\x19Ltv\xb4\xbc\x1dU35\xc3\x00\x060\x80\x01\x0c`\x00\x03\x18H\x8d\x01\x02\x80\x00 \x00\x08\x00\xb6\x17\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc8\x80\x01\x02\x80\x00 \x002\x98\xe8\xa9\x99K\xe7\xc3\xc6c\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\xa0<\x03\x04\x00\x01@\x00\x10\x00l/\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x90\x01\x03\x04\x00\x01@\x00d0\xd1\xd9\xd1\xf2vT\xcd\xd4\x0c\x03\x18\xc0\x00\x060\x80\x01\x0c` 5\x06\x08\x00\x02\x80\x00 \x00\xd8^\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c` \x03\x06\x08\x00\x02\x80\x00\xc8`\xa2\xa7f.\x9d\x0f\x1b\x8f\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xf2\x0c\x10\x00\x04\x00\x01@\x00\xb0\xbd\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x18\xc0@\x06\x0c\x10\x00\x04\x00\x01\x90\xc1DgG\xcb\xdbQ5S3\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\xd4\x18 \x00\x08\x00\x02\x80\x00`{1\x80\x01\x0c`\x00\x03\x18\xc0\x00\x060\x80\x81\x0c\x18 \x00\x08\x00\x02 \x83\x89\x9e\x9a\xb9t>l<\x060\x80\x01\x0c`\x00\x03\x18\xc0\x00\x06\xca3@\x00\x10\x00\x04\x00\x01\xc0\xf6b\x00\x03\x18\xc0\x00\x060\x80\x01\x0c`\x00\x03\x190@\x00\xb4F\x00\xfc\x7f\x00\x00\x00\xff\xffT\xaenr\x00\x00@\x00IDAT\xed\xddm\x8c\x9d\xe5y\xe0q\x8fMl\x02\xcc\xda\xc6\xc62\xd8\x18\x03\xf1KlG\xe2\xc5\x16R\xbet\xd9\xa4\x9b\x97f\x15@IPH\xf1\x07\xb6\x0b\x96 $awK$`c\xa4\r\xc9.I\x90\xa8\x16\xc5-\xe9\x06\x12ba!\xed\xa6\xc1\xd46.\xb6Aj\xa4j\xf3\xa2\xaa\xea\xc7\xdd\xa2m*E\xaa\xba\xbb\xc9\x97\xd2R={_f\x06\xc6\x83=\x1e\x1f\xdf\xc7~\xee\xe7\xfa\x8d4\x19;\xf3\xc2s\xee\xe7w?>\xd7\x7f\xce\x9cY\xf0\xdak\xafu}y]\xb4hQ\xb7`\xc1\x82\xe6_\xb7m\xdb\xd6}\xe0\x03\x1f\xf0j\r\x18`\x80\x01\x06\x18`\x80\x01\x06\x18`\x80\x81\x11\x0c\xc4L5\x84\xd90f\xdc\xbe\xcc\xdbq\x1c\x0b\xfat0\x02\x80h \x9c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x02\xc0x\xbeQ/\x00\x8c\xe1\x11\x07\x1e\x01\xe0\x82\xe5\x1f-\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x18\x18\xdd\x80\x00 \x004\xf3\x10\x10\x01`\xf4\x8d\xee"i\xed\x18`\x80\x01\x06\x18`\x80\x01\x06\x18`@\x00\x10\x00\x04\x80\x11~v\xc6\xc5\xd3\xc5\x93\x01\x06\x18`\x80\x01\x06\x18`\x80\x01\x06Z3 \x00\x08\x00\x02\x80\x00\xe0\tT\x18`\x80\x01\x06\x18`\x80\x01\x06\x18` \x81\x01\x01@\x00\x10\x00\x12l\xf4\xd6\xca\xa4\xe3U\xd3\x19`\x80\x01\x06\x18`\x80\x01\x06\x18\xa8o@\x00\x10\x00\x04\x00\x01@\xede\x80\x01\x06\x18`\x80\x01\x06\x18`\x80\x81\x04\x06\x04\x00\x01@\x00H\xb0\xd1\xd5\xd3\xfa\xf5\xd4\x9aZS\x06\x18`\x80\x01\x06\x18`\x80\x81\xd6\x0c\x08\x00\x02\x80\x00 \x00\xa8\xbd\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\x90\xc0\x80\x00 \x00\x08\x00\t6zke\xd2\xf1\xaa\xe9\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c\xd47 \x00\x08\x00\x02\x80\x00\xa0\xf62\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0@\x02\x03\x02\x80\x00 \x00$\xd8\xe8\xeai\xfdzjM\xad)\x03\x0c0\xc0\x00\x03\x0c0\xc0@k\x06\x04\x00\x01@\x00\x10\x00\xd4^\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x18H`@\x00\x10\x00\x04\x80\x04\x1b\xbd\xb52\xe9x\xd5t\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\xea\x1b\x10\x00\x04\x00\x01@\x00P{\x19`\x80\x01\x06\x18`\x80\x01\x06\x18` \x81\x01\x01@\x00\x10\x00\x12lt\xf5\xb4~=\xb5\xa6\xd6\x94\x01\x06\x18`\x80\x01\x06\x18`\xa05\x03\x02\x80\x00 \x00\x08\x00j/\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c$0 \x00\x08\x00\x02@\x82\x8d\xdeZ\x99t\xbcj:\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\xf5\r\x08\x00\x02\x80\x00 \x00\xa8\xbd\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\x90\xc0\x80\x00 \x00\x08\x00\t6\xbazZ\xbf\x9eZSk\xca\x00\x03\x0c0\xc0\x00\x03\x0c0\xd0\x9a\x01\x01@\x00\x10\x00\x04\x00\xb5\x97\x01\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x12\x18\x10\x00\x04\x00\x01 \xc1Fo\xadL:^5\x9d\x01\x06\x18`\x80\x01\x06\x18`\x80\x81\xfa\x06\x04\x00\x01@\x00\x10\x00\xd4^\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x18H`@\x00\x10\x00\x04\x80\x04\x1b]=\xad_O\xad\xa95e\x80\x01\x06\x18`\x80\x01\x06\x18h\xcd\x80\x00 \x00\x08\x00\x02\x80\xda\xcb\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\t\x0c\x08\x00\x02\x80\x00\x90`\xa3\xb7V&\x1d\xaf\x9a\xce\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0@}\x03\x02\x80\x00 \x00\x08\x00j/\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c$0 \x00\x08\x00\x02@\x82\x8d\xae\x9e\xd6\xaf\xa7\xd6\xd4\x9a2\xc0\x00\x03\x0c0\xc0\x00\x03\x0c\xb4f@\x00\x10\x00\x04\x00\x01@\xede\x80\x01\x06\x18`\x80\x01\x06\x18`\x80\x81\x04\x06\x04\x00\x01@\x00H\xb0\xd1[+\x93\x8eWMg\x80\x01\x06\x18`\x80\x01\x06\x18`\xa0\xbe\x01\x01@\x00\x10\x00\x04\x00\xb5\x97\x01\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x12\x18\x10\x00\x04\x00\x01 \xc1FWO\xeb\xd7SkjM\x19`\x80\x01\x06\x18`\x80\x01\x06Z3 \x00\x08\x00\x02\x80\x00\xa0\xf62\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0@\x02\x03\x02\x80\x00 \x00$\xd8\xe8\xad\x95I\xc7\xab\xa63\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0P\xdf\x80\x00 \x00\x08\x00\x02\x80\xda\xcb\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\t\x0c\x08\x00\x02\x80\x00\x90`\xa3\xab\xa7\xf5\xeb\xa95\xb5\xa6\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\xad\x19\x10\x00\x04\x00\x01@\x00P{\x19`\x80\x01\x06\x18`\x80\x01\x06\x18` \x81\x01\x01@\x00\x10\x00\x12l\xf4\xd6\xca\xa4\xe3U\xd3\x19`\x80\x01\x06\x18`\x80\x01\x06\x18\xa8o@\x00\x10\x00\x04\x00\x01@\xede\x80\x01\x06\x18`\x80\x01\x06\x18`\x80\x81\x04\x06\x04\x00\x01@\x00H\xb0\xd1\xd5\xd3\xfa\xf5\xd4\x9aZS\x06\x18`\x80\x01\x06\x18`\x80\x81\xd6\x0c\x08\x00\x02\x80\x00 \x00\xa8\xbd\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\x90\xc0\x80\x00 \x00\x08\x00\t6zke\xd2\xf1\xaa\xe9\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c\xd47 \x00\x08\x00\x02\x80\x00\xa0\xf62\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0@\x02\x03\x02\x80\x00 \x00$\xd8\xe8\xeai\xfdzjM\xad)\x03\x0c0\xc0\x00\x03\x0c0\xc0@k\x06\x04\x00\x01@\x00\x10\x00\xd4^\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x18H`@\x00\x10\x00\x04\x80\x04\x1b\xbd\xb52\xe9x\xd5t\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\xea\x1b\x10\x00\x04\x00\x01@\x00P{\x19`\x80\x01\x06\x18`\x80\x01\x06\x18` \x81\x01\x01@\x00\x10\x00\x12lt\xf5\xb4~=\xb5\xa6\xd6\x94\x01\x06\x18`\x80\x01\x06\x18`\xa05\x03\x02\x80\x00 \x00\x08\x00j/\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c$0 \x00\x08\x00\x02@\x82\x8d\xdeZ\x99t\xbcj:\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\xf5\r\x08\x00\x02\x80\x00 \x00\xa8\xbd\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\x90\xc0\x80\x00 \x00\x08\x00\t6\xbazZ\xbf\x9eZSk\xca\x00\x03\x0c0\xc0\x00\x03\x0c0\xd0\x9a\x01\x01@\x00\x10\x00\x04\x00\xb5\x97\x01\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x12\x18\x10\x00\x04\x00\x01 \xc1Fo\xadL:^5\x9d\x01\x06\x18`\x80\x01\x06\x18`\x80\x81\xfa\x06\x04\x00\x01@\x00\x10\x00\xd4^\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x18H`@\x00\x10\x00\x04\x80\x04\x1b]=\xad_O\xad\xa95e\x80\x01\x06\x18`\x80\x01\x06\x18h\xcd\x80\x00 \x00\x08\x00\x02\x80\xda\xcb\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\t\x0c\x08\x00\x02\x80\x00\x90`\xa3\xb7V&\x1d\xaf\x9a\xce\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0@}\x03\x02\x80\x00 \x00\x08\x00j/\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c$0 \x00\x08\x00\x02@\x82\x8d\xae\x9e\xd6\xaf\xa7\xd6\xd4\x9a2\xc0\x00\x03\x0c0\xc0\x00\x03\x0c\xb4f@\x00\x10\x00\x04\x00\x01@\xede\x80\x01\x06\x18`\x80\x01\x06\x18`\x80\x81\x04\x06\x04\x00\x01@\x00H\xb0\xd1[+\x93\x8eWMg\x80\x01\x06\x18`\x80\x01\x06\x18`\xa0\xbe\x01\x01@\x00\x10\x00\x04\x00\xb5\x97\x01\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x12\x18\x10\x00\x04\x00\x01 \xc1FWO\xeb\xd7SkjM\x19`\x80\x01\x06\x18`\x80\x01\x06Z3 \x00\x08\x00\x02\x80\x00\xa0\xf62\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0@\x02\x03\x02\x80\x00 \x00$\xd8\xe8\xad\x95I\xc7\xab\xa63\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0P\xdf\x80\x00 \x00\x08\x00\x02\x80\xda\xcb\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\t\x0c\x08\x00\x02\x80\x00\x90`\xa3\xab\xa7\xf5\xeb\xa95\xb5\xa6\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\xad\x19\x10\x00\x04\x00\x01@\x00P{\x19`\x80\x01\x06\x18`\x80\x01\x06\x18` \x81\x01\x01@\x00\x10\x00\x12l\xf4\xd6\xca\xa4\xe3U\xd3\x19`\x80\x01\x06\x18`\x80\x01\x06\x18\xa8o@\x00\x10\x00\x04\x00\x01@\xede\x80\x01\x06\x18`\x80\x01\x06\x18`\x80\x81\x04\x06\x04\x00\x01@\x00H\xb0\xd1\xd5\xd3\xfa\xf5\xd4\x9aZS\x06\x18`\x80\x01\x06\x18`\x80\x81\xd6\x0c\x08\x00\x02\x80\x00 \x00\xa8\xbd\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\x90\xc0\x80\x00 \x00\x08\x00\t6zke\xd2\xf1\xaa\xe9\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c\xd47 \x00\x08\x00\x02\x80\x00\xa0\xf62\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0@\x02\x03\x02\x80\x00 \x00$\xd8\xe8\xeai\xfdzjM\xad)\x03\x0c0\xc0\x00\x03\x0c0\xc0@k\x06\x04\x00\x01@\x00\x10\x00\xd4^\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x18H`@\x00\x10\x00\x04\x80\x04\x1b\xbd\xb52\xe9x\xd5t\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\xea\x1b\x10\x00\x04\x00\x01@\x00P{\x19`\x80\x01\x06\x18`\x80\x01\x06\x18` \x81\x01\x01@\x00\x10\x00\x12lt\xf5\xb4~=\xb5\xa6\xd6\x94\x01\x06\x18`\x80\x01\x06\x18`\xa05\x03\x02\x80\x00 \x00\x08\x00j/\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c$0 \x00\x08\x00\x02@\x82\x8d\xdeZ\x99t\xbcj:\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\xf5\r\x08\x00\x02\x80\x00 \x00\xa8\xbd\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c0\x90\xc0\x80\x00 \x00\x08\x00\t6\xbazZ\xbf\x9eZSk\xca\x00\x03\x0c0\xc0\x00\x03\x0c0\xd0\x9a\x01\x01@\x00\x10\x00\x04\x00\xb5\x97\x01\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x12\x18\x10\x00\x04\x00\x01 \xc1Fo\xadL:^5\x9d\x01\x06\x18`\x80\x01\x06\x18`\x80\x81\xfa\x06\x04\x00\x01@\x00\x10\x00\xd4^\x06\x18`\x80\x01\x06\x18`\x80\x01\x06\x18H`@\x00\x10\x00\x04\x80\x04\x1b]=\xad_O\xad\xa95e\x80\x01\x06\x18`\x80\x01\x06\x18h\xcd\x80\x00 \x00\x08\x00\x02\x80\xda\xcb\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\t\x0c\x08\x00\x02\x80\x00\x90`\xa3\xb7V&\x1d\xaf\x9a\xce\x00\x03\x0c0\xc0\x00\x03\x0c0\xc0@}\x03\x02\x80\x00 \x00\x08\x00j/\x03\x0c0\xc0\x00\x03\x0c0\xc0\x00\x03\x0c$0 \x00\x08\x00\x02@\x82\x8d\xae\x9e\xd6\xaf\xa7\xd6\
Download .txt
gitextract_c46jkt4m/

├── .github/
│   └── workflows/
│       └── cross-build.yml
├── .gitignore
├── LICENSE
├── data/
│   └── icons/
│       └── maestro_icon.icns
├── dist/
│   └── maestro_music-2.0.6-py3-none-any.whl
├── install-scripts/
│   ├── mac
│   ├── ubuntu
│   └── windows.nsi
├── maestro/
│   ├── __init__.py
│   ├── __main__.py
│   ├── __version__.py
│   ├── config.py
│   ├── helpers.py
│   ├── icon.py
│   ├── jit_funcs.py
│   ├── mac_presence.py
│   └── main.py
├── readme.md
├── requirements.txt
├── scripts/
│   ├── add_album_art.py
│   ├── custom_album_art.py
│   └── rename_tracktitles.py
├── setup.py
├── specs/
│   ├── maestro-mac.spec
│   ├── maestro-ubuntu.spec
│   └── maestro-windows.spec
└── uninstall-scripts/
    └── unix
Download .txt
SYMBOL INDEX (206 symbols across 7 files)

FILE: maestro/config.py
  function print_to_logfile (line 238) | def print_to_logfile(*args, **kwargs):

FILE: maestro/helpers.py
  class Song (line 27) | class Song:
    method __init__ (line 28) | def __init__(self, song_id: int):
    method reset (line 42) | def reset(self):
    method __eq__ (line 50) | def __eq__(self, value):
    method __hash__ (line 55) | def __hash__(self):
    method __repr__ (line 58) | def __repr__(self):
    method override_lyrics_path (line 62) | def override_lyrics_path(self):
    method translated_lyrics_path (line 68) | def translated_lyrics_path(self):
    method song_id (line 74) | def song_id(self):
    method song_file (line 78) | def song_file(self):
    method song_path (line 83) | def song_path(self):
    method song_title (line 88) | def song_title(self):
    method song_title (line 93) | def song_title(self, v):
    method tags (line 114) | def tags(self) -> set[str]:
    method tags (line 118) | def tags(self, v: set[str]):
    method clips (line 122) | def clips(self) -> dict[str, list[int, int]]:
    method set_clip (line 126) | def set_clip(self) -> str:
    method set_clip (line 130) | def set_clip(self, v: str):
    method listen_times (line 134) | def listen_times(self) -> dict[int | str, float]:
    method _load_metadata (line 137) | def _load_metadata(self):
    method artist (line 143) | def artist(self):
    method artist (line 147) | def artist(self, v):
    method album (line 151) | def album(self):
    method album (line 155) | def album(self, v):
    method album_artist (line 159) | def album_artist(self):
    method album_artist (line 163) | def album_artist(self, v):
    method duration (line 167) | def duration(self):
    method artwork (line 171) | def artwork(self):
    method raw_lyrics (line 181) | def raw_lyrics(self) -> str | None:
    method raw_lyrics (line 185) | def raw_lyrics(self, v):
    method raw_override_lyrics (line 197) | def raw_override_lyrics(self) -> str | None:
    method raw_override_lyrics (line 204) | def raw_override_lyrics(self, v):
    method raw_translated_lyrics (line 220) | def raw_translated_lyrics(self) -> str | None:
    method raw_translated_lyrics (line 227) | def raw_translated_lyrics(self, v):
    method _parse_lyrics (line 240) | def _parse_lyrics(self, raw_lyrics):
    method parsed_lyrics (line 254) | def parsed_lyrics(self):
    method parsed_override_lyrics (line 260) | def parsed_override_lyrics(self):
    method parsed_translated_lyrics (line 268) | def parsed_translated_lyrics(self):
    method get_metadata (line 275) | def get_metadata(self, key, resolve=True):
    method set_metadata (line 291) | def set_metadata(self, key, value):
    method remove_from_data (line 312) | def remove_from_data(self):
    method _save (line 315) | def _save(self):
  class SongData (line 320) | class SongData:
    method __init__ (line 321) | def __init__(self):
    method load (line 325) | def load(self):
    method __getitem__ (line 350) | def __getitem__(self, key):
    method __setitem__ (line 355) | def __setitem__(self, key, value):
    method __delitem__ (line 360) | def __delitem__(self, key):
    method __iter__ (line 365) | def __iter__(self):
    method items (line 370) | def items(self):
    method values (line 375) | def values(self):
    method _save (line 380) | def _save(self):
    method add_song (line 387) | def add_song(self, filename, tags=None):
  class Songs (line 417) | class Songs:
    method __init__ (line 422) | def __init__(self):
    method load (line 426) | def load(self):
    method __contains__ (line 429) | def __contains__(self, value: Song):
    method __iter__ (line 434) | def __iter__(self) -> Iterable[Song]:
    method __len__ (line 439) | def __len__(self):
  function is_safe_username (line 448) | def is_safe_username(url):
  function bounded_shuffle (line 452) | def bounded_shuffle(lst, radius=-1):
  function set_timeout (line 472) | def set_timeout(func, timeout, *args, **kwargs):
  class Scroller (line 482) | class Scroller:
    method __init__ (line 483) | def __init__(self, num_lines, win_size):
    method scroll_forward (line 489) | def scroll_forward(self):
    method scroll_backward (line 498) | def scroll_backward(self):
    method halfway (line 505) | def halfway(self):
    method resize (line 508) | def resize(self, win_size=None):
    method refresh (line 514) | def refresh(self):
  function fit_string_to_width (line 518) | def fit_string_to_width(s, width, length_so_far):
  function addstr_fit_to_width (line 529) | def addstr_fit_to_width(stdscr, s, width, length_so_far, *args, **kwargs):
  class FFmpegProcessHandler (line 540) | class FFmpegProcessHandler:
    method __init__ (line 541) | def __init__(self, username, password):
    method start (line 546) | def start(self):
    method _start (line 549) | def _start(self):
    method terminate (line 571) | def terminate(self):
    method restart (line 576) | def restart(self):
    method write (line 580) | def write(self, chunk):
  class PlaybackHandler (line 588) | class PlaybackHandler:
    method __init__ (line 589) | def __init__(
    method _load_audio (line 677) | def _load_audio(self, path, sr):
    method _audio_processing_loop (line 692) | def _audio_processing_loop(self):
    method _streaming_loop (line 826) | def _streaming_loop(self):
    method paused (line 873) | def paused(self):
    method paused (line 877) | def paused(self, value):
    method volume (line 883) | def volume(self):
    method volume (line 887) | def volume(self, v):
    method want_stream (line 893) | def want_stream(self):
    method want_stream (line 897) | def want_stream(self, value):
    method song (line 903) | def song(self):
    method song_id (line 907) | def song_id(self):
    method song_file (line 911) | def song_file(self):
    method song_path (line 915) | def song_path(self):
    method song_title (line 919) | def song_title(self):
    method song_artist (line 923) | def song_artist(self):
    method song_album (line 927) | def song_album(self):
    method album_artist (line 931) | def album_artist(self):
    method artwork (line 935) | def artwork(self):
    method screen_height (line 943) | def screen_height(self):
    method screen_width (line 947) | def screen_width(self):
    method can_show_lyrics (line 951) | def can_show_lyrics(self):
    method can_show_translated_lyrics (line 955) | def can_show_translated_lyrics(self):
    method focus (line 961) | def focus(self):
    method focus (line 965) | def focus(self, value):
    method seek (line 970) | def seek(self, pos):
    method scroll_forward (line 980) | def scroll_forward(self):
    method scroll_backward (line 990) | def scroll_backward(self):
    method snap_back (line 1000) | def snap_back(self):
    method set_volume (line 1007) | def set_volume(self, v):
    method quit (line 1011) | def quit(self):
    method prompting_delete_char (line 1018) | def prompting_delete_char(self):
    method update_screen (line 1027) | def update_screen(self):
    method connect_to_discord (line 1030) | def connect_to_discord(self):
    method update_discord_metadata (line 1042) | def update_discord_metadata(self):
    method update_mac_now_playing_metadata (line 1108) | def update_mac_now_playing_metadata(self):
    method update_icecast_metadata (line 1130) | def update_icecast_metadata(self):
    method icecast_metadata_update_loop (line 1147) | def icecast_metadata_update_loop(self):
    method threaded_update_icecast_metadata (line 1167) | def threaded_update_icecast_metadata(self):
    method update_stream_metadata (line 1172) | def update_stream_metadata(self):  # artwork + icecast metadata
    method update_metadata (line 1193) | def update_metadata(self):
    method initialize_discord (line 1201) | def initialize_discord(self):
    method threaded_initialize_discord (line 1210) | def threaded_initialize_discord(self):
    method output (line 1213) | def output(self, pos):
  function init_curses (line 1902) | def init_curses(stdscr):
  class SongParamType (line 1936) | class SongParamType(click.ParamType):
    method convert (line 1939) | def convert(self, value, param, ctx) -> Song:
  function yt_embed_artwork (line 1971) | def yt_embed_artwork(yt_dlp_info, crop):
  function clip_editor (line 2040) | def clip_editor(stdscr, song: Song, name, start=None, end=None):
  function clip_editor_output (line 2178) | def clip_editor_output(
  function get_username (line 2371) | def get_username():
  function get_password (line 2377) | def get_password():
  function signup (line 2383) | def signup(username=None, password=None, login_=True):
  function login (line 2430) | def login(username=None, password=None):
  function format_seconds (line 2475) | def format_seconds(secs, show_decimal=False, digital=True, include_hours...
  function search_song (line 2503) | def search_song(phrase):
  function print_entry (line 2525) | def print_entry(
  function multiprocessing_put_word (line 2615) | def multiprocessing_put_word(q, word):
  function versiontuple (line 2621) | def versiontuple(v):
  function pluralize (line 2625) | def pluralize(count, word, include_count=True):
  function is_timed_lyrics (line 2629) | def is_timed_lyrics(lyrics):
  function get_lyric (line 2635) | def get_lyric(lyric_obj):
  function set_lyric (line 2641) | def set_lyric(lyrics, i, val):
  function display_lyrics (line 2648) | def display_lyrics(lyrics, song, prefix: str = ""):
  function filter_songs (line 2677) | def filter_songs(

FILE: maestro/jit_funcs.py
  function lerp (line 22) | def lerp(start, stop, t):
  function bin_average (line 27) | def bin_average(arr: np.ndarray, n, include_remainder=False, func=None):
  function render (line 46) | def render(

FILE: maestro/mac_presence.py
  class AppDelegate (line 32) | class AppDelegate(NSObject):  # so Python doesn't bounce in the dock
    method applicationDidFinishLaunching_ (line 33) | def applicationDidFinishLaunching_(self, _aNotification):
    method sayHello_ (line 36) | def sayHello_(self, _sender):
  function app_helper_loop (line 39) | def app_helper_loop():
  class MockQueue (line 57) | class MockQueue:  # enable testing this file
    method put (line 58) | def put(self, *args, **kwargs):
    method empty (line 61) | def empty(self):
  class MockInt (line 65) | class MockInt:
    method __init__ (line 66) | def __init__(self) -> None:
    method value (line 70) | def value(self):
    method value (line 74) | def value(self, value):
  class MacNowPlaying (line 78) | class MacNowPlaying:
    method __init__ (line 79) | def __init__(self):
    method play_handler (line 131) | def play_handler(self, _event):
    method pause_handler (line 140) | def pause_handler(self, _event):
    method toggle_handler (line 149) | def toggle_handler(self, _event):
    method next_handler (line 157) | def next_handler(self, _event):
    method prev_handler (line 164) | def prev_handler(self, _event):
    method seek_backward_handler (line 171) | def seek_backward_handler(self, _event):
    method seek_forward_handler (line 178) | def seek_forward_handler(self, _event):
    method change_position_handler (line 185) | def change_position_handler(self, event):
    method stop (line 191) | def stop(self):
    method pause (line 198) | def pause(self):
    method resume (line 205) | def resume(self):
    method update (line 212) | def update(self):

FILE: maestro/main.py
  function _play (line 29) | def _play(
  function cli (line 786) | def cli(ctx: click.Context):
  function add (line 1004) | def add(
  function remove (line 1369) | def remove(args, force, tag):
  function tag_ (line 1431) | def tag_(songs, tags):
  function untag (line 1456) | def untag(songs, tags, all_, force):
  function play (line 1626) | def play(
  function rename (line 1810) | def rename(original, new_name, renaming_tag):
  function search (line 1850) | def search(phrase, searching_for_tags):
  function list_ (line 2013) | def list_(
  function entry (line 2288) | def entry(songs, year):
  function recommend (line 2320) | def recommend(song, title):
  function clips_ (line 2373) | def clips_(songs: tuple[helpers.Song]):
  function clip_ (line 2425) | def clip_(song: helpers.Song, name, start, end, editor):
  function unclip (line 2537) | def unclip(songs: tuple[helpers.Song], names, all_, force):
  function set_clip (line 2621) | def set_clip(songs: tuple[helpers.Song], name, force):
  function metadata (line 2668) | def metadata(songs: tuple[helpers.Song], pairs, artist, album, album_art...
  function dir_ (line 2750) | def dir_(directory):
  function version (line 2777) | def version():
  function login (line 2787) | def login(username):
  function logout (line 2806) | def logout(force):
  function signup (line 2834) | def signup(username, login_):
  function clear_logs (line 2849) | def clear_logs():
  function download_ffmpeg (line 2865) | def download_ffmpeg():
  function migrate (line 2876) | def migrate():
  function format_data (line 2949) | def format_data(indent: int):
  function lyrics_ (line 3021) | def lyrics_(
  function transliterate (line 3269) | def transliterate(songs, lang, override, force):
  function translate (line 3444) | def translate(songs, save_, from_langs, to_lang, force, remove_):
  function user (line 3596) | def user():

FILE: scripts/custom_album_art.py
  function yt_embed_artwork (line 22) | def yt_embed_artwork(path_, yt_dlp_info):

FILE: setup.py
  function read_requirements (line 15) | def read_requirements(path: str) -> list[str]:
  function main (line 44) | def main() -> None:
Condensed preview — 27 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (463K chars).
[
  {
    "path": ".github/workflows/cross-build.yml",
    "chars": 4602,
    "preview": "name: Cross-Platform Build\n\non:\n  push:\n    branches:\n      - dev\n\njobs:\n  pyinstaller-build-windows:\n    runs-on: windo"
  },
  {
    "path": ".gitignore",
    "chars": 193,
    "preview": ".vscode\nbuild\n*.egg-info\n**/*.log\n*.egg\n__pycache__\n*.code-workspace\n**/*.env\n.pylintrc\n\ntodo.md\ntest_songs/\nsong_info.t"
  },
  {
    "path": "LICENSE",
    "chars": 1058,
    "preview": "Copyright © 2022 Prajwal Vandana\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this s"
  },
  {
    "path": "install-scripts/mac",
    "chars": 419,
    "preview": "#!/bin/sh\n\nMAESTRO_BUNDLE_LOC=/usr/local/bin/maestro-bundle\nMAESTRO_SYMLINK_LOC=/usr/local/bin/maestro\n\n# Make maestro e"
  },
  {
    "path": "install-scripts/ubuntu",
    "chars": 1528,
    "preview": "#!/bin/sh\n\nPREINSTALL_LOC=.\nMAESTRO_BUNDLE_LOC=/usr/local/bin/maestro-bundle\nMAESTRO_SYMLINK_LOC=/usr/local/bin/maestro\n"
  },
  {
    "path": "install-scripts/windows.nsi",
    "chars": 1275,
    "preview": "!define PRODUCT_NAME \"maestro-cli\"\n\nOutFile \"maestro-installer.exe\"\n\nSection\n    ; Set installation directory\n    SetOut"
  },
  {
    "path": "maestro/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "maestro/__main__.py",
    "chars": 292,
    "preview": "import sys\n\nfrom multiprocessing import freeze_support\n\nfrom maestro.main import cli\n\n\nif __name__ == \"__main__\":\n    # "
  },
  {
    "path": "maestro/__version__.py",
    "chars": 17,
    "preview": "VERSION = \"2.0.6\""
  },
  {
    "path": "maestro/config.py",
    "chars": 6159,
    "preview": "import os\n\nfrom datetime import date, datetime\nfrom urllib.parse import urljoin\n\n\n# region constants\n\nDISCORD_ID = 10390"
  },
  {
    "path": "maestro/helpers.py",
    "chars": 88635,
    "preview": "# region imports\n\nimport atexit\nimport curses\nimport logging\nimport os\nimport subprocess\nimport threading\n\nlogging.disab"
  },
  {
    "path": "maestro/icon.py",
    "chars": 146639,
    "preview": "img = b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x08\\x06\\x00\\x00\\x00\\x7f\\x1d+\\x83\\x00\\x00\\x00"
  },
  {
    "path": "maestro/jit_funcs.py",
    "chars": 3314,
    "preview": "import warnings\n\nimport numpy as np\n\nfrom maestro import config\nfrom maestro.config import print_to_logfile\n\ntry:\n    fr"
  },
  {
    "path": "maestro/mac_presence.py",
    "chars": 9369,
    "preview": "# BIG thanks to @othalan on StackOverflow for this\n# adapted from https://stackoverflow.com/questions/69965175/pyobjc-ac"
  },
  {
    "path": "maestro/main.py",
    "chars": 129915,
    "preview": "# pylint: disable=possibly-used-before-assignment\n\n# region imports\nimport curses\nimport multiprocessing\nimport os\nimpor"
  },
  {
    "path": "readme.md",
    "chars": 10511,
    "preview": "# maestro\n[![PyPI downloads](https://static.pepy.tech/badge/maestro-music)](https://pepy.tech/project/maestro-music) [!["
  },
  {
    "path": "requirements.txt",
    "chars": 1319,
    "preview": "click  # CLI handling\njust_playback  # Audio playback\nmusic-tag  # Metadata handling\npillow  # Image processing (require"
  },
  {
    "path": "scripts/add_album_art.py",
    "chars": 1075,
    "preview": "\"\"\"\nAdd album art to songs that don't have it, using the title of the song to search\nSpotify for the album art. Can fail"
  },
  {
    "path": "scripts/custom_album_art.py",
    "chars": 2394,
    "preview": "\"\"\"\nUsage: python custom_album_art.py <path_to_songs_directory>\n\"\"\"\n\nimport json\nimport os\nimport subprocess\nimport sys\n"
  },
  {
    "path": "scripts/rename_tracktitles.py",
    "chars": 547,
    "preview": "\"\"\"\nRename the 'tracktitle' metadata property of all songs to the name of the song\nfile. Sufficiently new versions of ma"
  },
  {
    "path": "setup.py",
    "chars": 2245,
    "preview": "from os.path import normpath\nimport re\n\nfrom setuptools import setup, find_packages\n\n\nd = {}\nwith open(normpath(\"maestro"
  },
  {
    "path": "specs/maestro-mac.spec",
    "chars": 1458,
    "preview": "# -*- mode: python ; coding: utf-8 -*-\nfrom PyInstaller.utils.hooks import collect_all\n\ndatas = []\nbinaries = []\nhiddeni"
  },
  {
    "path": "specs/maestro-ubuntu.spec",
    "chars": 1685,
    "preview": "# -*- mode: python ; coding: utf-8 -*-\nfrom PyInstaller.utils.hooks import collect_all\n\ndatas = []\nbinaries = []\nhiddeni"
  },
  {
    "path": "specs/maestro-windows.spec",
    "chars": 1458,
    "preview": "# -*- mode: python ; coding: utf-8 -*-\nfrom PyInstaller.utils.hooks import collect_all\n\ndatas = []\nbinaries = []\nhiddeni"
  },
  {
    "path": "uninstall-scripts/unix",
    "chars": 565,
    "preview": "#!/bin/sh\n\nMAESTRO_BUNDLE_LOC=/usr/local/bin/maestro-bundle\nMAESTRO_SYMLINK_LOC=/usr/local/bin/maestro\n\n# Remove bundle "
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the PrajwalVandana/maestro-cli GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 27 files (406.9 KB), approximately 140.9k tokens, and a symbol index with 206 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!