[
  {
    "path": ".fvmrc",
    "content": "{\n  \"flutter\": \"3.41.4\"\n}"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report\ndescription: Report a bug in the app (stable or beta)\nlabels: [\"bug\"]\nbody:\n  - type: dropdown\n    id: channel\n    attributes:\n      label: Release channel\n      description: Which version are you using?\n      options:\n        - Stable\n        - Beta\n    validations:\n      required: true\n\n  - type: input\n    id: version\n    attributes:\n      label: App version\n      placeholder: e.g. 2.0.11\n    validations:\n      required: true\n\n  - type: dropdown\n    id: source\n    attributes:\n      label: Install source\n      options:\n        - GitHub Releases\n        - IzzyOnDroid (F-Droid)\n    validations:\n      required: true\n\n  - type: input\n    id: android\n    attributes:\n      label: Android version\n      placeholder: e.g. Android 14\n    validations:\n      required: true\n\n  - type: textarea\n    id: steps\n    attributes:\n      label: Steps to reproduce\n    validations:\n      required: true\n\n  - type: textarea\n    id: expected\n    attributes:\n      label: Expected behavior\n    validations:\n      required: true\n\n  - type: textarea\n    id: actual\n    attributes:\n      label: Actual behavior\n    validations:\n      required: true\n\n  - type: textarea\n    id: regression\n    attributes:\n      label: Regression information (beta only)\n      description: Did this work correctly in the latest stable version?\n      placeholder: Yes / No / Not sure\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\ncontact_links:\n  - name: Questions and support\n    url: https://github.com/sheikhhaziq/gyawun_music/discussions\n    about: Please use Discussions for questions and general support.\n"
  },
  {
    "path": ".github/workflows/auto-label-issues.yml",
    "content": "name: Auto-label issues by release channel\n\non:\n  issues:\n    types: [opened]\n\npermissions:\n  issues: write\n\njobs:\n  label:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Auto-label by release channel\n        uses: actions/github-script@v7\n        with:\n          script: |\n            const body = context.payload.issue.body || \"\";\n\n            function getSectionValue(title) {\n              const regex = new RegExp(\n                `###\\\\s+${title}[\\\\s\\\\S]*?\\\\n\\\\n([^#]+)`,\n                \"i\"\n              );\n              const match = body.match(regex);\n              return match ? match[1].trim() : null;\n            }\n\n            const channel = getSectionValue(\"Release channel\");\n\n            if (!channel) return;\n\n            const labels = [];\n\n            if (channel.toLowerCase().startsWith(\"stable\")) {\n              labels.push(\"stable\");\n            }\n\n            if (channel.toLowerCase().startsWith(\"beta\")) {\n              labels.push(\"beta\");\n            }\n\n            if (!labels.length) return;\n\n            await github.rest.issues.addLabels({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              issue_number: context.issue.number,\n              labels,\n            });\n"
  },
  {
    "path": ".github/workflows/beta-release.yml",
    "content": "name: Beta Release\n\non:\n  push:\n    tags:\n      - 'v*-beta.*'\n\njobs:\n  beta-release:\n    if: \"contains(github.ref, '-beta.')\"\n    runs-on: ubuntu-latest\n    outputs:\n      release_tag: ${{ steps.app_version.outputs.version }}\n      release_channel: 'beta'\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Validate tag is on beta branch\n        run: |\n          if ! git branch -r --contains \"${{ github.ref_name }}\" | grep -q 'origin/beta'; then\n            echo \"❌ Tag ${{ github.ref_name }} is not on the beta branch\"\n            exit 1\n          fi\n          echo \"✅ Tag is on beta branch\"\n\n      - name: Read FVM version\n        id: fvm_version\n        run: |\n          FVM_VERSION=$(jq -r '.flutterSdkVersion' .fvm/fvm_config.json)\n          echo \"version=$FVM_VERSION\" >> $GITHUB_OUTPUT\n          echo \"Flutter version from FVM: $FVM_VERSION\"\n\n      - name: Read and validate beta version\n        id: app_version\n        run: |\n          TAG=\"${{ github.ref_name }}\"       # e.g. v1.2.0-beta.1\n          VERSION=\"${TAG#v}\"                  # e.g. 1.2.0-beta.1\n\n          if [[ ! \"$VERSION\" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+-beta\\.[0-9]+$ ]]; then\n            echo \"❌ Tag must follow vX.Y.Z-beta.N format, got: $TAG\"\n            exit 1\n          fi\n\n          VERSION_CODE=$(grep '^version:' pubspec.yaml | awk '{print $2}' | cut -d'+' -f2)\n\n          if [[ -z \"$VERSION_CODE\" || \"$VERSION_CODE\" == \"0\" ]]; then\n            echo \"❌ pubspec.yaml must have a valid build number (not 0)\"\n            exit 1\n          fi\n\n          sed -i \"s/^version:.*/version: $VERSION+$VERSION_CODE/\" pubspec.yaml\n\n          echo \"version=$VERSION\" >> $GITHUB_OUTPUT\n          echo \"build=$VERSION_CODE\" >> $GITHUB_OUTPUT\n          echo \"✅ Beta version: $VERSION+$VERSION_CODE\"\n\n      - name: Setup Flutter with FVM\n        uses: kuhnroyal/flutter-fvm-config-action/setup@v3\n\n      - name: Decode keystore\n        run: |\n          echo \"${{ secrets.KEYSTORE_BASE64 }}\" | base64 --decode > android/app/keystore.jks\n\n      - name: Create key.properties\n        run: |\n          cat > android/key.properties << EOF\n          storePassword=${{ secrets.STORE_PASSWORD }}\n          keyPassword=${{ secrets.KEY_PASSWORD }}\n          keyAlias=${{ secrets.KEY_ALIAS }}\n          storeFile=keystore.jks\n          EOF\n\n      - name: Install dependencies\n        run: flutter pub get\n\n      - name: Generate Splash Screen\n        run: dart run flutter_native_splash:create\n\n      - name: Build Android APK (Beta - Universal)\n        run: flutter build apk --flavor beta --release\n\n      - name: Build Android APK (Beta - Split per ABI)\n        run: flutter build apk --flavor beta --release --split-per-abi\n\n      - name: Rename build artifacts\n        run: |\n          mkdir -p releases\n          V=${{ steps.app_version.outputs.version }}\n\n          cp build/app/outputs/flutter-apk/app-beta-release.apk \\\n             releases/gyawun-beta-v${V}-universal.apk\n\n          cp build/app/outputs/flutter-apk/app-arm64-v8a-beta-release.apk \\\n             releases/gyawun-beta-v${V}-arm64-v8a.apk\n\n          cp build/app/outputs/flutter-apk/app-armeabi-v7a-beta-release.apk \\\n             releases/gyawun-beta-v${V}-armeabi-v7a.apk\n\n          cp build/app/outputs/flutter-apk/app-x86_64-beta-release.apk \\\n             releases/gyawun-beta-v${V}-x86_64.apk\n\n      - name: Create Beta Pre-Release\n        uses: softprops/action-gh-release@v1\n        with:\n          tag_name: ${{ github.ref_name }}\n          name: Beta v${{ steps.app_version.outputs.version }}\n          prerelease: true\n          draft: false\n          body: |\n            ## 🧪 Beta Release v${{ steps.app_version.outputs.version }}\n\n            ⚠️ **This is a BETA release.**  \n            For testing purposes – may contain bugs.\n\n            ---\n\n            ### 🔖 Build Information\n            - **Channel:** Beta\n            - **Version:** `${{ steps.app_version.outputs.version }}`\n            - **Build Number:** `${{ steps.app_version.outputs.build }}`\n            - **Flutter SDK:** `${{ steps.fvm_version.outputs.version }}`\n            - **Branch:** `beta`\n\n            ---\n\n            ### 🧪 Testing Notes\n            - Beta quality – expect some bugs\n            - Please report issues on GitHub\n            - Backup your data before testing\n          files: |\n            releases/gyawun-beta-v${{ steps.app_version.outputs.version }}-universal.apk\n            releases/gyawun-beta-v${{ steps.app_version.outputs.version }}-arm64-v8a.apk\n            releases/gyawun-beta-v${{ steps.app_version.outputs.version }}-armeabi-v7a.apk\n            releases/gyawun-beta-v${{ steps.app_version.outputs.version }}-x86_64.apk\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".github/workflows/branch-protection-check.yml",
    "content": "name: Branch Protection Check\n\non:\n  pull_request:\n    branches:\n      - stable\n      - beta\n\njobs:\n  check-pr-source:\n    runs-on: ubuntu-latest\n    \n    steps:\n      - name: Check PR to stable is from beta\n        if: github.base_ref == 'stable'\n        run: |\n          if [ \"${{ github.head_ref }}\" != \"beta\" ]; then\n            echo \"❌ Error: Pull requests to 'stable' branch must come from 'beta' branch only.\"\n            echo \"Current source: ${{ github.head_ref }}\"\n            exit 1\n          fi\n          echo \"✅ PR is from beta branch - validation passed\"\n      \n      - name: Info for beta PRs\n        if: github.base_ref == 'beta'\n        run: |\n          echo \"✅ Pull request to beta branch from: ${{ github.head_ref }}\"\n          echo \"This will trigger a beta pre-release on merge.\""
  },
  {
    "path": ".github/workflows/stable-release.yml",
    "content": "name: Stable Release\n\non:\n  push:\n    tags:\n      - 'v[0-9]*.[0-9]*.[0-9]*'\n\njobs:\n  stable-release:\n    if: \"!contains(github.ref, '-beta.')\"\n    runs-on: ubuntu-latest\n    outputs:\n      release_tag: ${{ steps.app_version.outputs.version }}\n      release_channel: 'stable'\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Validate tag is on stable branch\n        run: |\n          if ! git branch -r --contains \"${{ github.ref_name }}\" | grep -q 'origin/stable'; then\n            echo \"❌ Tag ${{ github.ref_name }} is not on the stable branch\"\n            exit 1\n          fi\n          echo \"✅ Tag is on stable branch\"\n\n      - name: Read FVM version\n        id: fvm_version\n        run: |\n          FVM_VERSION=$(jq -r '.flutterSdkVersion' .fvm/fvm_config.json)\n          echo \"version=$FVM_VERSION\" >> $GITHUB_OUTPUT\n          echo \"Flutter version from FVM: $FVM_VERSION\"\n\n      - name: Read and validate production version\n        id: app_version\n        run: |\n          TAG=\"${{ github.ref_name }}\"       # e.g. v1.2.0\n          VERSION=\"${TAG#v}\"                  # e.g. 1.2.0\n\n          if [[ ! \"$VERSION\" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then\n            echo \"❌ Tag must follow vX.Y.Z format (no beta suffix), got: $TAG\"\n            exit 1\n          fi\n\n          VERSION_CODE=$(grep '^version:' pubspec.yaml | awk '{print $2}' | cut -d'+' -f2)\n\n          if [[ -z \"$VERSION_CODE\" || \"$VERSION_CODE\" == \"0\" ]]; then\n            echo \"❌ pubspec.yaml must have a valid build number (not 0)\"\n            exit 1\n          fi\n\n          sed -i \"s/^version:.*/version: $VERSION+$VERSION_CODE/\" pubspec.yaml\n\n          echo \"version=$VERSION\" >> $GITHUB_OUTPUT\n          echo \"build=$VERSION_CODE\" >> $GITHUB_OUTPUT\n          echo \"✅ Stable version: $VERSION+$VERSION_CODE\"\n\n      - name: Setup Flutter with FVM\n        uses: kuhnroyal/flutter-fvm-config-action/setup@v3\n\n      - name: Decode keystore\n        run: |\n          echo \"${{ secrets.KEYSTORE_BASE64 }}\" | base64 --decode > android/app/keystore.jks\n\n      - name: Create key.properties\n        run: |\n          cat > android/key.properties << EOF\n          storePassword=${{ secrets.STORE_PASSWORD }}\n          keyPassword=${{ secrets.KEY_PASSWORD }}\n          keyAlias=${{ secrets.KEY_ALIAS }}\n          storeFile=keystore.jks\n          EOF\n\n      - name: Install dependencies\n        run: flutter pub get\n\n      - name: Generate Splash Screen\n        run: dart run flutter_native_splash:create\n\n      - name: Build Android APK (Stable - Universal)\n        run: flutter build apk --flavor stable --release\n\n      - name: Build Android APK (Stable - Split per ABI)\n        run: flutter build apk --flavor stable --release --split-per-abi\n\n      - name: Rename build artifacts\n        run: |\n          mkdir -p releases\n          V=${{ steps.app_version.outputs.version }}\n\n          cp build/app/outputs/flutter-apk/app-stable-release.apk \\\n             releases/gyawun-v${V}-universal.apk\n\n          cp build/app/outputs/flutter-apk/app-arm64-v8a-stable-release.apk \\\n             releases/gyawun-v${V}-arm64-v8a.apk\n\n          cp build/app/outputs/flutter-apk/app-armeabi-v7a-stable-release.apk \\\n             releases/gyawun-v${V}-armeabi-v7a.apk\n\n          cp build/app/outputs/flutter-apk/app-x86_64-stable-release.apk \\\n             releases/gyawun-v${V}-x86_64.apk\n\n      - name: Create Stable Release\n        uses: softprops/action-gh-release@v1\n        with:\n          tag_name: ${{ github.ref_name }}\n          name: v${{ steps.app_version.outputs.version }}\n          prerelease: false\n          draft: false\n          body: |\n            ## ✅ Stable Release v${{ steps.app_version.outputs.version }}\n\n            **This is a stable production release.**\n\n            ---\n\n            ### 🔖 Build Information\n            - **Channel:** Stable\n            - **Version:** `${{ steps.app_version.outputs.version }}`\n            - **Build Number:** `${{ steps.app_version.outputs.build }}`\n            - **Flutter SDK:** `${{ steps.fvm_version.outputs.version }}`\n            - **Branch:** `stable`\n\n            ---\n\n            ### 📝 Release Notes\n            _See commit history for changes._\n          files: |\n            releases/gyawun-v${{ steps.app_version.outputs.version }}-universal.apk\n            releases/gyawun-v${{ steps.app_version.outputs.version }}-arm64-v8a.apk\n            releases/gyawun-v${{ steps.app_version.outputs.version }}-armeabi-v7a.apk\n            releases/gyawun-v${{ steps.app_version.outputs.version }}-x86_64.apk\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n**/Gyawun\n**/dist\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n\n**/android/**/gradle-wrapper.jar\n**/android/.gradle\n**/android/captures/\n**/android/gradlew\n**/android/gradlew.bat\n**/android/local.properties\n**/android/key.properties\n**/android/**/GeneratedPluginRegistrant.java\n\n/linux\n/macos\n/windows\n/ios\n/web\n.flatpak-builder\n\n.secrets\n\n# FVM Version Cache\n.fvm/\nrepo"
  },
  {
    "path": ".metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: \"adc901062556672b4138e18a4dc62a4be8f4b3c2\"\n  channel: \"stable\"\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2\n      base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2\n    - platform: linux\n      create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2\n      base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n    // Use IntelliSense to learn about possible attributes.\n    // Hover to view descriptions of existing attributes.\n    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"Flutter (Beta Flavor)\",\n            \"request\": \"launch\",\n            \"type\": \"dart\",\n            \"args\": [\n                \"--flavor\",\n                \"beta\"\n            ]\n        },\n        {\n            \"name\": \"Flutter (Production Flavor)\",\n            \"request\": \"launch\",\n            \"type\": \"dart\",\n            \"args\": [\n                \"--flavor\",\n                \"production\"\n            ]\n        }\n    ]\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"cmake.sourceDirectory\": \"/home/sheikh-haziq/Development/Jhelum/gyawun-app/linux\",\n  \"dart.flutterSdkPath\": \".fvm/versions/3.41.4\"\n}"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing Guide\n\nThank you for your interest in contributing!  \nThis repository follows a **strict, staged branch workflow** to maintain stability while allowing controlled development and testing.\n\nPlease read this document fully before opening a pull request.\n\n---\n\n## 📌 Repository Overview\n\n- **Project type:** Flutter application\n- **Primary maintainer:** Solo maintainer\n- **Default branch:** `main`\n\n---\n\n## 🌿 Branching Model\n\nThis project uses **three long-lived branches**:\n\n### `main` — Development\n\n- All **active development** happens here\n- Used by the maintainer for **manual testing**\n- May be unstable or incomplete\n- **All pull requests must target `main`**\n\n✅ This is the **only branch contributors should use**.\n\n---\n\n### `beta` — Pre-release\n\n- Receives changes **only from `main`**\n- Used for wider testing and early access\n- Merging into `beta`:\n  - Automatically publishes a **beta GitHub release**\n  - Version format: `x.y.z-beta.n`\n\n🚫 Contributors should **not** open PRs directly to `beta`.\n\n---\n\n### `stable` — Stable\n\n- Production-ready code only\n- Receives changes **only from `beta`**\n- Merging into `stable`:\n  - Automatically publishes a **stable GitHub release**\n  - Version format: `x.y.z`\n\n🚫 Pull requests to `stable` are not accepted.\n\n---\n\n## 🔁 Contribution Workflow\n\n### 1. Fork the Repository\n\n```bash\ngit clone https://github.com/<your-username>/<repo>.git\ncd <repo>\ngit checkout main\n```\n\n---\n\n### 2. Create a Feature or Fix Branch\n\n```bash\ngit checkout -b feature/short-description\n# or\ngit checkout -b fix/short-description\n```\n\n---\n\n### 3. Make Your Changes\n\n- Keep changes **focused and minimal**\n- Follow the existing project structure\n- Avoid unrelated refactors or dependency changes\n\n---\n\n### 4. Basic Checks\n\nBefore opening a PR, ensure:\n\n```bash\nflutter analyze\nflutter test\n```\n\n> Final testing is performed **manually by the maintainer** on the `main` branch.\n\n---\n\n### 5. Open a Pull Request\n\n- **Base branch:** `main`\n- **Compare branch:** your feature/fix branch\n- Clearly explain:\n  - What the change does\n  - Why it is needed\n  - Any user-facing or breaking changes\n\n🚫 PRs targeting `beta` or `stable` will be **closed without review**.\n\n---\n\n## 🚀 Release Flow (For Reference)\n\n| Action | Result |\n|------|-------|\n| Merge PR → `main` | Change queued for manual testing |\n| Merge `main` → `beta` | Beta release published |\n| Merge `beta` → `stable` | Stable release published |\n\nContributors do **not** need to manage versions, tags, or releases.\n\n---\n\n## 🐞 Issues & Discussions\n\n- **Bug reports & feature requests:** GitHub Issues\n- **Questions & general discussion:** GitHub Discussions\n- Old or unstructured issues may be closed during maintenance cleanups\n\n---\n\n## 📐 Code Guidelines\n\n- Follow Flutter and Dart best practices\n- Prefer clarity over cleverness\n- Avoid introducing new dependencies without discussion\n- Platform-specific logic should be well-isolated\n\n---\n\n## 🔒 Maintainer Notes\n\n- This project is mantained by **Two maintainers**\n- Reviews and merges may take time\n- Large or architectural changes should be discussed **before** implementation\n\n---\n\n## ❤️ Thank You\n\nEvery contribution—code, issues, or documentation—helps improve the project.\n\nHappy contributing! 🚀\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n\n![Gyawun Music Banner](cover.png)\n\n# 🎵 Gyawun Music\n\n**Where Music Knows No Bounds**\n\n*Experience the freedom of unlimited, ad-free music streaming from YouTube Music*\n\n[![GitHub release](https://img.shields.io/github/v/release/sheikhhaziq/gyawun_music?style=for-the-badge)](https://github.com/sheikhhaziq/gyawun_music/releases/latest)\n[![License](https://img.shields.io/badge/license-GPL--3.0-blue?style=for-the-badge)](LICENSE)\n[![Stars](https://img.shields.io/github/stars/sheikhhaziq/gyawun_music?style=for-the-badge)](https://github.com/sheikhhaziq/gyawun_music/stargazers)\n[![Downloads](https://img.shields.io/github/downloads/sheikhhaziq/gyawun_music/total?style=for-the-badge)](https://github.com/sheikhhaziq/gyawun_music/releases)\n\n[![Join us on Telegram](https://img.shields.io/badge/Join_us_on_Telegram-0088cc?style=for-the-badge&logo=telegram)](https://t.me/jhelumcorp)\n\n[Download](https://github.com/sheikhhaziq/gyawun_music/releases/latest) • [Website](https://gyawunmusic.vercel.app/) • [Contribute](CONTRIBUTING.md) • [Report Bug](https://github.com/sheikhhaziq/gyawun_music/issues)\n\n---\n\n</div>\n\n## ✨ What Makes Gyawun Special\n\nGyawun isn't just another music player—it's your gateway to an uninterrupted musical journey. Built with Flutter and powered by YouTube Music's vast library, Gyawun brings you millions of songs without the noise of advertisements, subscription walls, or unnecessary restrictions.\n\nImmerse yourself in crystal-clear audio quality, discover artists from every corner of the globe, and let the rhythm carry you wherever you go. With Gyawun, music flows freely, as it should.\n\n## 🚀 Features\n\n### Core Experience\n- 🎧 **Unlimited Streaming** - Access millions of songs from YouTube Music\n- 🚫 **Ad-Free Playback** - Enjoy uninterrupted listening, always\n- 🎼 **High-Quality Audio** - Adjustable audio quality to suit your preference\n- 🌙 **Background Playback** - Keep the music playing while you multitask\n- 💾 **Offline Downloads** - Save your favorite tracks for offline listening\n- ⏰ **Sleep Timer** - Fall asleep to your favorite music with auto-stop\n\n### Discovery & Organization\n- 🔍 **Smart Search** - Find songs, artists, and playlists effortlessly\n- ❤️ **Favorites** - Build your personal collection of beloved tracks\n- 📝 **Custom Playlists** - Curate and organize music your way\n- 🔄 **Flexible Queue** - Reorder songs on the fly\n- 📜 **Listening History** - Never lose track of what you've played\n- 📝 **Lyrics** - Sing along with synchronized lyrics powered by LRCLib\n\n### Personalization\n- 🎨 **Material You** - Dynamic theming that adapts to your style\n- 🌓 **Dark Mode** - Easy on the eyes, day or night\n- 🎚️ **Audio Enhancement** - Built-in equalizer and loudness enhancer\n- 🌍 **Multi-Language Support** - Available in multiple languages\n- 🔄 **Cross-Device Sync** - Sync recommendations across devices using visitor ID\n- 🎙️ **Podcast Support** - Stream your favorite podcasts alongside music\n\n## 📱 Installation\n\n### Android\n\nDownload the latest APK from our [releases page](https://github.com/sheikhhaziq/gyawun_music/releases/latest) and install it on your device.\n\n### Building from Source\n\n```bash\n# Clone the repository\ngit clone https://github.com/sheikhhaziq/gyawun_music.git\ncd gyawun_music\n\n# This project uses FVM for Flutter version management\n# Install FVM if you haven't already: https://fvm.app/docs/getting_started/installation\n# The Flutter version is specified in .fvmrc\n\n# Install the correct Flutter version\nfvm install\n\n# Use the project's Flutter version\nfvm use\n\n# Install dependencies\nfvm flutter pub get\n\n# Run the app\nfvm flutter run\n```\n\n## 🎯 Roadmap\n\nWe're constantly evolving. Here's what we're working on:\n\n- [ ] iOS Support\n- [ ] Desktop Applications (Windows, macOS, Linux)\n- [ ] Advanced Audio Controls\n- [ ] Social Features (Share playlists, collaborative playlists)\n- [ ] Import/Export Playlists\n- [ ] Enhanced Podcast Features\n\n## 🤝 Contributing\n\nGyawun is built by music lovers, for music lovers. We welcome contributions from developers, designers, translators, and enthusiasts of all skill levels.\n\nWhether you want to fix bugs, add features, improve documentation, or translate the app into your language, your contributions make Gyawun better for everyone.\n\nPlease read our [Contributing Guidelines](CONTRIBUTING.md) to get started.\n\n## 👥 Community\n\nJoin our growing community of music enthusiasts:\n\n- 💬 [Telegram Group](https://t.me/jhelumcorp) - Chat, share music, and get support\n- 🐛 [Issue Tracker](https://github.com/sheikhhaziq/gyawun_music/issues) - Report bugs\n- 💡 [Discussions](https://github.com/sheikhhaziq/gyawun_music/discussions) - Request features, share ideas and feedback\n\n## 🙏 Acknowledgments\n\nA heartfelt thank you to our incredible contributors who have helped shape Gyawun:\n\n[![Contributors](https://contrib.rocks/image?repo=sheikhhaziq/gyawun_music)](https://github.com/sheikhhaziq/gyawun_music/graphs/contributors)\n\n## ⚖️ Legal\n\n**Disclaimer:** This project and its contents are not affiliated with, funded, authorized, endorsed by, or in any way associated with YouTube, Google LLC, or any of its affiliates and subsidiaries. Any trademark, service mark, trade name, or other intellectual property rights used in this project are owned by the respective owners.\n\nGyawun is an open-source project created for educational and personal use. Users are responsible for ensuring their usage complies with YouTube's Terms of Service and applicable laws in their jurisdiction.\n\n## 📄 License\n\nThis project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details.\n\n---\n\n<div align=\"center\">\n\n**Made with ❤️ by the Gyawun community**\n\n*Let the music play on*\n\n[⬆ Back to Top](#-gyawun-music)\n\n</div>"
  },
  {
    "path": "analysis_options.yaml",
    "content": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n# The issues identified by the analyzer are surfaced in the UI of Dart-enabled\n# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be\n# invoked from the command line by running `flutter analyze`.\n\n# The following line activates a set of recommended lints for Flutter apps,\n# packages, and plugins designed to encourage good coding practices.\ninclude: package:flutter_lints/flutter.yaml\n\nlinter:\n  # The lint rules applied to this project can be customized in the\n  # section below to disable rules from the `package:flutter_lints/flutter.yaml`\n  # included above or to enable additional rules. A list of all available lints\n  # and their documentation is published at https://dart.dev/lints.\n  #\n  # Instead of disabling a lint rule for the entire project in the\n  # section below, it can also be suppressed for a single line of code\n  # or a specific dart file by using the `// ignore: name_of_lint` and\n  # `// ignore_for_file: name_of_lint` syntax on the line or in the file\n  # producing the lint.\n  rules:\n    # avoid_print: false  # Uncomment to disable the `avoid_print` rule\n    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n.cxx/\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/to/reference-keystore\nkey.properties\n**/*.keystore\n**/*.jks\n"
  },
  {
    "path": "android/app/build.gradle.kts",
    "content": "import java.io.FileInputStream\nimport java.util.Properties\n\nplugins {\n    id(\"com.android.application\")\n    id(\"org.jetbrains.kotlin.android\")\n    id(\"dev.flutter.flutter-gradle-plugin\")\n}\n\n/* ---------- local.properties ---------- */\n\nval localProperties = Properties()\nval localPropertiesFile = rootProject.file(\"local.properties\")\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.inputStream().use {\n        localProperties.load(it)\n    }\n}\n\n/* ---------- key.properties ---------- */\n\nval keystoreProperties = Properties()\nval keystorePropertiesFile = rootProject.file(\"key.properties\")\nif (keystorePropertiesFile.exists()) {\n    FileInputStream(keystorePropertiesFile).use {\n        keystoreProperties.load(it)\n    }\n}\n\nandroid {\n    namespace = \"com.jhelum.gyawun\"\n    compileSdk = flutter.compileSdkVersion\n    ndkVersion = flutter.ndkVersion\n\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_17\n        targetCompatibility = JavaVersion.VERSION_17\n    }\n\n    kotlinOptions {\n        jvmTarget = \"17\"\n    }\n\n    defaultConfig {\n        applicationId = \"com.jhelum.gyawun\"\n        minSdk = flutter.minSdkVersion\n        targetSdk = flutter.targetSdkVersion\n        versionCode = flutter.versionCode\n        versionName = flutter.versionName\n    }\n\n    /* ---------- Flavors ---------- */\n\n    flavorDimensions += \"default\"\n\n    productFlavors {\n        create(\"beta\") {\n            dimension = \"default\"\n            applicationIdSuffix = \".beta\"\n            resValue(\"string\", \"app_name\", \"Gyawun Music Beta\")\n        }\n\n        create(\"stable\") {\n            dimension = \"default\"\n            resValue(\"string\", \"app_name\", \"Gyawun Music\")\n        }\n    }\n\n    /* ---------- Signing ---------- */\n\n    signingConfigs {\n        create(\"release\") {\n            keyAlias = keystoreProperties[\"keyAlias\"] as String?\n            keyPassword = keystoreProperties[\"keyPassword\"] as String?\n            storeFile = keystoreProperties[\"storeFile\"]?.let { file(it) }\n            storePassword = keystoreProperties[\"storePassword\"] as String?\n        }\n    }\n\n    buildTypes {\n        getByName(\"release\") {\n            signingConfig = signingConfigs.getByName(\"release\")\n            ndk {\n                debugSymbolLevel = \"none\"\n            }\n        }\n    }\n\n    packaging {\n        jniLibs {\n            useLegacyPackaging = true\n        }\n    }\n}\n\nflutter {\n    source = \"../..\"\n}\n"
  },
  {
    "path": "android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "content": "<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <!-- ===== Permissions ===== -->\n\n    <!-- Android < 10 -->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n\n    <!-- Android 11+ (scoped storage exemption) -->\n    <uses-permission android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"/>\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\"/>\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK\"/>\n    <uses-permission android:name=\"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\"/>\n\n    <!-- ===== Queries MUST be here ===== -->\n    <queries>\n        <intent>\n            <action android:name=\"android.intent.action.PROCESS_TEXT\"/>\n            <data android:mimeType=\"text/plain\"/>\n        </intent>\n    </queries>\n\n    <!-- ===== Application ===== -->\n    <application\n        android:name=\"${applicationName}\"\n        android:label=\"@string/app_name\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:usesCleartextTraffic=\"true\"\n        android:enableOnBackInvokedCallback=\"true\"\n        android:requestLegacyExternalStorage=\"true\">\n\n        <activity\n            android:name=\"com.ryanheise.audioservice.AudioServiceActivity\"\n            android:exported=\"true\"\n            android:launchMode=\"singleTask\"\n            android:taskAffinity=\"\"\n            android:theme=\"@style/LaunchTheme\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:hardwareAccelerated=\"true\"\n            android:windowSoftInputMode=\"adjustResize\">\n\n            <meta-data\n                android:name=\"io.flutter.embedding.android.NormalTheme\"\n                android:resource=\"@style/NormalTheme\"/>\n\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n\n            <intent-filter>\n                <action android:name=\"android.intent.action.SEND\"/>\n                <category android:name=\"android.intent.category.DEFAULT\"/>\n                <data android:mimeType=\"text/plain\"/>\n            </intent-filter>\n\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\"/>\n                <category android:name=\"android.intent.category.DEFAULT\"/>\n                <category android:name=\"android.intent.category.BROWSABLE\"/>\n                <data android:scheme=\"https\"/>\n                <data android:host=\"music.youtube.com\"/>\n            </intent-filter>\n        </activity>\n\n        <service\n            android:name=\"com.ryanheise.audioservice.AudioService\"\n            android:foregroundServiceType=\"mediaPlayback\"\n            android:exported=\"true\"\n            tools:ignore=\"Instantiatable\">\n\n            <intent-filter>\n                <action android:name=\"android.media.browse.MediaBrowserService\"/>\n            </intent-filter>\n        </service>\n\n        <receiver\n            android:name=\"com.ryanheise.audioservice.MediaButtonReceiver\"\n            android:exported=\"true\"\n            tools:ignore=\"Instantiatable\">\n\n            <intent-filter>\n                <action android:name=\"android.intent.action.MEDIA_BUTTON\"/>\n            </intent-filter>\n        </receiver>\n\n        <meta-data\n            android:name=\"com.google.android.gms.car.application\"\n            android:resource=\"@xml/automotive_app_desc\"/>\n\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\"/>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/jhelum/gyawun/MainActivity.kt",
    "content": "package com.jhelum.gyawun\n\nimport io.flutter.embedding.android.FlutterActivity\n\nclass MainActivity : FlutterActivity()\n"
  },
  {
    "path": "android/app/src/main/res/anim/in_animation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@android:integer/config_shortAnimTime\"\n    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"\n    android:shareInterpolator=\"true\">\n\n    <alpha\n        android:fillAfter=\"true\"\n        android:fillBefore=\"true\"\n        android:fillEnabled=\"true\"\n        android:fromAlpha=\"0\"\n        android:toAlpha=\"1.0\" />\n\n    <translate android:fromYDelta=\"100%p\" />\n\n</set>"
  },
  {
    "path": "android/app/src/main/res/anim/out_animation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@android:integer/config_shortAnimTime\"\n    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"\n    android:shareInterpolator=\"true\">\n\n    <alpha\n        android:fillAfter=\"true\"\n        android:fillBefore=\"true\"\n        android:fillEnabled=\"true\"\n        android:fromAlpha=\"1.0\"\n        android:toAlpha=\"0\" />\n\n    <translate\n        android:fromYDelta=\"0%p\"\n        android:toYDelta=\"-100%p\" />\n\n</set>"
  },
  {
    "path": "android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <bitmap android:gravity=\"fill\" android:src=\"@drawable/background\"/>\n    </item>\n    <item>\n        <bitmap android:gravity=\"center\" android:src=\"@drawable/splash\"/>\n    </item>\n    <item>\n        <bitmap android:gravity=\"bottom\" android:src=\"@drawable/branding\"/>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/drawable-hdpi/audio_service_stop.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"36dp\"\n    android:height=\"36dp\"\n    android:viewportWidth=\"512\"\n    android:viewportHeight=\"512\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M289.94,256l95,-95A24,24 0,0 0,351 127l-95,95 -95,-95A24,24 0,0 0,127 161l95,95 -95,95A24,24 0,1 0,161 385l95,-95 95,95A24,24 0,0 0,385 351Z\"/>\n</vector>"
  },
  {
    "path": "android/app/src/main/res/drawable-mdpi/audio_service_stop.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"512\"\n    android:viewportHeight=\"512\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M289.94,256l95,-95A24,24 0,0 0,351 127l-95,95 -95,-95A24,24 0,0 0,127 161l95,95 -95,95A24,24 0,1 0,161 385l95,-95 95,95A24,24 0,0 0,385 351Z\"/>\n</vector>"
  },
  {
    "path": "android/app/src/main/res/drawable-night/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Dark theme splash screen with launcher icon -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@android:color/black\" /> <!-- Black background -->\n    <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/ic_launcher\" /> <!-- Use launcher icon -->\n    </item>\n</layer-list>"
  },
  {
    "path": "android/app/src/main/res/drawable-v21/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <bitmap android:gravity=\"fill\" android:src=\"@drawable/background\"/>\n    </item>\n    <item>\n        <bitmap android:gravity=\"center\" android:src=\"@drawable/splash\"/>\n    </item>\n    <item>\n        <bitmap android:gravity=\"bottom\" android:src=\"@drawable/branding\"/>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/drawable-xhdpi/audio_service_stop.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"48dp\"\n    android:height=\"48dp\"\n    android:viewportWidth=\"512\"\n    android:viewportHeight=\"512\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M289.94,256l95,-95A24,24 0,0 0,351 127l-95,95 -95,-95A24,24 0,0 0,127 161l95,95 -95,95A24,24 0,1 0,161 385l95,-95 95,95A24,24 0,0 0,385 351Z\"/>\n</vector>"
  },
  {
    "path": "android/app/src/main/res/drawable-xxhdpi/audio_service_stop.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"72dp\"\n    android:height=\"72dp\"\n    android:viewportWidth=\"512\"\n    android:viewportHeight=\"512\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M289.94,256l95,-95A24,24 0,0 0,351 127l-95,95 -95,-95A24,24 0,0 0,127 161l95,95 -95,95A24,24 0,1 0,161 385l95,-95 95,95A24,24 0,0 0,385 351Z\"/>\n</vector>"
  },
  {
    "path": "android/app/src/main/res/drawable-xxxhdpi/audio_service_stop.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"96dp\"\n    android:height=\"96dp\"\n    android:viewportWidth=\"512\"\n    android:viewportHeight=\"512\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M289.94,256l95,-95A24,24 0,0 0,351 127l-95,95 -95,-95A24,24 0,0 0,127 161l95,95 -95,95A24,24 0,1 0,161 385l95,-95 95,95A24,24 0,0 0,385 351Z\"/>\n</vector>"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <background android:drawable=\"@mipmap/ic_launcher_background\"/>\n  <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n  <monochrome android:drawable=\"@mipmap/ic_launcher_monochrome\"/>\n</adaptive-icon>"
  },
  {
    "path": "android/app/src/main/res/raw/keep.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\"\n  tools:keep=\"@drawable/*\" />"
  },
  {
    "path": "android/app/src/main/res/values/attrs.xml",
    "content": "<resources>\n    <declare-styleable name=\"AppWidgetAttrs\">\n        <attr name=\"appWidgetPadding\" format=\"dimension\" />\n        <attr name=\"appWidgetInnerRadius\" format=\"dimension\" />\n        <attr name=\"appWidgetRadius\" format=\"dimension\" />\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values/colors.xml",
    "content": "<resources>\n    <color name=\"light_blue_50\">#FFE1F5FE</color>\n    <color name=\"light_blue_200\">#FF81D4FA</color>\n    <color name=\"light_blue_600\">#FF039BE5</color>\n    <color name=\"light_blue_900\">#FF01579B</color>\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!--\nRefer to App Widget Documentation for margin information\nhttp://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout\n    -->\n    <dimen name=\"widget_margin\">0dp</dimen>\n\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#000000</color>\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"appwidget_text\">Play</string>\n    <string name=\"add_widget\">Add widget</string>\n    <string name=\"app_widget_description\">BlackHole Music</string>\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             Flutter draws its first frame -->\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n        <item name=\"android:forceDarkAllowed\">false</item>\n        <item name=\"android:windowFullscreen\">false</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">false</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowBackground\">@android:color/black</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n    </style>\n    <style name=\"Widget.Android.AppWidget.Container\" parent=\"android:Widget\">\n        <item name=\"android:id\">@android:id/background</item>\n        <item name=\"android:background\">?android:attr/colorBackground</item>\n    </style>\n    <style name=\"Widget.Android.AppWidget.InnerView\" parent=\"android:Widget\">\n        <item name=\"android:background\">?android:attr/colorBackground</item>\n        <item name=\"android:textColor\">?android:attr/textColorPrimary</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values/themes.xml",
    "content": "<resources>\n\n    <style name=\"Theme.Android.AppWidgetContainerParent\" parent=\"@android:style/Theme.DeviceDefault\">\n        <!-- Radius of the outer bound of widgets to make the rounded corners -->\n        <item name=\"appWidgetRadius\">16dp</item>\n        <!--\n        Radius of the inner view's bound of widgets to make the rounded corners.\n        It needs to be 8dp or less than the value of appWidgetRadius\n        -->\n        <item name=\"appWidgetInnerRadius\">8dp</item>\n    </style>\n\n    <style name=\"Theme.Android.AppWidgetContainer\" parent=\"Theme.Android.AppWidgetContainerParent\">\n        <!-- Apply padding to avoid the content of the widget colliding with the rounded corners -->\n        <item name=\"appWidgetPadding\">16dp</item>\n    </style>\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values-night/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n        <item name=\"android:forceDarkAllowed\">false</item>\n        <item name=\"android:windowFullscreen\">false</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">false</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-night-v31/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:forceDarkAllowed\">false</item>\n        <item name=\"android:windowFullscreen\">false</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">false</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n        <item name=\"android:windowSplashScreenBrandingImage\">@drawable/android12branding</item>\n        <item name=\"android:windowSplashScreenAnimatedIcon\">@drawable/android12splash</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-v31/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:forceDarkAllowed\">false</item>\n        <item name=\"android:windowFullscreen\">false</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">false</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n        <item name=\"android:windowSplashScreenBrandingImage\">@drawable/android12branding</item>\n        <item name=\"android:windowSplashScreenAnimatedIcon\">@drawable/android12splash</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/xml/automotive_app_desc.xml",
    "content": "<automotiveApp>\n    <uses name=\"media\"/>\n</automotiveApp>"
  },
  {
    "path": "android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "android/build/reports/problems/problems-report.html",
    "content": "<!DOCTYPE html>\n\n<html lang=\"en\">\n<head>\n    <!-- Required meta tags -->\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\n                    <style type=\"text/css\">\n                /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */\nhtml {\n    line-height: 1.15;\n    -ms-text-size-adjust: 100%;\n    -webkit-text-size-adjust: 100%\n}\n\nbody {\n    margin: 0\n}\n\narticle, aside, footer, header, nav, section {\n    display: block\n}\n\nh1 {\n    font-size: 2em;\n    margin: .67em 0\n}\n\nfigcaption, figure, main {\n    display: block\n}\n\nfigure {\n    margin: 1em 40px\n}\n\nhr {\n    box-sizing: content-box;\n    height: 0;\n    overflow: visible\n}\n\npre {\n    font-family: monospace, monospace;\n    font-size: 1em\n}\n\na {\n    background-color: transparent;\n    -webkit-text-decoration-skip: objects\n}\n\nabbr[title] {\n    border-bottom: none;\n    text-decoration: underline;\n    text-decoration: underline dotted\n}\n\nb, strong {\n    font-weight: inherit\n}\n\nb, strong {\n    font-weight: bolder\n}\n\ncode, kbd, samp {\n    font-family: monospace, monospace;\n    font-size: 1em\n}\n\ndfn {\n    font-style: italic\n}\n\nmark {\n    background-color: #ff0;\n    color: #000\n}\n\nsmall {\n    font-size: 80%\n}\n\nsub, sup {\n    font-size: 75%;\n    line-height: 0;\n    position: relative;\n    vertical-align: baseline\n}\n\nsub {\n    bottom: -.25em\n}\n\nsup {\n    top: -.5em\n}\n\naudio, video {\n    display: inline-block\n}\n\naudio:not([controls]) {\n    display: none;\n    height: 0\n}\n\nimg {\n    border-style: none\n}\n\nsvg:not(:root) {\n    overflow: hidden\n}\n\nbutton, input, optgroup, select, textarea {\n    font-family: sans-serif;\n    font-size: 100%;\n    line-height: 1.15;\n    margin: 0\n}\n\nbutton, input {\n    overflow: visible\n}\n\nbutton, select {\n    text-transform: none\n}\n\n[type=reset], [type=submit], button, html [type=button] {\n    -webkit-appearance: button\n}\n\n[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner {\n    border-style: none;\n    padding: 0\n}\n\n[type=button]:-moz-focusring, [type=reset]:-moz-focusring, [type=submit]:-moz-focusring, button:-moz-focusring {\n    outline: 1px dotted ButtonText\n}\n\nfieldset {\n    padding: .35em .75em .625em\n}\n\nlegend {\n    box-sizing: border-box;\n    color: inherit;\n    display: table;\n    max-width: 100%;\n    padding: 0;\n    white-space: normal\n}\n\nprogress {\n    display: inline-block;\n    vertical-align: baseline\n}\n\ntextarea {\n    overflow: auto\n}\n\n[type=checkbox], [type=radio] {\n    box-sizing: border-box;\n    padding: 0\n}\n\n[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button {\n    height: auto\n}\n\n[type=search] {\n    -webkit-appearance: textfield;\n    outline-offset: -2px\n}\n\n[type=search]::-webkit-search-cancel-button, [type=search]::-webkit-search-decoration {\n    -webkit-appearance: none\n}\n\n::-webkit-file-upload-button {\n    -webkit-appearance: button;\n    font: inherit\n}\n\ndetails, menu {\n    display: block\n}\n\nsummary {\n    display: list-item\n}\n\ncanvas {\n    display: inline-block\n}\n\ntemplate {\n    display: none\n}\n\n[hidden] {\n    display: none\n}\n\n/* configuration cache styles */\n\n.report-wrapper {\n    margin: 0;\n    padding: 0 24px;\n}\n\n.gradle-logo {\n    width: 32px;\n    height: 24px;\n    background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAYAAACbU/80AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAGAAAAAA915G0AAAD5klEQVRIDbVWC0xTZxT+emmhVUEeA1/ROh/tFAFFGK7oJisIKsNVoOwBbJPowEWHzikRxeiMRpwwjDWRBHQLIzOmiRhe22BT40TitiyaMBQFfMEeLMIEaSmk+/+rvd7be4no6Elu7n++c/5zzv845/wyOyG4iGyDgzCdNOPLM9W41n4bnmNUiHo5DNsz0hGsmcV6lbkyAOOWXJjrz4qWp1C4o3z/LqzWL4VcJB1FIHmZHn/f78a6pDcxbeIEfNvQiPwTZbDZBpC24zOEaGfDpTsgtZby6u+QlrubFWUY3nh6AH39/ahr/Bn1jZfxW3ML2js60dtvgbtcQVblj8CZM7A0PBSrol6Ft+c4KZ8iTB1nwN0//8IEP9/hA2i924Gir0/iq8oa/NvbJzLiDKiUSqTE6pGVbEBY4BxnsYAPSnwXTa3tLCZ5BF3dPdAkGNHzoFcwcaRMnC4CeZkZiAgKFE252nITC1Pew9Dj5GNEGgS4Rbb5eZ1Te7UXG6FLX4cV6zeh5kIDaDpSunL9Boyf5nLOpwT4Sx+BxWrFK8QAnTAapPRQwofcj86uLoG59cbVEOzA0NAQNh38Atn5RSjY8rFAmc/I3dyQvOx1PsSNVy7Roa3ajHDePbBYLSLn1MaGd5KFAXy07xAOl59C6elK+I73hIHcbGd6wXs8qkyH8FZcjLOI5X/9/TrOnLsAldJDUu4As1NToFFPe3IEpm/M2HigwCFnU6t4Zw6Ck1JhGRhgcXq5juXloKyqFnlHirmz5CaNcEAv59kSE9wVikcB3O78A/MSU0Fznk/H9+yAetJEnPr+B8RFLsLcGS8ia28+qQuX+WrPNNZOV+Nc6VH4+3iz89g0pEaLzRUiQ3LGDWsM8Qidq2WL0PGKKlgf74ZIeQTAfFJ6a44WIsDXh9OW/dPdY58aawC9KK6kpOgolO7JxViVSuBGXnvxksudZ5F0O5yzGYxMJnBOGaau4fnPU2RNAtCFBKFoa7akczaAptY2iWmjB33+yQa4kZwfjpi2ex3Dyf43vuAljWQ/4Btmei1WPj+q45hF4U+1J4fEizCEvNf0EWHoIW244sfzoN1RipaT2kDfdjfv3MNpojdISjmfIheE8Fnp8WR9vJ2Zr+O+bYUmO+kJ9KnIUtf9bnvY2x9wcqrrvnCJvfL8Tw4V9v9LU7PdKzJaoNdy645AR4ph1JMncZHRKrVvYyYY5kmP8iO1v2T3dk6HDtYmrgJtOnwKnaPFrg8z+BBX7QSgEyOPJfX9Qd9DFs40GgTOHbrBs2ch4bXFuEG2mmFkeD9hpUMk+NMXEe0TNtsg/Ly94DVurEAuxfwHC1WiVbe0U7MAAAAASUVORK5CYII=\");\n    background-size: contain;\n}\n\n.header {\n    display: flex;\n    flex-wrap: wrap;\n    position: fixed;\n    top: 0;\n    left: 0;\n    width: 100%;\n    padding: 24px 24px 0 24px;\n    background-color: white;\n    z-index: 1;\n}\n\n.learn-more {\n    margin-left: auto;\n    align-self: center;\n    font-size: 0.875rem;\n    font-weight: normal;\n}\n\n.title {\n    display: flex;\n    align-items: center;\n    padding: 18px 0 24px 0;\n    flex: 1 0 100%;\n}\n\n.content {\n    font-size: 0.875rem;\n    padding: 240px 0 48px;\n    overflow-x: auto;\n    white-space: nowrap;\n}\n\n.content ol:first-of-type {\n    margin: 0;\n}\n\n.tree-btn {\n    cursor: pointer;\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-top: -0.2em;\n}\n\n.tree-btn.collapsed {\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 192 512\"><path d=\"M166.9 264.5l-117.8 116c-4.7 4.7-12.3 4.7-17 0l-7.1-7.1c-4.7-4.7-4.7-12.3 0-17L127.3 256 25.1 155.6c-4.7-4.7-4.7-12.3 0-17l7.1-7.1c4.7-4.7 12.3-4.7 17 0l117.8 116c4.6 4.7 4.6 12.3-.1 17z\" fill=\"%23999999\" stroke=\"%23999999\"/></svg>');\n}\n\n.tree-btn.expanded {\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"><path d=\"M119.5 326.9L3.5 209.1c-4.7-4.7-4.7-12.3 0-17l7.1-7.1c4.7-4.7 12.3-4.7 17 0L128 287.3l100.4-102.2c4.7-4.7 12.3-4.7 17 0l7.1 7.1c4.7 4.7 4.7 12.3 0 17L136.5 327c-4.7 4.6-12.3 4.6-17-.1z\" fill=\"%23999999\" stroke=\"%23999999\"/></svg>');\n}\n\nul .tree-btn {\n    margin-right: 3px;\n}\n\n.leaf-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"><path d=\"M32 256 H224\" stroke=\"%23999999\" stroke-width=\"48\" stroke-linecap=\"round\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-top: -0.2em;\n}\n\n.invisible-text {\n    user-select: all; /* Allow the text to be selectable */\n    color: transparent; /* Hide the text */\n    text-indent: -9999px; /* Move the text out of view */\n    position: relative;\n    white-space: pre; /* Preserve meaningful whitespace in the invisible text for copying */\n}\n\n.text-for-copy {\n    display: inline-block;\n}\n\n.enum-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\"><circle cx=\"512\" cy=\"512\" r=\"200\" /></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-inline-end: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.error-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"><path d=\"M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z\" fill=\"%23FC461E\" stroke=\"%23FC461E\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-inline-end: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.advice-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-image: url('data:image/svg+xml;utf8,<svg width=\"800px\" height=\"800px\" viewBox=\"-4.93 0 122.88 122.88\" version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"  style=\"enable-background:new 0 0 113.01 122.88\" xml:space=\"preserve\"><g><path d=\"M44.13,102.06c-1.14,0.03-2.14-0.81-2.3-1.96c-0.17-1.2,0.64-2.31,1.82-2.54c-1.3-7.37-4.85-11.43-8.6-15.72 c-2.92-3.34-5.95-6.81-8.34-11.92c-2.35-5.03-3.64-10.23-3.6-15.63c0.05-5.4,1.42-10.96,4.4-16.71c0.02-0.04,0.04-0.07,0.06-0.11 l0,0c3.91-6.62,9.38-11.04,15.47-13.52c5.11-2.09,10.66-2.8,16.1-2.3c5.42,0.5,10.73,2.2,15.37,4.94 c5.9,3.49,10.75,8.67,13.42,15.21c1.44,3.54,2.42,7.49,2.54,11.82c0.12,4.31-0.62,8.96-2.61,13.88 c-2.66,6.59-6.18,10.68-9.47,14.51c-3.03,3.53-5.85,6.81-7.42,11.84c0.89,0.21,1.59,0.94,1.73,1.9c0.17,1.24-0.7,2.39-1.94,2.56 l-0.77,0.11c-0.14,1.09-0.23,2.26-0.27,3.51l0.25-0.04c1.24-0.17,2.39,0.7,2.56,1.94c0.17,1.24-0.7,2.39-1.94,2.56l-0.78,0.11 c0.01,0.15,0.02,0.3,0.03,0.45l0,0c0.07,0.88,0.08,1.73,0.03,2.54l0.13-0.02c1.25-0.15,2.38,0.74,2.54,1.98 c0.15,1.25-0.74,2.38-1.98,2.54l-1.68,0.21c-1.2,3.11-3.34,5.48-5.87,6.94c-1.74,1.01-3.67,1.59-5.61,1.71 c-1.97,0.12-3.96-0.25-5.78-1.13c-2.08-1.02-3.94-2.71-5.29-5.14c-0.65-0.33-1.13-0.97-1.23-1.75c-0.04-0.31-0.01-0.61,0.07-0.89 c-0.39-1.16-0.68-2.43-0.87-3.83l-0.07,0.01c-1.24,0.17-2.39-0.7-2.56-1.94c-0.17-1.24,0.7-2.39,1.94-2.56l0.54-0.08 C44.19,104.32,44.18,103.16,44.13,102.06L44.13,102.06z M2.18,58.86C1.01,58.89,0.04,57.98,0,56.81c-0.04-1.17,0.88-2.14,2.05-2.18 l8.7-0.3c1.17-0.04,2.14,0.88,2.18,2.05c0.04,1.17-0.88,2.14-2.05,2.18L2.18,58.86L2.18,58.86z M110.68,50.25 c1.16-0.12,2.2,0.73,2.32,1.89c0.12,1.16-0.73,2.2-1.89,2.32l-8.66,0.91c-1.16,0.12-2.2-0.73-2.32-1.89 c-0.12-1.16,0.73-2.2,1.89-2.32L110.68,50.25L110.68,50.25z M94.91,14.78c0.65-0.97,1.96-1.23,2.93-0.58 c0.97,0.65,1.23,1.96,0.58,2.93l-4.84,7.24c-0.65,0.97-1.96,1.23-2.93,0.58c-0.97-0.65-1.23-1.96-0.58-2.93L94.91,14.78 L94.91,14.78z M57.63,2.06c0.03-1.17,1-2.09,2.16-2.06c1.17,0.03,2.09,1,2.06,2.16l-0.22,8.7c-0.03,1.17-1,2.09-2.16,2.06 c-1.17-0.03-2.09-1-2.06-2.16L57.63,2.06L57.63,2.06z M13.88,15.53c-0.86-0.8-0.9-2.14-0.11-2.99c0.8-0.86,2.14-0.9,2.99-0.11 l6.37,5.94c0.86,0.8,0.9,2.14,0.11,2.99c-0.8,0.86-2.14,0.9-2.99,0.11L13.88,15.53L13.88,15.53z M47.88,96.95l18.49-2.63 c1.59-6.7,5.05-10.73,8.8-15.08c3.08-3.58,6.36-7.4,8.76-13.34c1.76-4.35,2.41-8.43,2.31-12.19c-0.1-3.75-0.96-7.21-2.24-10.34 c-2.3-5.63-6.51-10.11-11.65-13.15c-4.11-2.43-8.8-3.94-13.59-4.37c-4.77-0.44-9.64,0.19-14.13,2.02 c-5.26,2.15-9.99,5.97-13.39,11.72c-2.64,5.12-3.86,10.02-3.9,14.73c-0.04,4.74,1.11,9.33,3.2,13.8c2.13,4.56,4.97,7.8,7.69,10.92 C42.47,83.9,46.48,88.49,47.88,96.95L47.88,96.95z M65.62,99.02l-17.27,2.45c0.05,1.1,0.07,2.25,0.05,3.47l17.05-2.42 C65.47,101.29,65.52,100.12,65.62,99.02L65.62,99.02z M48.49,109.52c0.12,0.92,0.3,1.76,0.53,2.54l16.55-2.04 c0.11-0.86,0.13-1.77,0.05-2.74l0,0l0-0.02l-0.01-0.17L48.49,109.52L48.49,109.52z M51.37,116.36c0.64,0.67,1.35,1.19,2.1,1.55 c1.15,0.56,2.42,0.79,3.67,0.72c1.29-0.08,2.57-0.47,3.74-1.15c1.1-0.64,2.09-1.53,2.88-2.65L51.37,116.36L51.37,116.36z\"/></g></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-inline-end: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.warning-icon {\n    display: inline-block;\n    width: 13px;\n    height: 13px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><path d=\"M270.2 160h35.5c3.4 0 6.1 2.8 6 6.2l-7.5 196c-.1 3.2-2.8 5.8-6 5.8h-20.5c-3.2 0-5.9-2.5-6-5.8l-7.5-196c-.1-3.4 2.6-6.2 6-6.2zM288 388c-15.5 0-28 12.5-28 28s12.5 28 28 28 28-12.5 28-28-12.5-28-28-28zm281.5 52L329.6 24c-18.4-32-64.7-32-83.2 0L6.5 440c-18.4 31.9 4.6 72 41.6 72H528c36.8 0 60-40 41.5-72zM528 480H48c-12.3 0-20-13.3-13.9-24l240-416c6.1-10.6 21.6-10.7 27.7 0l240 416c6.2 10.6-1.5 24-13.8 24z\" fill=\"%23DEAD22\" stroke=\"%23DEAD22\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.3ex;\n    margin-inline-end: 1.1ex;\n    margin-top: -0.1em;\n}\n\n.documentation-button {\n    cursor: pointer;\n    display: inline-block;\n    width: 13px;\n    height: 13px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 340c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28zm7.67-24h-16c-6.627 0-12-5.373-12-12v-.381c0-70.343 77.44-63.619 77.44-107.408 0-20.016-17.761-40.211-57.44-40.211-29.144 0-44.265 9.649-59.211 28.692-3.908 4.98-11.054 5.995-16.248 2.376l-13.134-9.15c-5.625-3.919-6.86-11.771-2.645-17.177C185.658 133.514 210.842 116 255.67 116c52.32 0 97.44 29.751 97.44 80.211 0 67.414-77.44 63.849-77.44 107.408V304c0 6.627-5.373 12-12 12zM256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8z\" fill=\"%23999999\" stroke=\"%23999999\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-inline-end: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.documentation-button::selection {\n    color: transparent;\n}\n\n.documentation-button:hover {\n    color: transparent;\n}\n\n.copy-button {\n    cursor: pointer;\n    display: inline-block;\n    width: 12px;\n    height: 12px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path d=\"M433.941 193.941l-51.882-51.882A48 48 0 0 0 348.118 128H320V80c0-26.51-21.49-48-48-48h-66.752C198.643 13.377 180.858 0 160 0s-38.643 13.377-45.248 32H48C21.49 32 0 53.49 0 80v288c0 26.51 21.49 48 48 48h80v48c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48V227.882a48 48 0 0 0-14.059-33.941zm-22.627 22.627a15.888 15.888 0 0 1 4.195 7.432H352v-63.509a15.88 15.88 0 0 1 7.431 4.195l51.883 51.882zM160 30c9.941 0 18 8.059 18 18s-8.059 18-18 18-18-8.059-18-18 8.059-18 18-18zM48 384c-8.822 0-16-7.178-16-16V80c0-8.822 7.178-16 16-16h66.752c6.605 18.623 24.389 32 45.248 32s38.643-13.377 45.248-32H272c8.822 0 16 7.178 16 16v48H176c-26.51 0-48 21.49-48 48v208H48zm352 96H176c-8.822 0-16-7.178-16-16V176c0-8.822 7.178-16 16-16h144v72c0 13.2 10.8 24 24 24h72v208c0 8.822-7.178 16-16 16z\" fill=\"%23999999\" stroke=\"%23999999\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.groups{\n    display: flex;\n    border-bottom: 1px solid #EDEEEF;\n    flex: 1 0 100%;\n}\n\n.uncategorized {\n    display: flex;\n    border-top: 4px solid #EDEEEF;\n    flex: 1 0 100%;\n}\n\n.group-selector {\n    padding: 0 52px 24px 0;\n    font-size: 0.9rem;\n    font-weight: bold;\n    color: #999999;\n    cursor: pointer;\n}\n\n.group-selector__count {\n    margin: 0 8px;\n    border-radius: 8px;\n    background-color: #999;\n    color: #fff;\n    padding: 1px 8px 2px;\n    font-size: 0.75rem;\n}\n\n.group-selector--active {\n    color: #02303A;\n    cursor: auto;\n}\n\n.group-selector--active .group-selector__count {\n    background-color: #686868;\n}\n\n.group-selector--disabled {\n    cursor: not-allowed;\n}\n\n.accordion-header {\n    cursor: pointer;\n}\n\n.container {\n    padding-left: 0.5em;\n    padding-right: 0.5em;\n}\n\n.stacktrace {\n    border-radius: 4px;\n    overflow-x: auto;\n    padding: 0.5rem;\n    margin-bottom: 0;\n    min-width: 1000px;\n}\n\n/* Lato (bold, regular) */\n@font-face {\n    font-display: swap;\n    font-family: Lato;\n    font-weight: 500;\n    font-style: normal;\n    src: url(\"https://assets.gradle.com/lato/fonts/lato-semibold/lato-semibold.woff2\") format(\"woff2\"),\n    url(\"https://assets.gradle.com/lato/fonts/lato-semibold/lato-semibold.woff\") format(\"woff\");\n}\n\n@font-face {\n    font-display: swap;\n    font-family: Lato;\n    font-weight: bold;\n    font-style: normal;\n    src: url(\"https://assets.gradle.com/lato/fonts/lato-bold/lato-bold.woff2\") format(\"woff2\"),\n    url(\"https://assets.gradle.com/lato/fonts/lato-bold/lato-bold.woff\") format(\"woff\");\n}\n\n* {\n    -webkit-box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    box-sizing: border-box;\n}\n\nhtml,\nbody {\n    margin: 0;\n    padding: 0;\n}\n\nhtml {\n    font-family: \"Lato\", \"Helvetica Neue\", Arial, sans-serif;\n    font-size: 16px;\n    font-weight: 400;\n    line-height: 1.5;\n}\n\nbody {\n    color: #02303A;\n    background-color: #ffffff;\n    -webkit-text-size-adjust: 100%;\n    -ms-text-size-adjust: 100%;\n    -webkit-font-smoothing: antialiased;\n}\n\n\n/* typography */\nh1, h2, h3, h4, h5, h6 {\n    color: #02303A;\n    text-rendering: optimizeLegibility;\n    margin: 0;\n}\n\nh1 {\n    font-size: 1rem;\n}\n\nh2 {\n    font-size: 0.9rem;\n}\n\nh3 {\n    font-size: 1.125rem;\n}\n\nh4, h5, h6 {\n    font-size: 0.875rem;\n}\n\nh1 code {\n    font-weight: bold;\n}\n\nul, ol, dl {\n    list-style-position: outside;\n    line-height: 1.6;\n    padding: 0;\n    margin: 0 0 0 20px;\n    list-style-type: none;\n}\n\nli {\n    line-height: 2;\n}\n\na {\n    color: #1DA2BD;\n    text-decoration: none;\n    transition: all 0.3s ease, visibility 0s;\n}\n\na:hover {\n    color: #35c1e4;\n}\n\n/* code */\ncode, pre {\n    font-family: Inconsolata, Monaco, \"Courier New\", monospace;\n    font-style: normal;\n    font-variant-ligatures: normal;\n    font-variant-caps: normal;\n    font-variant-numeric: normal;\n    font-variant-east-asian: normal;\n    font-weight: normal;\n    font-stretch: normal;\n    color: #686868;\n}\n\n*:not(pre) > code {\n    letter-spacing: 0;\n    padding: 0.1em 0.5ex;\n    text-rendering: optimizeSpeed;\n    word-spacing: -0.15em;\n    word-wrap: break-word;\n}\n\npre {\n    font-size: 0.75rem;\n    line-height: 1.8;\n    margin-top: 0;\n    margin-bottom: 1.5em;\n    padding: 1rem;\n}\n\npre code {\n    background-color: transparent;\n    color: inherit;\n    line-height: 1.8;\n    font-size: 100%;\n    padding: 0;\n}\n\na code {\n    color: #1BA8CB;\n}\n\npre.code, pre.programlisting, pre.screen, pre.tt {\n    background-color: #f7f7f8;\n    border-radius: 4px;\n    font-size: 1em;\n    line-height: 1.45;\n    margin-bottom: 1.25em;\n    overflow-x: auto;\n    padding: 1rem;\n}\n\nli em, p em {\n    padding: 0 1px;\n}\n\ncode em, tt em {\n    text-decoration: none;\n}\n\ncode + .copy-button {\n    margin-inline-start: 0.2ex;\n}\n\n.java-exception {\n    font-size: 0.75rem;\n    padding-left: 24px;\n}\n\n.java-exception ul {\n    margin: 0;\n    line-height: inherit;\n}\n\n.java-exception code {\n    white-space: pre;\n}\n\n.java-exception-part-toggle {\n    user-select: none;\n    cursor: pointer;\n    border-radius: 2px;\n    padding: 0.1em 0.2em;\n    background: azure;\n    color: #686868;\n}\n\n                </style>\n    <!-- Inconsolata is used as a default monospace font in the report. -->\n    <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Inconsolata:400,700\" />\n\n    <title>Gradle Configuration Cache</title>\n</head>\n<body>\n\n<div id=\"playground\"></div>\n\n<div class=\"report\" id=\"report\">\n    Loading...\n</div>\n\n<script type=\"text/javascript\">\nfunction configurationCacheProblems() { return (\n// begin-report-data\n{\"diagnostics\":[{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('version = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('namespace = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('version = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('checkAllWarnings = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('warningsAsErrors = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('group = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('version = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('namespace = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('group = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('version = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('namespace = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('minSdk = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('group = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('version = <value>') instead.\"}]]},{\"locations\":[{}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('namespace = <value>') instead.\"}]]}],\"problemsReport\":{\"totalProblemCount\":15,\"buildName\":\"android\",\"requestedTasks\":\"assembleProductionRelease\",\"documentationLink\":\"https://docs.gradle.org/8.14/userguide/reporting_problems.html\",\"documentationLinkCaption\":\"Problem report\",\"summaries\":[{\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"count\":19}]}}\n// end-report-data\n);}\n</script>\n                <script type=\"text/javascript\">\n                !function(n,t){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define([],t):\"object\"==typeof exports?exports[\"configuration-cache-report\"]=t():n[\"configuration-cache-report\"]=t()}(this,(()=>(({70:function(){void 0===ArrayBuffer.isView&&(ArrayBuffer.isView=function(n){return null!=n&&null!=n.__proto__&&n.__proto__.__proto__===Int8Array.prototype.__proto__}),void 0===Math.imul&&(Math.imul=function(n,t){return(4294901760&n)*(65535&t)+(65535&n)*(0|t)|0}),this[\"configuration-cache-report\"]=function(n){\"use strict\";var t,r,i,e,u,o,f,s,c,a,h,l,_,v,d,g,w,b,p,m,k,q,y,B,C,x,j,P,I,S,z,T,E,L,N,A,M,F,D,O,R,H,$,G,U,V,Q,Z,Y,W,K,X,J,nn,tn,rn,en,un,on,fn,sn,cn,an,hn,ln,_n,vn,dn,gn,wn,bn,pn,mn,kn,qn,yn,Bn,Cn,xn,jn,Pn,In,Sn,zn,Tn,En,Ln,Nn,An,Mn=Math.imul,Fn=ArrayBuffer.isView;function Dn(n,t){if(!(t>=0))throw _u(ce(\"Requested element count \"+t+\" is less than zero.\"));return function(n,t){if(!(t>=0))throw _u(ce(\"Requested element count \"+t+\" is less than zero.\"));if(0===t)return Ct();if(t>=n.length)return function(n){switch(n.length){case 0:return Ct();case 1:return qr(n[0]);default:return function(n){return Hr(function(n){return new It(n,!1)}(n))}(n)}}(n);if(1===t)return qr(n[0]);var r=0,i=Rr(),e=0,u=n.length;n:for(;e<u;){var o=n[e];if(e=e+1|0,i.d(o),(r=r+1|0)===t)break n}return i}(n,rt(n.length-t|0,0))}function On(n,t,r,i,e,u,o){return t=t===A?\", \":t,r=r===A?\"\":r,i=i===A?\"\":i,e=e===A?-1:e,u=u===A?\"...\":u,o=o===A?null:o,function(n,t,r,i,e,u,o,f){r=r===A?\", \":r,i=i===A?\"\":i,e=e===A?\"\":e,u=u===A?-1:u,o=o===A?\"...\":o,f=f===A?null:f,t.e(i);var s=0,c=0,a=n.length;n:for(;c<a;){var h=n[c];if(c=c+1|0,(s=s+1|0)>1&&t.e(r),!(u<0||s<=u))break n;Jt(t,h,f)}return u>=0&&s>u&&t.e(o),t.e(e),t}(n,Bi(),t,r,i,e,u,o).toString()}function Rn(n){return n.length-1|0}function Hn(n,t){if(null==t){var r=0,i=n.length-1|0;if(r<=i)do{var e=r;if(r=r+1|0,null==n[e])return e}while(r<=i)}else{var u=0,o=n.length-1|0;if(u<=o)do{var f=u;if(u=u+1|0,le(t,n[f]))return f}while(u<=o)}return-1}function $n(n,t,r,i,e,u,o){return t=t===A?\", \":t,r=r===A?\"\":r,i=i===A?\"\":i,e=e===A?-1:e,u=u===A?\"...\":u,o=o===A?null:o,Gn(n,Bi(),t,r,i,e,u,o).toString()}function Gn(n,t,r,i,e,u,o,f){r=r===A?\", \":r,i=i===A?\"\":i,e=e===A?\"\":e,u=u===A?-1:u,o=o===A?\"...\":o,f=f===A?null:f,t.e(i);var s=0,c=n.f();n:for(;c.g();){var a=c.h();if((s=s+1|0)>1&&t.e(r),!(u<0||s<=u))break n;Jt(t,a,f)}return u>=0&&s>u&&t.e(o),t.e(e),t}function Un(n){if(n.i())throw xu(\"List is empty.\");return n.j(0)}function Vn(n){return new tt(n)}function Qn(n){if(Ke(n,Di)){var t;switch(n.k()){case 0:t=Ct();break;case 1:t=qr(Ke(n,Fi)?n.j(0):n.f().h());break;default:t=Zn(n)}return t}return xt(Xn(n))}function Zn(n){return Hr(n)}function Yn(n){if(Ke(n,Di)&&n.k()<=1)return Qn(n);var t=Xn(n);return function(n){var t=(n.k()/2|0)-1|0;if(t<0)return br();var r=jt(n),i=0;if(i<=t)do{var e=i;i=i+1|0;var u=n.j(e);n.f4(e,n.j(r)),n.f4(r,u),r=r-1|0}while(e!==t)}(t),t}function Wn(n,t){if(!(t>=0))throw _u(ce(\"Requested element count \"+t+\" is less than zero.\"));return function(n,t){if(!(t>=0))throw _u(ce(\"Requested element count \"+t+\" is less than zero.\"));if(0===t)return Ct();if(Ke(n,Di)){if(t>=n.k())return Qn(n);if(1===t)return qr(function(n){if(Ke(n,Fi))return Un(n);var t=n.f();if(!t.g())throw xu(\"Collection is empty.\");return t.h()}(n))}var r=0,i=Rr(),e=n.f();n:for(;e.g();){var u=e.h();if(i.d(u),(r=r+1|0)===t)break n}return xt(i)}(n,rt(n.k()-t|0,0))}function Kn(n,t){if(!(t>=0))throw _u(ce(\"Requested element count \"+t+\" is less than zero.\"));if(0===t)return Ct();var r=n.k();if(t>=r)return Qn(n);if(1===t)return qr(Jn(n));var i=Rr();if(Ke(n,bi)){var e=r-t|0;if(e<r)do{var u=e;e=e+1|0,i.d(n.j(u))}while(e<r)}else for(var o=n.l(r-t|0);o.g();){var f=o.h();i.d(f)}return i}function Xn(n){return Ke(n,Di)?Zn(n):nt(n,Or())}function Jn(n){if(n.i())throw xu(\"List is empty.\");return n.j(jt(n))}function nt(n,t){for(var r=n.f();r.g();){var i=r.h();t.d(i)}return t}function tt(n){this.n_1=n}function rt(n,t){return n<t?t:n}function it(n,t){return n>t?t:n}function et(n,t){return Kt().q(n,t,-1)}function ut(n,t){return new Ft(n,t)}function ot(n){var t=n.f();if(!t.g())return Ct();var r=t.h();if(!t.g())return qr(r);var i=Or();for(i.d(r);t.g();)i.d(t.h());return i}function ft(n){this.r_1=n}function st(n,t){this.s_1=n,this.t_1=t}function ct(){}function at(n){this.x_1=n,this.w_1=0}function ht(n,t){this.a1_1=n,at.call(this,n),_t().b1(t,this.a1_1.k()),this.w_1=t}function lt(){t=this}function _t(){return null==t&&new lt,t}function vt(){_t(),ct.call(this)}function dt(n){this.h1_1=n}function gt(n,t){return t===n?\"(this Map)\":Vi(t)}function wt(n,t){var r;n:{for(var i=n.o().f();i.g();){var e=i.h();if(le(e.j1(),t)){r=e;break n}}r=null}return r}function bt(){r=this}function pt(){return null==r&&new bt,r}function mt(n){this.q1_1=n,ct.call(this)}function kt(){pt(),this.n1_1=null,this.o1_1=null}function qt(){i=this}function yt(){return null==i&&new qt,i}function Bt(n){return n.length>0?ou(n):Ct()}function Ct(){return null==e&&new Pt,e}function xt(n){switch(n.k()){case 0:return Ct();case 1:return qr(n.j(0));default:return n}}function jt(n){return n.k()-1|0}function Pt(){e=this,this.z1_1=new ke(-1478467534,-1720727600)}function It(n,t){this.b2_1=n,this.c2_1=t}function St(){u=this}function zt(){return null==u&&new St,u}function Tt(n,t){return Ke(n,Di)?n.k():t}function Et(n,t){if(Ke(t,Di))return n.m(t);for(var r=!1,i=t.f();i.g();){var e=i.h();n.d(e)&&(r=!0)}return r}function Lt(){}function Nt(n,t){this.h2_1=n,this.g2_1=n.i2_1.l(function(n,t){if(!(0<=t&&t<=n.k()))throw du(\"Position index \"+t+\" must be in range [\"+Ve(0,n.k())+\"].\");return n.k()-t|0}(n,t))}function At(n){vt.call(this),this.i2_1=n}function Mt(n){this.k2_1=n,this.j2_1=n.l2_1.f()}function Ft(n,t){this.l2_1=n,this.m2_1=t}function Dt(n){for(;n.n2_1.g();){var t=n.n2_1.h();if(n.q2_1.t2_1(t)===n.q2_1.s2_1)return n.p2_1=t,n.o2_1=1,br()}n.o2_1=0}function Ot(n){this.q2_1=n,this.n2_1=n.r2_1.f(),this.o2_1=-1,this.p2_1=null}function Rt(n,t,r){t=t===A||t,this.r2_1=n,this.s2_1=t,this.t2_1=r}function Ht(){return null==o&&new $t,o}function $t(){o=this,this.u2_1=new ke(1993859828,793161749)}function Gt(n,t,r){return Ut(Ut(n,r)-Ut(t,r)|0,r)}function Ut(n,t){var r=n%t|0;return r>=0?r:r+t|0}function Vt(){f=this,this.p_1=new Zt(1,0)}function Qt(){return null==f&&new Vt,f}function Zt(n,t){Qt(),Xt.call(this,n,t,1)}function Yt(n,t,r){Lt.call(this),this.d3_1=r,this.e3_1=t,this.f3_1=this.d3_1>0?n<=t:n>=t,this.g3_1=this.f3_1?n:this.e3_1}function Wt(){s=this}function Kt(){return null==s&&new Wt,s}function Xt(n,t,r){if(Kt(),0===r)throw _u(\"Step must be non-zero.\");if(r===mr().MIN_VALUE)throw _u(\"Step must be greater than Int.MIN_VALUE to avoid overflow on negation.\");this.z2_1=n,this.a3_1=function(n,t,r){var i;if(r>0)i=n>=t?t:t-Gt(t,n,r)|0;else{if(!(r<0))throw _u(\"Step is zero.\");i=n<=t?t:t+Gt(n,t,0|-r)|0}return i}(n,t,r),this.b3_1=r}function Jt(n,t,r){null!=r?n.e(r(t)):null==t||nu(t)?n.e(t):t instanceof Mi?n.i3(t.h3_1):n.e(Vi(t))}function nr(n,t,r){if(n===t)return!0;if(!(r=r!==A&&r))return!1;var i=xi(n),e=xi(t);return i===e||le(new Mi(ne(Li(i).toLowerCase(),0)),new Mi(ne(Li(e).toLowerCase(),0)))}function tr(n){return re(n)-1|0}function rr(n,t,r,i){return r=r===A?0:r,(i=i!==A&&i)||\"string\"!=typeof n?ir(n,t,r,re(n),i):n.indexOf(t,r)}function ir(n,t,r,i,e,u){var o=(u=u!==A&&u)?et(it(r,tr(n)),rt(i,0)):Ve(rt(r,0),it(i,re(n)));if(\"string\"==typeof n&&\"string\"==typeof t){var f=o.z2_1,s=o.a3_1,c=o.b3_1;if(c>0&&f<=s||c<0&&s<=f)do{var a=f;if(f=f+c|0,Ti(t,0,n,a,re(t),e))return a}while(a!==s)}else{var h=o.z2_1,l=o.a3_1,_=o.b3_1;if(_>0&&h<=l||_<0&&l<=h)do{var v=h;if(h=h+_|0,fr(t,0,n,v,re(t),e))return v}while(v!==l)}return-1}function er(n){var t=0,r=re(n)-1|0,i=!1;n:for(;t<=r;){var e=ji(ne(n,i?r:t));if(i){if(!e)break n;r=r-1|0}else e?t=t+1|0:i=!0}return ie(n,t,r+1|0)}function ur(n,t){return ce(ie(n,t.y2(),t.c3()+1|0))}function or(n,t,r,i,e){r=r===A?0:r,i=i!==A&&i,sr(e=e===A?0:e);var u,o,f=ou(t);return new hr(n,r,e,(u=f,o=i,function(n,t){var r=function(n,t,r,i){if(!i&&1===t.k()){var e=function(n){if(Ke(n,Fi))return function(n){var t;switch(n.k()){case 0:throw xu(\"List is empty.\");case 1:t=n.j(0);break;default:throw _u(\"List has more than one element.\")}return t}(n);var t=n.f();if(!t.g())throw xu(\"Collection is empty.\");var r=t.h();if(t.g())throw _u(\"Collection has more than one element.\");return r}(t),u=rr(n,e,r);return u<0?null:_r(u,e)}var o=Ve(rt(r,0),re(n));if(\"string\"==typeof n){var f=o.z2_1,s=o.a3_1,c=o.b3_1;if(c>0&&f<=s||c<0&&s<=f)do{var a,h=f;f=f+c|0;n:{for(var l=t.f();l.g();){var _=l.h();if(Ti(_,0,n,h,_.length,i)){a=_;break n}}a=null}if(null!=a)return _r(h,a)}while(h!==s)}else{var v=o.z2_1,d=o.a3_1,g=o.b3_1;if(g>0&&v<=d||g<0&&d<=v)do{var w,b=v;v=v+g|0;n:{for(var p=t.f();p.g();){var m=p.h();if(fr(m,0,n,b,m.length,i)){w=m;break n}}w=null}if(null!=w)return _r(b,w)}while(b!==d)}return null}(n,u,t,o);return null==r?null:_r(r.t3_1,r.u3_1.length)}))}function fr(n,t,r,i,e,u){if(i<0||t<0||t>(re(n)-e|0)||i>(re(r)-e|0))return!1;var o=0;if(o<e)do{var f=o;if(o=o+1|0,!nr(ne(n,t+f|0),ne(r,i+f|0),u))return!1}while(o<e);return!0}function sr(n){if(!(n>=0))throw _u(ce(\"Limit must be non-negative, but was \"+n))}function cr(n){if(n.l3_1<0)n.j3_1=0,n.m3_1=null;else{var t;if(n.o3_1.r3_1>0?(n.n3_1=n.n3_1+1|0,t=n.n3_1>=n.o3_1.r3_1):t=!1,t||n.l3_1>re(n.o3_1.p3_1))n.m3_1=Ve(n.k3_1,tr(n.o3_1.p3_1)),n.l3_1=-1;else{var r=n.o3_1.s3_1(n.o3_1.p3_1,n.l3_1);if(null==r)n.m3_1=Ve(n.k3_1,tr(n.o3_1.p3_1)),n.l3_1=-1;else{var i=r.v3(),e=r.w3();n.m3_1=function(n,t){return t<=mr().MIN_VALUE?Qt().p_1:Ve(n,t-1|0)}(n.k3_1,i),n.k3_1=i+e|0,n.l3_1=n.k3_1+(0===e?1:0)|0}}n.j3_1=1}}function ar(n){this.o3_1=n,this.j3_1=-1,this.k3_1=function(n,t,r){if(0>r)throw _u(\"Cannot coerce value to an empty range: maximum \"+r+\" is less than minimum 0.\");return n<0?0:n>r?r:n}(n.q3_1,0,re(n.p3_1)),this.l3_1=this.k3_1,this.m3_1=null,this.n3_1=0}function hr(n,t,r,i){this.p3_1=n,this.q3_1=t,this.r3_1=r,this.s3_1=i}function lr(n,t){this.t3_1=n,this.u3_1=t}function _r(n,t){return new lr(n,t)}function vr(){}function dr(){}function gr(){}function wr(){c=this}function br(){return null==c&&new wr,c}function pr(){a=this,this.MIN_VALUE=-2147483648,this.MAX_VALUE=2147483647,this.SIZE_BYTES=4,this.SIZE_BITS=32}function mr(){return null==a&&new pr,a}function kr(n){for(var t=[],r=n.f();r.g();)t.push(r.h());return t}function qr(n){return 0===(t=[n]).length?Or():Hr(new It(t,!0));var t}function yr(n){return n<0&&function(){throw Pu(\"Index overflow has happened.\")}(),n}function Br(n){return void 0!==n.toArray?n.toArray():kr(n)}function Cr(n){return function(n,t){for(var r=0,i=n.length;r<i;){var e=n[r];r=r+1|0,t.d(e)}return t}(t=[n],(r=t.length,i=de(ve(ni)),function(n,t,r){Mr.call(r),ni.call(r),r.y5_1=function(n){return Kr(n,0,de(ve(Xr)))}(n)}(r,0,i),i));var t,r,i}function xr(){ct.call(this)}function jr(n){this.j4_1=n,this.h4_1=0,this.i4_1=-1}function Pr(n,t){this.n4_1=n,jr.call(this,n),_t().b1(t,this.n4_1.k()),this.h4_1=t}function Ir(){xr.call(this),this.o4_1=0}function Sr(n){this.r4_1=n}function zr(n){this.s4_1=n}function Tr(n,t){this.t4_1=n,this.u4_1=t}function Er(){Mr.call(this)}function Lr(n){this.x4_1=n,Mr.call(this)}function Nr(n){this.e5_1=n,xr.call(this)}function Ar(){kt.call(this),this.c5_1=null,this.d5_1=null}function Mr(){xr.call(this)}function Fr(){h=this;var n=Rr();n.c_1=!0,this.i5_1=n}function Dr(){return null==h&&new Fr,h}function Or(){return n=de(ve(Gr)),t=[],Gr.call(n,t),n;var n,t}function Rr(n){return t=de(ve(Gr)),r=[],Gr.call(t,r),t;var t,r}function Hr(n){return function(n,t){var r;return r=Br(n),Gr.call(t,r),t}(n,de(ve(Gr)))}function $r(n,t){return _t().e1(t,n.k()),t}function Gr(n){Dr(),Ir.call(this),this.b_1=n,this.c_1=!1}function Ur(n,t,r,i,e){if(r===i)return n;var u=(r+i|0)/2|0,o=Ur(n,t,r,u,e),f=Ur(n,t,u+1|0,i,e),s=o===t?n:t,c=r,a=u+1|0,h=r;if(h<=i)do{var l=h;if(h=h+1|0,c<=u&&a<=i){var _=o[c],v=f[a];e.compare(_,v)<=0?(s[l]=_,c=c+1|0):(s[l]=v,a=a+1|0)}else c<=u?(s[l]=o[c],c=c+1|0):(s[l]=f[a],a=a+1|0)}while(l!==i);return s}function Vr(n,t){return(3&n)-(3&t)|0}function Qr(){_=this}function Zr(n){this.n5_1=n,Er.call(this)}function Yr(n){return function(n,t){Ar.call(t),Xr.call(t),t.t5_1=n,t.u5_1=n.w5()}(new ui((null==_&&new Qr,_)),n),n}function Wr(){return Yr(de(ve(Xr)))}function Kr(n,t,r){if(Yr(r),!(n>=0))throw _u(ce(\"Negative initial capacity: \"+n));if(!(t>=0))throw _u(ce(\"Non-positive load factor: \"+t));return r}function Xr(){this.v5_1=null}function Jr(n,t){return Mr.call(t),ni.call(t),t.y5_1=n,t}function ni(){}function ti(n,t){var r=ii(n,n.h6_1.m5(t));if(null==r)return null;var i=r;if(null!=i&&Xe(i))return ri(i,n,t);var e=i;return n.h6_1.l5(e.j1(),t)?e:null}function ri(n,t,r){var i;n:{for(var e=0,u=n.length;e<u;){var o=n[e];if(e=e+1|0,t.h6_1.l5(o.j1(),r)){i=o;break n}}i=null}return i}function ii(n,t){var r=n.i6_1[t];return void 0===r?null:r}function ei(n){this.g6_1=n,this.z5_1=-1,this.a6_1=Object.keys(n.i6_1),this.b6_1=-1,this.c6_1=null,this.d6_1=!1,this.e6_1=-1,this.f6_1=null}function ui(n){this.h6_1=n,this.i6_1=this.k6(),this.j6_1=0}function oi(){}function fi(n){this.n6_1=n,this.l6_1=null,this.m6_1=null,this.m6_1=this.n6_1.y6_1.v6_1}function si(){v=this;var n,t=(_i(0,0,n=de(ve(vi))),n);t.x6_1=!0,this.e7_1=t}function ci(){return null==v&&new si,v}function ai(n,t,r){this.d7_1=n,Tr.call(this,t,r),this.b7_1=null,this.c7_1=null}function hi(n){this.y6_1=n,Er.call(this)}function li(){return Yr(n=de(ve(vi))),vi.call(n),n.w6_1=Wr(),n;var n}function _i(n,t,r){return Kr(n,t,r),vi.call(r),r.w6_1=Wr(),r}function vi(){ci(),this.v6_1=null,this.x6_1=!1}function di(){d=this;var n=gi(0),t=n.y5_1;(t instanceof vi?t:pe()).j5(),this.f7_1=n}function gi(n){return function(n,t){return function(n,t,r){Jr(function(n,t){return _i(n,t,de(ve(vi)))}(n,t),r),wi.call(r)}(n,0,t),t}(n,de(ve(wi)))}function wi(){null==d&&new di}function bi(){}function pi(){}function mi(n){pi.call(this),this.k7_1=n}function ki(){qi.call(this)}function qi(){pi.call(this),this.m7_1=\"\"}function yi(){if(!w){w=!0;var n=\"undefined\"!=typeof process&&process.versions&&!!process.versions.node;g=n?new mi(process.stdout):new ki}}function Bi(){return n=de(ve(Ci)),Ci.call(n,\"\"),n;var n}function Ci(n){this.o7_1=void 0!==n?n:\"\"}function xi(n){var t=Li(n).toUpperCase();return t.length>1?n:ne(t,0)}function ji(n){return function(n){return 9<=n&&n<=13||28<=n&&n<=32||160===n||n>4096&&(5760===n||8192<=n&&n<=8202||8232===n||8233===n||8239===n||8287===n||12288===n)}(n)}function Pi(){b=this,this.q7_1=new RegExp(\"[\\\\\\\\^$*+?.()|[\\\\]{}]\",\"g\"),this.r7_1=new RegExp(\"[\\\\\\\\$]\",\"g\"),this.s7_1=new RegExp(\"\\\\$\",\"g\")}function Ii(){return null==b&&new Pi,b}function Si(n,t){Ii(),this.v7_1=n,this.w7_1=function(n){if(Ke(n,Di)){var t;switch(n.k()){case 0:t=Ht();break;case 1:t=Cr(Ke(n,Fi)?n.j(0):n.f().h());break;default:t=nt(n,gi(n.k()))}return t}return function(n){switch(n.k()){case 0:return Ht();case 1:return Cr(n.f().h());default:return n}}(nt(n,(r=de(ve(wi)),Jr(li(),r),wi.call(r),r)));var r}(t),this.x7_1=new RegExp(n,$n(t,\"\",\"gu\",A,A,A,zi)),this.y7_1=null,this.z7_1=null}function zi(n){return n.d8_1}function Ti(n,t,r,i,e,u){return fr(n,t,r,i,e,u=u!==A&&u)}function Ei(n,t){return n-t|0}function Li(n){return String.fromCharCode(n)}function Ni(){p=this,this.e8_1=0,this.f8_1=65535,this.g8_1=55296,this.h8_1=56319,this.i8_1=56320,this.j8_1=57343,this.k8_1=55296,this.l8_1=57343,this.m8_1=2,this.n8_1=16}function Ai(){return null==p&&new Ni,p}function Mi(n){Ai(),this.h3_1=n}function Fi(){}function Di(){}function Oi(){}function Ri(){}function Hi(){}function $i(){}function Gi(){m=this}function Ui(n,t){null==m&&new Gi,this.p8_1=n,this.q8_1=t}function Vi(n){var t=null==n?null:ce(n);return null==t?\"null\":t}function Qi(n){return new Zi(n)}function Zi(n){this.t8_1=n,this.s8_1=0}function Yi(){return Ji(),k}function Wi(){return Ji(),q}function Ki(){return Ji(),y}function Xi(){return Ji(),B}function Ji(){x||(x=!0,k=new ArrayBuffer(8),q=new Float64Array(Yi()),new Float32Array(Yi()),y=new Int32Array(Yi()),Wi()[0]=-1,B=0!==Ki()[0]?1:0,C=1-Xi()|0)}function ne(n,t){var r;if(te(n)){var i,e=n.charCodeAt(t);if(Ai(),e<0?i=!0:(Ai(),i=e>65535),i)throw _u(\"Invalid Char code: \"+e);r=Ue(e)}else r=n.y3(t);return r}function te(n){return\"string\"==typeof n}function re(n){return te(n)?n.length:n.x3()}function ie(n,t,r){return te(n)?n.substring(t,r):n.z3(t,r)}function ee(n){return ce(n)}function ue(n,t){var r;switch(typeof n){case\"number\":r=\"number\"==typeof t?oe(n,t):t instanceof ke?oe(n,t.w8()):fe(n,t);break;case\"string\":case\"boolean\":r=fe(n,t);break;default:r=function(n,t){return n.a4(t)}(n,t)}return r}function oe(n,t){var r;if(n<t)r=-1;else if(n>t)r=1;else if(n===t){var i;if(0!==n)i=0;else{var e=1/n;i=e===1/t?0:e<0?-1:1}r=i}else r=n!=n?t!=t?0:1:-1;return r}function fe(n,t){return n<t?-1:n>t?1:0}function se(n){if(!(\"kotlinHashCodeValue$\"in n)){var t=4294967296*Math.random()|0,r=new Object;r.value=t,r.enumerable=!1,Object.defineProperty(n,\"kotlinHashCodeValue$\",r)}return n.kotlinHashCodeValue$}function ce(n){return null==n?\"null\":function(n){return!!Ye(n)||Fn(n)}(n)?\"[...]\":n.toString()}function ae(n){if(null==n)return 0;var t;switch(typeof n){case\"object\":t=\"function\"==typeof n.hashCode?n.hashCode():se(n);break;case\"function\":t=se(n);break;case\"number\":t=function(n){return Ji(),(0|n)===n?Ge(n):(Wi()[0]=n,Mn(Ki()[(Ji(),C)],31)+Ki()[Xi()]|0)}(n);break;case\"boolean\":t=n?1:0;break;default:t=he(String(n))}return t}function he(n){var t=0,r=0,i=n.length-1|0;if(r<=i)do{var e=r;r=r+1|0;var u=n.charCodeAt(e);t=Mn(t,31)+u|0}while(e!==i);return t}function le(n,t){return null==n?null==t:null!=t&&(\"object\"==typeof n&&\"function\"==typeof n.equals?n.equals(t):n!=n?t!=t:\"number\"==typeof n&&\"number\"==typeof t?n===t&&(0!==n||1/n==1/t):n===t)}function _e(n,t){null!=Error.captureStackTrace?Error.captureStackTrace(n,t):n.stack=(new Error).stack}function ve(n){return n.prototype}function de(n){return Object.create(n)}function ge(n,t,r){Error.call(n),function(n,t,r){var i=eu(Object.getPrototypeOf(n));if(!(1&i)){var e;if(null==t){var u;if(null!==t){var o=null==r?null:r.toString();u=null==o?A:o}else u=A;e=u}else e=t;n.message=e}2&i||(n.cause=r),n.name=Object.getPrototypeOf(n).constructor.name}(n,t,r)}function we(n){var t;return null==n?function(){throw Eu()}():t=n,t}function be(){throw Nu()}function pe(){throw Mu()}function me(){j=this,this.x8_1=new ke(0,-2147483648),this.y8_1=new ke(-1,2147483647),this.z8_1=8,this.a9_1=64}function ke(n,t){null==j&&new me,gr.call(this),this.u8_1=n,this.v8_1=t}function qe(){return $e(),P}function ye(){return $e(),I}function Be(){return $e(),S}function Ce(){return $e(),T}function xe(){return $e(),E}function je(n,t){if($e(),Te(n,t))return 0;var r=Ne(n),i=Ne(t);return r&&!i?-1:!r&&i?1:Ne(Ie(n,t))?-1:1}function Pe(n,t){$e();var r=n.v8_1>>>16|0,i=65535&n.v8_1,e=n.u8_1>>>16|0,u=65535&n.u8_1,o=t.v8_1>>>16|0,f=65535&t.v8_1,s=t.u8_1>>>16|0,c=0,a=0,h=0,l=0;return c=(c=c+((a=(a=a+((h=(h=h+((l=l+(u+(65535&t.u8_1)|0)|0)>>>16|0)|0)+(e+s|0)|0)>>>16|0)|0)+(i+f|0)|0)>>>16|0)|0)+(r+o|0)|0,new ke((h&=65535)<<16|(l&=65535),(c&=65535)<<16|(a&=65535))}function Ie(n,t){return $e(),Pe(n,t.e9())}function Se(n,t){if($e(),Ae(n))return qe();if(Ae(t))return qe();if(Te(n,Ce()))return Me(t)?Ce():qe();if(Te(t,Ce()))return Me(n)?Ce():qe();if(Ne(n))return Ne(t)?Se(Fe(n),Fe(t)):Fe(Se(Fe(n),t));if(Ne(t))return Fe(Se(n,Fe(t)));if(De(n,xe())&&De(t,xe()))return Oe(ze(n)*ze(t));var r=n.v8_1>>>16|0,i=65535&n.v8_1,e=n.u8_1>>>16|0,u=65535&n.u8_1,o=t.v8_1>>>16|0,f=65535&t.v8_1,s=t.u8_1>>>16|0,c=65535&t.u8_1,a=0,h=0,l=0,_=0;return l=l+((_=_+Mn(u,c)|0)>>>16|0)|0,_&=65535,h=(h=h+((l=l+Mn(e,c)|0)>>>16|0)|0)+((l=(l&=65535)+Mn(u,s)|0)>>>16|0)|0,l&=65535,a=(a=(a=a+((h=h+Mn(i,c)|0)>>>16|0)|0)+((h=(h&=65535)+Mn(e,s)|0)>>>16|0)|0)+((h=(h&=65535)+Mn(u,f)|0)>>>16|0)|0,h&=65535,a=a+(((Mn(r,c)+Mn(i,s)|0)+Mn(e,f)|0)+Mn(u,o)|0)|0,new ke(l<<16|_,(a&=65535)<<16|h)}function ze(n){return $e(),4294967296*n.v8_1+function(n){return $e(),n.u8_1>=0?n.u8_1:4294967296+n.u8_1}(n)}function Te(n,t){return $e(),n.v8_1===t.v8_1&&n.u8_1===t.u8_1}function Ee(n,t){if($e(),t<2||36<t)throw mu(\"radix out of range: \"+t);if(Ae(n))return\"0\";if(Ne(n)){if(Te(n,Ce())){var r=Le(t),i=n.d9(r),e=Ie(Se(i,r),n).g9();return Ee(i,t)+e.toString(t)}return\"-\"+Ee(Fe(n),t)}for(var u=2===t?31:t<=10?9:t<=21?7:t<=35?6:5,o=Oe(Math.pow(t,u)),f=n,s=\"\";;){var c=f.d9(o),a=Ie(f,Se(c,o)).g9().toString(t);if(Ae(f=c))return a+s;for(;a.length<u;)a=\"0\"+a;s=a+s}}function Le(n){return $e(),new ke(n,n<0?-1:0)}function Ne(n){return $e(),n.v8_1<0}function Ae(n){return $e(),0===n.v8_1&&0===n.u8_1}function Me(n){return $e(),!(1&~n.u8_1)}function Fe(n){return $e(),n.e9()}function De(n,t){return $e(),je(n,t)<0}function Oe(n){if($e(),(t=n)!=t)return qe();if(n<=-0x8000000000000000)return Ce();if(n+1>=0x8000000000000000)return $e(),z;if(n<0)return Fe(Oe(-n));var t,r=4294967296;return new ke(n%r|0,n/r|0)}function Re(n,t){return $e(),je(n,t)>0}function He(n,t){return $e(),je(n,t)>=0}function $e(){L||(L=!0,P=Le(0),I=Le(1),S=Le(-1),z=new ke(-1,2147483647),T=new ke(0,-2147483648),E=Le(16777216))}function Ge(n){return n instanceof ke?n.g9():function(n){return n>2147483647?2147483647:n<-2147483648?-2147483648:0|n}(n)}function Ue(n){var t;return t=function(n){return n<<16>>16}(Ge(n)),function(n){return 65535&n}(t)}function Ve(n,t){return new Zt(n,t)}function Qe(n,t,r,i){return Ze(\"class\",n,t,r,i,null)}function Ze(n,t,r,i,e,u){return{kind:n,simpleName:t,associatedObjectKey:r,associatedObjects:i,suspendArity:e,$kClass$:A,iid:u}}function Ye(n){return Array.isArray(n)}function We(n,t,r,i,e,u,o,f){null!=i&&(n.prototype=Object.create(i.prototype),n.prototype.constructor=n);var s=r(t,u,o,null==f?[]:f);n.$metadata$=s,null!=e&&((null!=s.iid?n:n.prototype).$imask$=function(n){for(var t=1,r=[],i=0,e=n.length;i<e;){var u=n[i];i=i+1|0;var o=t,f=u.prototype.$imask$,s=null==f?u.$imask$:f;null!=s&&(r.push(s),o=s.length);var c=u.$metadata$.iid,a=null==c?null:(l=void 0,v=1<<(31&(h=c)),(l=new Int32Array(1+(h>>5)|0))[_=h>>5]=l[_]|v,l);null!=a&&(r.push(a),o=Math.max(o,a.length)),o>t&&(t=o)}var h,l,_,v;return function(n,t){for(var r=0,i=new Int32Array(n);r<n;){for(var e=r,u=0,o=0,f=t.length;o<f;){var s=t[o];o=o+1|0,e<s.length&&(u|=s[e])}i[e]=u,r=r+1|0}return i}(t,r)}(e))}function Ke(n,t){return function(n,t){var r=n.$imask$;return null!=r&&function(n,t){var r=t>>5;if(r>n.length)return!1;var i=1<<(31&t);return!!(n[r]&i)}(r,t)}(n,t.$metadata$.iid)}function Xe(n){return!!Ye(n)&&!n.$type$}function Je(n){var t;switch(typeof n){case\"string\":case\"number\":case\"boolean\":case\"function\":t=!0;break;default:t=n instanceof Object}return t}function nu(n){return\"string\"==typeof n||Ke(n,vr)}function tu(n,t,r,i){return Ze(\"interface\",n,t,r,i,(null==N&&(N=0),N=ru()+1|0,ru()))}function ru(){if(null!=N)return N;!function(){throw Du(\"lateinit property iid has not been initialized\")}()}function iu(n,t,r,i){return Ze(\"object\",n,t,r,i,null)}function eu(n){var t=n.constructor,r=null==t?null:t.$metadata$,i=null==r?null:r.errorInfo;if(null!=i)return i;var e,u=0;if(uu(n,\"message\")&&(u|=1),uu(n,\"cause\")&&(u|=2),3!==u){var o=(e=n,Object.getPrototypeOf(e));o!=Error.prototype&&(u|=eu(o))}return null!=r&&(r.errorInfo=u),u}function uu(n,t){return n.hasOwnProperty(t)}function ou(n){return new Gr(n)}function fu(n,t,r){for(var i=new Int32Array(r),e=0,u=0,o=0,f=0,s=n.length;f<s;){var c=ne(n,f);f=f+1|0;var a=t[c];if(u|=(31&a)<<o,a<32){var h=e;e=h+1|0,i[h]=u,u=0,o=0}else o=o+5|0}return i}function su(n,t){for(var r=0,i=n.length-1|0,e=-1,u=0;r<=i;)if(t>(u=n[e=(r+i|0)/2|0]))r=e+1|0;else{if(t===u)return e;i=e-1|0}return e-(t<u?1:0)|0}function cu(){M=this;var n=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",t=new Int32Array(128),r=0,i=re(n)-1|0;if(r<=i)do{var e=r;r=r+1|0,t[ne(n,e)]=e}while(r<=i);var u=fu(\"hCgBpCQGYHZH5BRpBPPPPPPRMP5BPPlCPP6BkEPPPPcPXPzBvBrB3BOiDoBHwD+E3DauCnFmBmB2D6E1BlBTiBmBlBP5BhBiBrBvBjBqBnBPRtBiCmCtBlB0BmB5BiB7BmBgEmChBZgCoEoGVpBSfRhBPqKQ2BwBYoFgB4CJuTiEvBuCuDrF5DgEgFlJ1DgFmBQtBsBRGsB+BPiBlD1EIjDPRPPPQPPPPPGQSQS/DxENVNU+B9zCwBwBPPCkDPNnBPqDYY1R8B7FkFgTgwGgwUwmBgKwBuBScmEP/BPPPPPPrBP8B7F1B/ErBqC6B7BiBmBfQsBUwCw/KwqIwLwETPcPjQgJxFgBlBsD\",t,222),o=new Int32Array(u.length),f=0,s=u.length-1|0;if(f<=s)do{var c=f;f=f+1|0,o[c]=0===c?u[c]:o[c-1|0]+u[c]|0}while(f<=s);this.h9_1=o,this.i9_1=fu(\"aaMBXHYH5BRpBPPPPPPRMP5BPPlCPPzBDOOPPcPXPzBvBjB3BOhDmBBpB7DoDYxB+EiBP1DoExBkBQhBekBPmBgBhBctBiBMWOOXhCsBpBkBUV3Ba4BkB0DlCgBXgBtD4FSdBfPhBPpKP0BvBXjEQ2CGsT8DhBtCqDpFvD1D3E0IrD2EkBJrBDOBsB+BPiBlB1EIjDPPPPPPPPPPPGPPMNLsBNPNPKCvBvBPPCkDPBmBPhDXXgD4B6FzEgDguG9vUtkB9JcuBSckEP/BPPPPPPBPf4FrBjEhBpC3B5BKaWPrBOwCk/KsCuLqDHPbPxPsFtEaaqDL\",t,222),this.j9_1=fu(\"GFjgggUHGGFFZZZmzpz5qB6s6020B60ptltB6smt2sB60mz22B1+vv+8BZZ5s2850BW5q1ymtB506smzBF3q1q1qB1q1q1+Bgii4wDTm74g3KiggxqM60q1q1Bq1o1q1BF1qlrqrBZ2q5wprBGFZWWZGHFsjiooLowgmOowjkwCkgoiIk7ligGogiioBkwkiYkzj2oNoi+sbkwj04DghhkQ8wgiYkgoioDsgnkwC4gikQ//v+85BkwvoIsgoyI4yguI0whiwEowri4CoghsJowgqYowgm4DkwgsY/nwnzPowhmYkg6wI8yggZswikwHgxgmIoxgqYkwgk4DkxgmIkgoioBsgssoBgzgyI8g9gL8g9kI0wgwJoxgkoC0wgioFkw/wI0w53iF4gioYowjmgBHGq1qkgwBF1q1q8qBHwghuIwghyKk0goQkwgoQk3goQHGFHkyg0pBgxj6IoinkxDswno7Ikwhz9Bo0gioB8z48Rwli0xN0mpjoX8w78pDwltoqKHFGGwwgsIHFH3q1q16BFHWFZ1q10q1B2qlwq1B1q10q1B2q1yq1B6q1gq1Biq1qhxBir1qp1Bqt1q1qB1g1q1+B//3q16B///q1qBH/qlqq9Bholqq9B1i00a1q10qD1op1HkwmigEigiy6Cptogq1Bixo1kDq7/j00B2qgoBWGFm1lz50B6s5q1+BGWhggzhwBFFhgk4//Bo2jigE8wguI8wguI8wgugUog1qoB4qjmIwwi2KgkYHHH4lBgiFWkgIWoghssMmz5smrBZ3q1y50B5sm7gzBtz1smzB5smz50BqzqtmzB5sgzqzBF2/9//5BowgoIwmnkzPkwgk4C8ys65BkgoqI0wgy6FghquZo2giY0ghiIsgh24B4ghsQ8QF/v1q1OFs0O8iCHHF1qggz/B8wg6Iznv+//B08QgohsjK0QGFk7hsQ4gB\",t,222)}function au(){return null==M&&new cu,M}function hu(){F=this,this.k9_1=new Int32Array([170,186,688,704,736,837,890,7468,7544,7579,8305,8319,8336,8560,9424,11388,42652,42864,43e3,43868]),this.l9_1=new Int32Array([1,1,9,2,5,1,1,63,1,37,1,1,13,16,26,2,2,1,2,4])}function lu(){return null==F&&new hu,F}function _u(n){var t=function(n,t){return yu(n,t),vu.call(t),t}(n,de(ve(vu)));return _e(t,_u),t}function vu(){_e(this,vu)}function du(n){var t=function(n,t){return yu(n,t),gu.call(t),t}(n,de(ve(gu)));return _e(t,du),t}function gu(){_e(this,gu)}function wu(n){var t=function(n,t){return yu(n,t),bu.call(t),t}(n,de(ve(bu)));return _e(t,wu),t}function bu(){_e(this,bu)}function pu(n,t){return ge(t,n),ku.call(t),t}function mu(n){var t=pu(n,de(ve(ku)));return _e(t,mu),t}function ku(){_e(this,ku)}function qu(n){return function(n){ge(n),ku.call(n)}(n),Bu.call(n),n}function yu(n,t){return pu(n,t),Bu.call(t),t}function Bu(){_e(this,Bu)}function Cu(){var n,t=(qu(n=de(ve(ju))),ju.call(n),n);return _e(t,Cu),t}function xu(n){var t=function(n,t){return yu(n,t),ju.call(t),t}(n,de(ve(ju)));return _e(t,xu),t}function ju(){_e(this,ju)}function Pu(n){var t=function(n,t){return yu(n,t),Iu.call(t),t}(n,de(ve(Iu)));return _e(t,Pu),t}function Iu(){_e(this,Iu)}function Su(){var n,t=(qu(n=de(ve(Tu))),Tu.call(n),n);return _e(t,Su),t}function zu(n){var t=function(n,t){return yu(n,t),Tu.call(t),t}(n,de(ve(Tu)));return _e(t,zu),t}function Tu(){_e(this,Tu)}function Eu(){var n,t=(qu(n=de(ve(Lu))),Lu.call(n),n);return _e(t,Eu),t}function Lu(){_e(this,Lu)}function Nu(){var n,t=(qu(n=de(ve(Au))),Au.call(n),n);return _e(t,Nu),t}function Au(){_e(this,Au)}function Mu(){var n,t=(qu(n=de(ve(Fu))),Fu.call(n),n);return _e(t,Mu),t}function Fu(){_e(this,Fu)}function Du(n){var t=function(n,t){return yu(n,t),Ou.call(t),t}(n,de(ve(Ou)));return _e(t,Du),t}function Ou(){_e(this,Ou)}function Ru(n,t){var r,i=n.className;return(r=\"(^|.*\\\\s+)\"+t+\"($|\\\\s+.*)\",function(n,t){return Si.call(t,n,Ht()),t}(r,de(ve(Si)))).a8(i)}function Hu(n,t){this.o9_1=n,this.p9_1=t}function $u(n){this.q9_1=n}function Gu(n,t,r){var i,e=Gf(),u=ro(),o=$f().ga(t),f=ro();if(0===re(r))i=Of();else{var s=n.ia_1,c=null==s?null:new $u(s).v9(r,\"Copy reference to the clipboard\");i=null==c?Of():c}return e.ja([u,o,f,i])}function Uu(n){n=n===A?null:n,this.ia_1=n}function Vu(n,t,r){to.call(this),this.ma_1=n,this.na_1=t,this.oa_1=r}function Qu(n,t){this.ra_1=n,this.sa_1=t}function Zu(n,t){to.call(this),this.va_1=n,this.wa_1=t}function Yu(n,t){to.call(this),this.xa_1=n,this.ya_1=t}function Wu(n){to.call(this),this.za_1=n}function Ku(n){to.call(this),this.ab_1=n}function Xu(n){to.call(this),this.bb_1=n}function Ju(n,t){to.call(this),this.cb_1=n,this.db_1=t}function no(n){to.call(this),this.eb_1=n}function to(){}function ro(){return so(),D}function io(){return so(),O}function eo(){return so(),R}function uo(){return so(),H}function oo(n){return so(),Gf().fb(us(fo),n)}function fo(n){return so(),n.gb([\"invisible-text\",\"text-for-copy\"]),br()}function so(){$||($=!0,D=oo(\"`\"),O=oo(\" \"),R=oo(\"(\"),H=oo(\")\"))}function co(n,t){to.call(this),this.hb_1=n,this.ib_1=t}function ao(n){to.call(this),this.jb_1=n}function ho(n,t){to.call(this),this.kb_1=n,this.lb_1=t}function lo(n){to.call(this),this.mb_1=n}function _o(n){to.call(this),this.nb_1=n}function vo(n){to.call(this),this.ob_1=n}function go(n,t,r){to.call(this),this.pb_1=n,this.qb_1=t,this.rb_1=r}function wo(n){to.call(this),this.sb_1=n}function bo(n){to.call(this),this.tb_1=n}function po(n){return n.xb_1.vb_1.k()}function mo(){if(Z)return br();Z=!0,G=new jo(\"Inputs\",0,\"Build configuration inputs\"),U=new jo(\"ByMessage\",1,\"Problems grouped by message\"),V=new jo(\"ByLocation\",2,\"Problems grouped by location\"),Q=new jo(\"IncompatibleTasks\",3,\"Incompatible tasks\")}function ko(n){Fc.call(this),this.yb_1=n}function qo(n){Fc.call(this),this.ac_1=n}function yo(n){Fc.call(this),this.bc_1=n}function Bo(n){Fc.call(this),this.cc_1=n}function Co(n){Po.call(this),this.dc_1=n}function xo(n,t,r,i,e,u,o,f){this.ec_1=n,this.fc_1=t,this.gc_1=r,this.hc_1=i,this.ic_1=e,this.jc_1=u,this.kc_1=o,this.lc_1=f}function jo(n,t,r){Ui.call(this,n,t),this.qc_1=r}function Po(){Oc.call(this)}function Io(n,t){var r=Hf(),i=us(Oo),e=Hf().y9(us(Ro),[]),u=function(n,t){var r,i=Hf(),e=us(Vo),u=Gf().ga(\"Learn more about the \"),o=Yf();return i.y9(e,[u,o.fb(us((r=t,function(n){return n.bd(r.tc_1),br()})),t.sc_1),Gf().ga(\".\")])}(0,t.gc_1),o=Hf().y9(us(Ho),[zo(0,t)]),f=Hf();return r.y9(i,[e,u,o,f.y9(us($o),[No(0,Qo(),t.lc_1,po(t.jc_1)),No(0,Zo(),t.lc_1,po(t.hc_1)),No(0,Yo(),t.lc_1,po(t.ic_1)),No(0,Wo(),t.lc_1,po(t.kc_1))])])}function So(n,t){var r,i,e=Hf(),u=us(Go);switch(t.lc_1.q8_1){case 0:r=Mo(0,t.jc_1,((i=function(n){return new yo(n)}).callableName=\"<init>\",i));break;case 3:r=Mo(0,t.kc_1,function(){var n=function(n){return new Bo(n)};return n.callableName=\"<init>\",n}());break;case 1:r=Mo(0,t.hc_1,function(){var n=function(n){return new qo(n)};return n.callableName=\"<init>\",n}());break;case 2:r=Mo(0,t.ic_1,function(){var n=function(n){return new ko(n)};return n.callableName=\"<init>\",n}());break;default:be()}return e.y9(u,[r])}function zo(n,t){return Hf().ja([Lo(0,t),To(0,t)])}function To(n,t){for(var r=Hf(),i=t.fc_1,e=Or(),u=0,o=i.f();o.g();){var f=o.h(),s=u;u=s+1|0,Et(e,0===yr(s)?qr(Eo(Xo(),f)):Bt([Wf().ja([]),Eo(Xo(),f)]))}return r.ha(e)}function Eo(n,t){return Uf().ja([Uc(t)])}function Lo(n,t){return Rf().ja([$c().ka(t.ec_1)])}function No(n,t,r,i){var e,u,o;return Hf().y9(us((e=i,u=t,o=r,function(n){return n.t9(\"group-selector\"),0===e?(n.t9(\"group-selector--disabled\"),br()):u.equals(o)?(n.t9(\"group-selector--active\"),br()):(n.u9(function(n){return function(t){return new Co(n)}}(u)),br()),br()})),[Gf().rc(t.qc_1,[Ao(0,i)])])}function Ao(n,t){return Gf().y9(us(Uo),[io(),eo(),Gf().ga(\"\"+t),uo()])}function Mo(n,t,r){return function(n,t,r){var i,e=Hf(),u=Vf();return e.ja([u.ha(Ps(t,(i=r,function(n){var t,r=n.cd().ub_1;return r instanceof Zu?Kc(i,(Xo(),(t=function(n){return Fo(0,n)}).callableName=\"viewNode\",t),n,r.va_1,r.wa_1,Rc()):r instanceof Yu?Kc(i,function(){var n=function(n){return Fo(0,n)};return n.callableName=\"viewNode\",n}(Xo()),n,r.xa_1,r.ya_1,Hc()):r instanceof co?Kc(i,function(){var n=function(n){return Fo(0,n)};return n.callableName=\"viewNode\",n}(Xo()),n,r.hb_1,r.ib_1,A,Ao(Xo(),n.cd().vb_1.k())):r instanceof Vu?Wc(i,n,r):Kc(i,function(){var n=function(n){return Fo(0,n)};return n.callableName=\"viewNode\",n}(Xo()),n,r)})))])}(0,t.xb_1.uc().vc(),r)}function Fo(n,t){var r;return t instanceof ao?Vc((r=t,function(n){return n.ed(\"project \"),n.fd(r.jb_1),br()})):t instanceof go?Vc(function(n){return function(t){return t.ed(n.pb_1+\" \"),t.fd(n.qb_1),t.ed(\" of \"),t.fd(n.rb_1),br()}}(t)):t instanceof vo?Vc(function(n){return function(t){return t.ed(\"system property \"),t.fd(n.ob_1),br()}}(t)):t instanceof ho?Vc(function(n){return function(t){return t.ed(\"task \"),t.fd(n.kb_1),t.ed(\" of type \"),t.fd(n.lb_1),br()}}(t)):t instanceof _o?Vc(function(n){return function(t){return t.ed(\"bean of type \"),t.fd(n.nb_1),br()}}(t)):t instanceof wo?Vc(function(n){return function(t){return t.ed(n.sb_1),br()}}(t)):t instanceof bo?Vc(function(n){return function(t){return t.ed(\"class \"),t.fd(n.tb_1),br()}}(t)):t instanceof no?Vc(function(n){return function(t){return t.ed(n.eb_1),br()}}(t)):t instanceof Wu?Uc(t.za_1):t instanceof Ju?Jo(t):Gf().ga(ce(t))}function Do(n){return n.t9(\"report-wrapper\"),br()}function Oo(n){return n.t9(\"header\"),br()}function Ro(n){return n.t9(\"gradle-logo\"),br()}function Ho(n){return n.t9(\"title\"),br()}function $o(n){return n.t9(\"groups\"),br()}function Go(n){return n.t9(\"content\"),br()}function Uo(n){return n.t9(\"group-selector__count\"),br()}function Vo(n){return n.t9(\"learn-more\"),br()}function Qo(){return mo(),G}function Zo(){return mo(),U}function Yo(){return mo(),V}function Wo(){return mo(),Q}function Ko(){Y=this}function Xo(){return null==Y&&new Ko,Y}function Jo(n){var t;return Yf().fb(us((t=n,function(n){return n.t9(\"documentation-button\"),n.bd(t.cb_1),br()})),n.db_1)}function nf(n,t,r){this.kd_1=n,this.ld_1=t,this.md_1=r}function tf(n,t,r){this.nd_1=n,this.od_1=t,this.pd_1=r}function rf(n,t){for(var r=mf(n),i=t.trace,e=Rr(i.length),u=0,o=i.length;u<o;){var f,s=i[u];u=u+1|0,f=of(s),e.d(f)}return new nf(t,r,e)}function ef(n,t){var r,i=null==(r=t.kd_1.error)?null:ff(r);null==i||n.d(i)}function uf(n){return function(n,t,r){var i=null==n.error?null:new Zu(t,r);return null==i?new Yu(t,r):i}(n.kd_1,new Wu(n.ld_1),af(n.kd_1))}function of(n){var t;switch(n.kind){case\"Project\":t=new ao(n.path);break;case\"Task\":t=new ho(n.path,n.type);break;case\"TaskPath\":t=new lo(n.path);break;case\"Bean\":t=new _o(n.type);break;case\"Field\":t=new go(\"field\",n.name,n.declaringType);break;case\"InputProperty\":t=new go(\"input property\",n.name,n.task);break;case\"OutputProperty\":t=new go(\"output property\",n.name,n.task);break;case\"SystemProperty\":t=new vo(n.name);break;case\"PropertyUsage\":t=new go(\"property\",n.name,n.from);break;case\"BuildLogic\":t=new wo(n.location);break;case\"BuildLogicClass\":t=new bo(n.type);break;default:t=new no(\"Gradle runtime\")}return t}function ff(n){var t=n.parts;if(null==t){var r=n.summary;return null==r?null:new Wu(mf(r))}for(var i=n.summary,e=null==i?null:mf(i),u=Or(),o=Qi(t);o.g();){var f=cf(o.h());null==f||u.d(f)}for(var s=$n(u,\"\\n\"),c=Or(),a=Qi(t);a.g();){var h=sf(a.h());null==h||c.d(h)}return new Vu(e,s,c)}function sf(n){var t=cf(n);if(null==t)return null;var r,i,e=ot(new Rt(function(n,t,r,i){var e;return ut(or(n,[\"\\r\\n\",\"\\n\",\"\\r\"],A,r=r!==A&&r,i=i===A?0:i),(e=n,function(n){return ur(e,n)}))}(t),!0,bf));return new Qu(e,(r=!(null==n.internalText),i=e.k(),r&&i>1?Cs():null))}function cf(n){var t=n.text;return null==t?n.internalText:t}function af(n){var t=n.documentationLink;return null==t?null:new Ju(t,\"\")}function hf(n,t){return new ds(lf(n,Ef().sd(t),Cs()))}function lf(n,t,r){return new js(n,function(n,t){var r,i=ut(Vn(n.o()),Nf);return ot(ut(new st(i,new _f(pf)),(r=t,function(n){return lf(n.v3(),n.w3().wd_1,r)})))}(t,1===Lf(t)?xs():Cs()),0===Lf(t)?Cs():r)}function _f(n){this.td_1=n}function vf(n){var t=Or(),r=n.ld_1,i=Un(r.ca_1).fa_1,e=ce(er(nu(i)?i:pe())),u=r.vd(function(n,t){var r;if(!(t>=0))throw _u(ce(\"Requested element count \"+t+\" is less than zero.\"));if(0===t)return Qn(n);if(Ke(n,Di)){var i=n.k()-t|0;if(i<=0)return Ct();if(1===i)return qr(function(n){if(Ke(n,Fi))return Jn(n);var t=n.f();if(!t.g())throw xu(\"Collection is empty.\");for(var r=t.h();t.g();)r=t.h();return r}(n));if(r=Rr(),Ke(n,Fi)){if(Ke(n,bi)){var e=t,u=n.k();if(e<u)do{var o=e;e=e+1|0,r.d(n.j(o))}while(e<u)}else for(var f=n.l(t);f.g();){var s=f.h();r.d(s)}return r}}else r=Or();for(var c=0,a=n.f();a.g();){var h=a.h();c>=t?r.d(h):c=c+1|0}return xt(r)}(r.ca_1,1));return t.d(new co(new no(e),af(n.kd_1))),t.d(new Wu(u)),t.m(n.md_1),t.j5()}function df(n){var t=Or(),r=n.ld_1,i=r.vd(r.ca_1);return t.d(new Yu(new Wu(i),af(n.kd_1))),t.j5()}function gf(n){var t=Or();return t.d(uf(n)),t.m(n.md_1),ef(t,n),t.j5()}function wf(n){var t=Or();return t.m(new At(n.md_1)),t.d(uf(n)),ef(t,n),t.j5()}function bf(n){return re(n)>0}function pf(n,t){return function(n,t){return n===t?0:null==n?-1:null==t?1:ue(null!=n&&(\"string\"==(i=typeof(r=n))||\"boolean\"===i||function(n){return\"number\"==typeof n||n instanceof ke}(r)||Ke(r,dr))?n:pe(),t);var r,i}(Vi(n.v3()),Vi(t.v3()))}function mf(n){var t;return Sf().qd((t=n,function(n){for(var r=t,i=0,e=r.length;i<e;){var u=r[i];i=i+1|0;var o=u.text;null==o||n.ed(o);var f=u.name;null==f||(n.fd(f),br())}return br()}))}function kf(n,t){return(0!==(r=n)?r.toString():\"No\")+\" \"+qf(t,n)+\" \"+yf(n)+\" found\";var r}function qf(n,t){return t<2?n:n+\"s\"}function yf(n){return n<=1?\"was\":\"were\"}function Bf(n,t){this.sc_1=n,this.tc_1=t}function Cf(n){jf.call(this),this.fa_1=n}function xf(n,t){jf.call(this),this.da_1=n,this.ea_1=t}function jf(){}function Pf(){this.dd_1=Or()}function If(){W=this}function Sf(){return null==W&&new If,W}function zf(n){Sf(),this.ca_1=n}function Tf(){K=this}function Ef(){return null==K&&new Tf,K}function Lf(n){return n.k()}function Nf(n){var t=n.j1(),r=n.i1();return _r(t,new Af(Ke(r,Ri)?r:pe()))}function Af(n){Ef(),this.wd_1=n}function Mf(n,t,r){var i;Df(t,n,r),i=\"Component mounted at #\"+n.id+\".\",yi(),(yi(),g).j7(i)}function Ff(n){var t=document.getElementById(n);if(null==t)throw wu(\"'\"+n+\"' element missing\");return t}function Df(n,t,r){var i,e,u;i=n.z9(r),e=t,u=function(n,t,r){return function(i){return Df(n,r,n.ba(i,t)),br()}}(n,r,t),_s(),e.innerHTML=\"\",hs(e,i,u)}function Of(){return _s(),X}function Rf(){return _s(),J}function Hf(){return _s(),nn}function $f(){return _s(),tn}function Gf(){return _s(),rn}function Uf(){return _s(),en}function Vf(){return _s(),un}function Qf(){return _s(),on}function Zf(){return _s(),fn}function Yf(){return _s(),sn}function Wf(){return _s(),cn}function Kf(n){this.x9_1=n}function Xf(){an=this}function Jf(){return null==an&&new Xf,an}function ns(){hn=this,es.call(this)}function ts(){return null==hn&&new ns,hn}function rs(n,t,r,i){t=t===A?Ct():t,r=r===A?null:r,i=i===A?Ct():i,es.call(this),this.be_1=n,this.ce_1=t,this.de_1=r,this.ee_1=i}function is(){}function es(){Jf()}function us(n){_s();var t,r=Or();return n(new os((t=r,function(n){return t.d(n),br()}))),r}function os(n){this.r9_1=n}function fs(n,t){as.call(this),this.fe_1=n,this.ge_1=t}function ss(n){as.call(this),this.he_1=n}function cs(n,t){as.call(this),this.ie_1=n,this.je_1=t}function as(){}function hs(n,t,r){if(_s(),t instanceof rs)!function(n,t,r){var i=function(n,t,r){var i=n.createElement(t);return r(i),i}(we(n.ownerDocument),t,r);n.appendChild(i)}(n,t.be_1,(e=t,u=r,function(n){for(var t=e.ce_1.f();t.g();)ls(n,t.h(),u);var r=e.de_1;null==r||function(n,t){n.appendChild(we(n.ownerDocument).createTextNode(t))}(n,r);for(var i=e.ee_1.f();i.g();)hs(n,i.h(),u);return br()}));else if(t instanceof is){var i=t instanceof is?t:pe();hs(n,i.ke_1,function(n,t){return function(r){return n(t.le_1(r)),br()}}(r,i))}else if(le(t,ts()))return br();var e,u}function ls(n,t,r){var i,e;_s(),t instanceof cs?n.setAttribute(t.ie_1,t.je_1):t instanceof ss?function(n,t){for(var r=Or(),i=0,e=t.length;i<e;){var u=t[i];i=i+1|0,Ru(n,u)||r.d(u)}var o=r;if(!o.i()){var f=n.className,s=ce(er(nu(f)?f:pe())),c=Bi();c.p7(s),0!==re(s)&&c.p7(\" \"),Gn(o,c,\" \"),n.className=c.toString()}}(n,[t.he_1]):t instanceof fs&&n.addEventListener(t.fe_1,(i=r,e=t,function(n){return n.stopPropagation(),i(e.ge_1(n)),br()}))}function _s(){ln||(ln=!0,X=ts(),new Kf(\"hr\"),J=new Kf(\"h1\"),new Kf(\"h2\"),nn=new Kf(\"div\"),new Kf(\"pre\"),tn=new Kf(\"code\"),rn=new Kf(\"span\"),en=new Kf(\"small\"),un=new Kf(\"ol\"),on=new Kf(\"ul\"),fn=new Kf(\"li\"),sn=new Kf(\"a\"),cn=new Kf(\"br\"),new Kf(\"p\"))}function vs(n){gs.call(this),this.ne_1=n}function ds(n){this.xb_1=n}function gs(){}function ws(n){return n.me(A,A,n.wb_1.ad())}function bs(){_n=this}function ps(){return null==_n&&new bs,_n}function ms(){if(gn)return br();gn=!0,vn=new ys(\"Collapsed\",0),dn=new ys(\"Expanded\",1)}function ks(n){Bs.call(this),this.ve_1=n}function qs(n,t,r){Bs.call(this),this.se_1=n,this.te_1=t,this.ue_1=r}function ys(n,t){Ui.call(this,n,t)}function Bs(){}function Cs(){return ms(),vn}function xs(){return ms(),dn}function js(n,t,r){t=t===A?Ct():t,r=r===A?Cs():r,this.ub_1=n,this.vb_1=t,this.wb_1=r}function Ps(n,t){return ot(ut(n,(r=t,function(n){return function(n,t){var r,i=n.cd(),e=Zf(),u=t(n),o=i.vb_1;r=null==(i.wb_1.equals(xs())&&!o.i()?o:null)?null:function(n,t){return Qf().ha(function(n,t){return Ps(n.vc(),t)}(n,t))}(n,t);var f=r;return e.ja([u,null==f?Of():f])}(n,r)})));var r}function Is(){if(xn)return br();xn=!0,kn=new Ss(\"ByMessage\",0,\"Messages\"),qn=new Ss(\"ByGroup\",1,\"Group\"),yn=new Ss(\"ByFileLocation\",2,\"File Locations\"),Bn=new Ss(\"ByPluginLocation\",3,\"Plugin Locations\"),Cn=new Ss(\"ByTaskLocation\",4,\"Task Locations\")}function Ss(n,t,r){Ui.call(this,n,t),this.cf_1=r}function zs(n,t){this.df_1=n,this.ef_1=t}function Ts(n,t){this.ff_1=n,this.gf_1=t}function Es(n,t,r){ec();for(var i=Or(),e=li(),u=0,o=n.length;u<o;){var f=n[u];u=u+1|0;var s=f.locations;null==s||0===s.length?i.d(Os(f)):r(f,e)||i.d(Os(f))}var c=function(n,t,r){ec();for(var i=n.w1(),e=Rr(Tt(i,10)),u=i.f();u.g();){var o;o=u.h().t3_1,e.d(o)}var f=Zn(e);return t.i()||f.d(new js(new oc(Sf().rd(\"no location\"),!0),t)),r>0&&f.d(Ns(r)),f}(e,i,t);return new ds(new js(new uc(\"text\"),c))}function Ls(n){return ec(),t=n,function(n,r){var i,e=n.locations;if(null==e)i=null;else{for(var u=Or(),o=Qi(e);o.g();){var f=o.h();null!=t(f)&&u.d(f)}i=u}var s,c=i;if(null==c||c.i())s=!1;else{for(var a=c.f();a.g();){var h=a.h();As(r,we(t(h)),n,h)}s=!0}return s};var t}function Ns(n){return ec(),new js(new Wu(Sf().rd(n+\" more problem\"+(n>1?\"s have\":\" has\")+\" been skipped\")))}function As(n,t,r,i){ec();var e,u,o=n.v1(t);if(null==o){var f=Or(),s=_r(new js(new oc(Sf().qd((u=t,function(n){return n.fd(u),br()}))),f,xs()),f);n.h5(t,s),e=s}else e=o;e.u3_1.d(Os(r,i))}function Ms(n,t,r,i){var e,u;if(t=t===A?Or():t,r=r===A?li():r,i===A){var o=(ec(),mn);u=o+1|0,ec(),mn=u,e=o}else e=i;i=e,this.hf_1=n,this.if_1=t,this.jf_1=r,this.kf_1=i}function Fs(n,t){if(ec(),t.i())return null;for(var r,i=n,e=null,u=t.f();u.g();){var o=u.h();r=e;var f,s=i,c=o.ef_1+\" (\"+o.df_1+\")\",a=s.v1(c);if(null==a){var h=Or(),l=new Ms(new js(new oc(Sf().qd(Ys(o))),h,xs()),h);s.h5(c,l),f=l}else f=a;e=f,null==r||we(r).if_1.u(we(e).hf_1)||we(r).if_1.d(we(e).hf_1),i=we(e).jf_1}return e}function Ds(n,t){if(ec(),n.k()===t.length){var r;n:{var i=function(n,t){var r=t.length,i=Tt(n,10),e=Rr(Math.min(i,r)),u=0,o=n.f();t:for(;o.g();){var f,s=o.h();if(u>=r)break t;var c=u;u=c+1|0,f=_r(s,t[c]),e.d(f)}return e}(n,t);if(Ke(i,Di)&&i.i())r=!0;else{for(var e=i.f();e.g();){var u=e.h();if(u.t3_1.df_1!==u.u3_1.name||u.t3_1.ef_1!==u.u3_1.displayName){r=!1;break n}}r=!0}}return r}return!1}function Os(n,t,r){t=t===A?null:t,r=r!==A&&r,ec();var i=function(n,t,r){t=t===A?null:t,r=r!==A&&r,ec();var i=$s(function(n,t){return ec(),n&&null!=t.contextualLabel?we(t.contextualLabel):Rs(t)}(r,n),t).j5();return Hs(n,new Wu(i))}(n,t,r),e=function(n,t,r){r=r!==A&&r,ec();var i,e=n.problemDetails;if(null==e)i=null;else{var u,o=e[0].text,f=null==o?null:function(n,t,r,i){if(r=r!==A&&r,i=i===A?0:i,1===t.length){var e=t[0];if(0!==re(e))return function(n,t,r,i){sr(i);var e=0,u=rr(n,t,e,r);if(-1===u||1===i)return qr(ce(n));var o,f=i>0,s=Rr(f&&it(i,10));n:do{var c;if(c=ce(ie(n,e,u)),s.d(c),e=u+t.length|0,f&&s.k()===(i-1|0))break n;u=rr(n,t,e,r)}while(-1!==u);return o=ce(ie(n,e,re(n))),s.d(o),s}(n,e,r,i)}for(var u=function(n){return new ft(n)}(or(n,t,A,r,i)),o=Rr(Tt(u,10)),f=u.f();f.g();){var s;s=ur(n,f.h()),o.d(s)}return o}(o,[\"\\n\"]);if(null==f)u=null;else{for(var s=Rr(Tt(f,10)),c=f.f();c.g();){var a,h=c.h();a=Us(n)?Sf().qd(Ks(h)):Sf().rd(h),s.d(a)}u=s}var l,_=u;if(null==_)l=null;else{for(var v=Rr(Tt(_,10)),d=_.f();d.g();){var g;g=new js(new Wu(d.h())),v.d(g)}l=v}var w=null==l?null:Zn(l);i=null==w?Or():w}var b=i,p=null==b?Or():b;r||null==n.contextualLabel||p.d(new js(new Wu(Sf().rd(we(n.contextualLabel)))));var m=function(n){ec();var t=n.solutions;if(null==t||0===t.length)return null;for(var r=new Xu(Sf().rd(\"Solutions\")),i=we(n.solutions),e=Rr(i.length),u=0,o=i.length;u<o;){var f,s=i[u];u=u+1|0,f=new js(new Ku(mf(s))),e.d(f)}return new js(r,e)}(n);null==m||p.d(m);var k,q=n.error,y=null==q?null:ff(q);if(null==y||p.d(new js(y)),t){var B=n.locations;k=!(null==B||0===B.length)}else k=!1;return k&&p.d(function(n){ec();var t,r=n.locations;if(null==r)t=null;else{for(var i=Rr(r.length),e=Qi(r);e.g();){var u,o=e.h();u=new js(new Wu(Sf().qd(Xs(o)))),i.d(u)}t=i}var f=t;return new js(new no(\"Locations\"),null==f?Ct():f)}(n)),p}(n,null==t,r);return new js(i,e)}function Rs(n){return ec(),function(n){if(0===n.length)throw xu(\"Array is empty.\");return n[Rn(n)]}(n.problemId).displayName}function Hs(n,t){var r;switch(ec(),n.severity){case\"WARNING\":var i=n.documentationLink;r=new Yu(t,null==i?null:new Ju(i,\"\"));break;case\"ERROR\":var e=n.documentationLink;r=new Zu(t,null==e?null:new Ju(e,\"\"));break;case\"ADVICE\":var u=n.documentationLink;r=new fc(t,null==u?null:new Ju(u,\"\"));break;default:console.error(\"no severity \"+n.severity),r=t}return r}function $s(n,t){t=t===A?null:t,ec();var r,i=new Pf;if(i.ed(n),null==t);else if(null!=t.line){var e=Gs(t);i.xd(e+(r=t,ec(),null==r.line||null==r.length?\"\":\"-\"+r.length),\"\"+t.path+e),br()}else null!=t.taskPath?(i.fd(we(t.taskPath)),br()):null!=t.pluginId&&(i.fd(we(t.pluginId)),br());return i}function Gs(n){var t;if(ec(),null==n.line)t=null;else{var r,i=\":\"+n.line,e=n.column;t=i+(null==(r=null==e?null:\":\"+e)?\"\":r)}return null==t?\"\":t}function Us(n){var t;ec();var r,i=n.problemId;n:{for(var e=0,u=i.length;e<u;){var o=i[e];if(e=e+1|0,\"compilation\"===o.name){r=o;break n}}r=null}if(null!=r){var f,s=n.problemId;n:{for(var c=0,a=s.length;c<a;){var h=s[c];if(c=c+1|0,\"java\"===h.name){f=h;break n}}f=null}t=!(null==f)}else t=!1;return t}function Vs(n){return ec(),n.path}function Qs(n){return ec(),n.pluginId}function Zs(n){return ec(),n.taskPath}function Ys(n){return function(t){return t.ed(n.ef_1),t.fd(n.df_1),br()}}function Ws(n){return ec(),n.name}function Ks(n){return function(t){return t.xd(function(n,t,r,i){i=i!==A&&i;var e=new RegExp(Ii().t7(\" \"),i?\"gui\":\"gu\"),u=Ii().u7(\" \");return n.replace(e,u)}(n),\"\"),br()}}function Xs(n){return function(t){var r;return t.ed(\"- \"),t.fd((r=n,ec(),null!=r.path?\"\"+r.path+Gs(r):null!=r.taskPath?we(r.taskPath):\"<undefined>\")),br()}}function Js(){return Is(),kn}function nc(){return Is(),qn}function tc(){return Is(),yn}function rc(){return Is(),Bn}function ic(){return Is(),Cn}function ec(){jn||(jn=!0,wn=Ls(Vs),bn=Ls(Qs),pn=Ls(Zs),mn=0)}function uc(n){sc.call(this),this.lf_1=n}function oc(n,t){t=t!==A&&t,sc.call(this),this.mf_1=n,this.nf_1=t}function fc(n,t){t=t===A?null:t,to.call(this),this.of_1=n,this.pf_1=t}function sc(){to.call(this)}function cc(n){Fc.call(this),this.qf_1=n}function ac(n){Fc.call(this),this.rf_1=n}function hc(n){Fc.call(this),this.sf_1=n}function lc(n){Fc.call(this),this.tf_1=n}function _c(n){Fc.call(this),this.uf_1=n}function vc(n){gc.call(this),this.vf_1=n}function dc(n,t,r,i,e,u,o,f,s,c){this.wf_1=n,this.xf_1=t,this.yf_1=r,this.zf_1=i,this.ag_1=e,this.bg_1=u,this.cg_1=o,this.dg_1=f,this.eg_1=s,this.fg_1=c}function gc(){Oc.call(this)}function wc(n,t){var r=Or();po(t.zf_1)>0&&r.d(yc(0,Js(),t.fg_1,t.eg_1)),po(t.ag_1)>0&&r.d(yc(0,nc(),t.fg_1,t.eg_1)),po(t.bg_1)>0&&r.d(yc(0,tc(),t.fg_1,t.eg_1)),po(t.cg_1)>0&&r.d(yc(0,rc(),t.fg_1,t.eg_1)),po(t.dg_1)>0&&r.d(yc(0,ic(),t.fg_1,t.eg_1));var i=Hf(),e=us(jc),u=Hf().y9(us(Pc),[]),o=function(n,t){var r,i=Hf(),e=us(Ec),u=Gf().ga(\"Learn more about \"),o=Yf();return i.y9(e,[u,o.fb(us((r=t,function(n){return n.bd(r.tc_1),br()})),t.sc_1),Gf().ga(\".\")])}(0,t.yf_1),f=Hf().y9(us(Ic),[pc(0,t)]),s=Hf();return i.y9(e,[u,o,f,s.zd(us(Sc),r)])}function bc(n,t){var r,i,e=Hf(),u=us(zc);switch(t.fg_1.q8_1){case 0:r=Bc(0,t.zf_1,((i=function(n){return new cc(n)}).callableName=\"<init>\",i));break;case 1:r=Bc(0,t.ag_1,function(){var n=function(n){return new ac(n)};return n.callableName=\"<init>\",n}());break;case 2:r=Bc(0,t.bg_1,function(){var n=function(n){return new hc(n)};return n.callableName=\"<init>\",n}());break;case 3:r=Bc(0,t.cg_1,function(){var n=function(n){return new lc(n)};return n.callableName=\"<init>\",n}());break;case 4:r=Bc(0,t.dg_1,function(){var n=function(n){return new _c(n)};return n.callableName=\"<init>\",n}());break;default:be()}return e.y9(u,[r])}function pc(n,t){return Hf().ja([qc(0,t),mc(0,t)])}function mc(n,t){for(var r=Hf(),i=t.xf_1,e=Or(),u=0,o=i.f();o.g();){var f=o.h(),s=u;u=s+1|0,Et(e,0===yr(s)?qr(kc(Nc(),f)):Bt([Wf().ja([]),kc(Nc(),f)]))}return r.ha(e)}function kc(n,t){return Uf().ja([Uc(t)])}function qc(n,t){return Rf().ja([$c().ka(t.wf_1)])}function yc(n,t,r,i){var e,u,o,f;return Hf().y9(us((e=i,u=t,o=r,function(n){return n.t9(\"group-selector\"),0===e?(n.t9(\"group-selector--disabled\"),br()):u.equals(o)?(n.t9(\"group-selector--active\"),br()):(n.u9(function(n){return function(t){return new vc(n)}}(u)),br()),br()})),[Gf().rc(t.cf_1,[(f=i,Gf().y9(us(Tc),[io(),eo(),Gf().ga(\"\"+f),uo()]))])])}function Bc(n,t,r){return function(n,t,r){var i,e=Hf(),u=Vf();return e.ja([u.ha(Ps(t,(i=r,function(n){return function(n,t,r,i){var e,u;return t instanceof uc?Uc(Sf().rd(t.lf_1)):t instanceof oc?Hf().y9(us((u=t,function(n){return u.nf_1&&(n.t9(\"uncategorized\"),br()),br()})),[Hf().ja([Qc(r,i),Uc(t.mf_1)])]):t instanceof Vu?Wc(i,r,t):t instanceof Wu?Uc(t.za_1):t instanceof Ku?Hf().ja([(sa(),Tn),Uc(t.ab_1)]):t instanceof Xu?Hf().ja([Qc(r,i),Uc(t.bb_1)]):t instanceof Zu?Kc(i,((e=function(n){return Cc(0,n)}).callableName=\"viewIt\",e),r,t.va_1,t.wa_1,Rc()):t instanceof fc?Kc(i,function(){var n=function(n){return Cc(0,n)};return n.callableName=\"viewIt\",n}(),r,t.of_1,t.pf_1,(sa(),Sn)):t instanceof Yu?Kc(i,function(){var n=function(n){return Cc(0,n)};return n.callableName=\"viewIt\",n}(),r,t.xa_1,t.ya_1,Hc()):t instanceof no?Hf().ja([Qc(r,i),Uc(Sf().rd(t.eb_1))]):Gf().ga(\"Unknown node type viewNode: \"+t)}(Nc(),n.cd().ub_1,n,i)})))])}(0,t.xb_1.uc().vc(),r)}function Cc(n,t){var r;if(t instanceof Ju)r=Jo(t);else if(t instanceof no)r=Uc(Sf().rd(t.eb_1));else if(t instanceof Wu)r=Uc(t.za_1);else{var i=\"Unknown node type viewIt: \"+t;console.error(i),r=Gf().ga(i)}return r}function xc(n){return n.t9(\"report-wrapper\"),br()}function jc(n){return n.t9(\"header\"),br()}function Pc(n){return n.t9(\"gradle-logo\"),br()}function Ic(n){return n.t9(\"title\"),br()}function Sc(n){return n.t9(\"groups\"),br()}function zc(n){return n.t9(\"content\"),br()}function Tc(n){return n.t9(\"group-selector__count\"),br()}function Ec(n){return n.t9(\"learn-more\"),br()}function Lc(){Pn=this,document.title=\"Gradle - Problems Report\"}function Nc(){return null==Pn&&new Lc,Pn}function Ac(n,t,r){return n.pe(t.zb().oe(),r)}function Mc(n){Oc.call(this),this.hd_1=n}function Fc(){Oc.call(this)}function Dc(n,t){Oc.call(this),this.wc_1=n,this.xc_1=t}function Oc(){}function Rc(){return sa(),In}function Hc(){return sa(),zn}function $c(){return sa(),En}function Gc(){return sa(),Ln}function Uc(n){return sa(),Gc().ka(n)}function Vc(n){return sa(),Gc().ka(Sf().qd(n))}function Qc(n,t){return sa(),n.cd().ye()?Xc(n,t):function(n){return sa(),Gf().fb(us(oa),Jc(n))}(n)}function Zc(n,t,r,i){var e,u,o;return sa(),Gf().fb(us((e=r,u=t,o=i,function(n){return n.t9(\"java-exception-part-toggle\"),n.u9(function(n,t){return function(r){return new Dc(n,t())}}(u,o)),n.s9(\"Click to \"+function(n){var t;switch(sa(),n.q8_1){case 0:t=\"show\";break;case 1:t=\"hide\";break;default:be()}return t}(e)),br()})),\"(\"+n+\" internal \"+qf(\"line\",n)+\" \"+function(n){var t;switch(sa(),n.q8_1){case 0:t=\"hidden\";break;case 1:t=\"shown\";break;default:be()}return t}(r)+\")\")}function Yc(n,t){t=t===A?Of():t,sa();for(var r=Qf(),i=Rr(Tt(n,10)),e=0,u=n.f();u.g();){var o,f=e;e=f+1|0,s=u.h(),c=(c=0===yr(f)?t:Of())===A?Of():c,sa(),o=Zf().ja([$f().ga(s),c]),i.d(o)}var s,c;return r.ha(i)}function Wc(n,t,r){sa();var i,e,u,o=Hf(),f=Xc(t,n),s=Gf().ga(\"Exception\"),c=Gf().ja([(sa(),Nn).v9(r.na_1,\"Copy exception to the clipboard\")]),a=null==r.ma_1?null:Gf().ga(\" \"),h=null==a?Of():a,l=r.ma_1,_=null==l?null:Uc(l),v=null==_?Of():_;switch(t.cd().wb_1.q8_1){case 0:i=Of();break;case 1:i=function(n,t){sa();for(var r=Hf(),i=us(fa),e=n.oa_1,u=Rr(Tt(e,10)),o=0,f=e.f();f.g();){var s,c=f.h(),a=o;o=a+1|0;var h,l=yr(a);if(null!=c.sa_1){var _,v=Zc(c.ra_1.k(),l,c.sa_1,t),d=c.sa_1;switch(null==d?-1:d.q8_1){case 0:_=Yc(Kn(c.ra_1,1),v);break;case 1:_=Yc(c.ra_1,v);break;default:be()}h=_}else h=Yc(c.ra_1);s=h,u.d(s)}return r.zd(i,u)}(r,(e=n,u=t,function(){return e(new vs(u))}));break;default:be()}return o.ja([f,s,c,h,v,i])}function Kc(n,t,r,i,e,u,o){e=e===A?null:e,u=u===A?Of():u,o=o===A?Of():o,sa();var f=Hf(),s=Qc(r,n),c=t(i),a=null==e?null:t(e);return f.ja([s,u,c,null==a?Of():a,o])}function Xc(n,t){var r,i;return sa(),Gf().fb(us((r=n,i=t,function(n){return n.gb([\"invisible-text\",\"tree-btn\"]),r.cd().wb_1===Cs()&&(n.t9(\"collapsed\"),br()),r.cd().wb_1===xs()&&(n.t9(\"expanded\"),br()),n.s9(\"Click to \"+function(n){var t;switch(sa(),n.q8_1){case 0:t=\"expand\";break;case 1:t=\"collapse\";break;default:be()}return t}(r.cd().wb_1)),n.u9(function(n,t){return function(r){return n(new vs(t))}}(i,r)),br()})),Jc(n))}function Jc(n){return sa(),function(n,t){var r;if(!(t>=0))throw _u(ce(\"Count 'n' must be non-negative, but was \"+t+\".\"));switch(t){case 0:r=\"\";break;case 1:r=ce(n);break;default:var i=\"\";if(0!==re(n))for(var e=ce(n),u=t;1&~u||(i+=e),0!=(u=u>>>1|0);)e+=e;return i}return r}(\"    \",n.we()-1|0)+\"- \"}function na(n){return sa(),n.gb([\"invisible-text\",\"error-icon\"]),br()}function ta(n){return sa(),n.gb([\"invisible-text\",\"advice-icon\"]),br()}function ra(n){return sa(),n.gb([\"invisible-text\",\"warning-icon\"]),br()}function ia(n){return sa(),n.gb([\"invisible-text\",\"enum-icon\"]),br()}function ea(n){return sa(),new Mc(n)}function ua(n){return sa(),new Mc(n)}function oa(n){return sa(),n.gb([\"invisible-text\",\"leaf-icon\"]),br()}function fa(n){return sa(),n.t9(\"java-exception\"),br()}function sa(){if(!An){An=!0;var n=Gf();In=n.fb(us(na),\"[error] \");var t=Gf();Sn=t.fb(us(ta),\"[advice] \");var r=Gf();zn=r.fb(us(ra),\"[warn]  \");var i=Gf();Tn=i.fb(us(ia),\"[enum]  \"),En=new Uu,Ln=new Uu(ea),Nn=new $u(ua)}}return We(tt,A,Qe),We(ft,A,Qe),We(st,A,Qe),We(Di,\"Collection\",tu),We(ct,\"AbstractCollection\",Qe,A,[Di]),We(at,\"IteratorImpl\",Qe),We(ht,\"ListIteratorImpl\",Qe,at),We(lt,\"Companion\",iu),We(Fi,\"List\",tu,A,[Di]),We(vt,\"AbstractList\",Qe,ct,[ct,Fi]),We(dt,A,Qe),We(bt,\"Companion\",iu),We(mt,A,Qe,ct),We(Ri,\"Map\",tu),We(kt,\"AbstractMap\",Qe,A,[Ri]),We(qt,\"Companion\",iu),We(bi,\"RandomAccess\",tu),We(Pt,\"EmptyList\",iu,A,[Fi,bi]),We(It,\"ArrayAsCollection\",Qe,A,[Di]),We(St,\"EmptyIterator\",iu),We(Lt,\"IntIterator\",Qe),We(Nt,A,Qe),We(At,\"ReversedListReadOnly\",Qe,vt),We(Mt,A,Qe),We(Ft,\"TransformingSequence\",Qe),We(Ot,A,Qe),We(Rt,\"FilteringSequence\",Qe),We($i,\"Set\",tu,A,[Di]),We($t,\"EmptySet\",iu,A,[$i]),We(Vt,\"Companion\",iu),We(Xt,\"IntProgression\",Qe),We(Zt,\"IntRange\",Qe,Xt),We(Yt,\"IntProgressionIterator\",Qe,Lt),We(Wt,\"Companion\",iu),We(ar,A,Qe),We(hr,\"DelimitedRangesSequence\",Qe),We(lr,\"Pair\",Qe),We(vr,\"CharSequence\",tu),We(dr,\"Comparable\",tu),We(gr,\"Number\",Qe),We(wr,\"Unit\",iu),We(pr,\"IntCompanionObject\",iu),We(xr,\"AbstractMutableCollection\",Qe,ct,[ct,Di]),We(jr,\"IteratorImpl\",Qe),We(Pr,\"ListIteratorImpl\",Qe,jr),We(Ir,\"AbstractMutableList\",Qe,xr,[xr,Di,Fi]),We(Sr,A,Qe),We(zr,A,Qe),We(Oi,\"Entry\",tu),We(Hi,\"MutableEntry\",tu,A,[Oi]),We(Tr,\"SimpleEntry\",Qe,A,[Hi]),We(Mr,\"AbstractMutableSet\",Qe,xr,[xr,$i,Di]),We(Er,\"AbstractEntrySet\",Qe,Mr),We(Lr,A,Qe,Mr),We(Nr,A,Qe,xr),We(Ar,\"AbstractMutableMap\",Qe,kt,[kt,Ri]),We(Fr,\"Companion\",iu),We(Gr,\"ArrayList\",Qe,Ir,[Ir,Di,Fi,bi]),We(Qr,\"HashCode\",iu),We(Zr,\"EntrySet\",Qe,Er),We(Xr,\"HashMap\",Qe,Ar,[Ar,Ri]),We(ni,\"HashSet\",Qe,Mr,[Mr,$i,Di]),We(ei,A,Qe),We(oi,\"InternalMap\",tu),We(ui,\"InternalHashCodeMap\",Qe,A,[oi]),We(fi,\"EntryIterator\",Qe),We(si,\"Companion\",iu),We(ai,\"ChainEntry\",Qe,Tr),We(hi,\"EntrySet\",Qe,Er),We(vi,\"LinkedHashMap\",Qe,Xr,[Xr,Ri]),We(di,\"Companion\",iu),We(wi,\"LinkedHashSet\",Qe,ni,[ni,$i,Di]),We(pi,\"BaseOutput\",Qe),We(mi,\"NodeJsOutput\",Qe,pi),We(qi,\"BufferedOutput\",Qe,pi),We(ki,\"BufferedOutputToConsoleLog\",Qe,qi),We(Ci,\"StringBuilder\",Qe,A,[vr]),We(Pi,\"Companion\",iu),We(Si,\"Regex\",Qe),We(Ni,\"Companion\",iu),We(Mi,\"Char\",Qe,A,[dr]),We(Gi,\"Companion\",iu),We(Ui,\"Enum\",Qe,A,[dr]),We(Zi,A,Qe),We(me,\"Companion\",iu),We(ke,\"Long\",Qe,gr,[gr,dr]),We(cu,\"Letter\",iu),We(hu,\"OtherLowercase\",iu),We(ku,\"Exception\",Qe,Error),We(Bu,\"RuntimeException\",Qe,ku),We(vu,\"IllegalArgumentException\",Qe,Bu),We(gu,\"IndexOutOfBoundsException\",Qe,Bu),We(bu,\"IllegalStateException\",Qe,Bu),We(ju,\"NoSuchElementException\",Qe,Bu),We(Iu,\"ArithmeticException\",Qe,Bu),We(Tu,\"UnsupportedOperationException\",Qe,Bu),We(Lu,\"NullPointerException\",Qe,Bu),We(Au,\"NoWhenBranchMatchedException\",Qe,Bu),We(Fu,\"ClassCastException\",Qe,Bu),We(Ou,\"UninitializedPropertyAccessException\",Qe,Bu),We(Hu,\"Model\",Qe),We($u,\"CopyButtonComponent\",Qe),We(Uu,\"PrettyTextComponent\",Qe),We(to,\"ProblemNode\",Qe),We(Vu,\"Exception\",Qe,to),We(Qu,\"StackTracePart\",Qe),We(Zu,\"Error\",Qe,to),We(Yu,\"Warning\",Qe,to),We(Wu,\"Message\",Qe,to),We(Ku,\"ListElement\",Qe,to),We(Xu,\"TreeNode\",Qe,to),We(Ju,\"Link\",Qe,to),We(no,\"Label\",Qe,to),We(co,\"Info\",Qe,to),We(ao,\"Project\",Qe,to),We(ho,\"Task\",Qe,to),We(lo,\"TaskPath\",Qe,to),We(_o,\"Bean\",Qe,to),We(vo,\"SystemProperty\",Qe,to),We(go,\"Property\",Qe,to),We(wo,\"BuildLogic\",Qe,to),We(bo,\"BuildLogicClass\",Qe,to),We(Oc,\"BaseIntent\",Qe),We(Fc,\"TreeIntent\",Qe,Oc),We(ko,\"TaskTreeIntent\",Qe,Fc),We(qo,\"MessageTreeIntent\",Qe,Fc),We(yo,\"InputTreeIntent\",Qe,Fc),We(Bo,\"IncompatibleTaskTreeIntent\",Qe,Fc),We(Po,\"Intent\",Qe,Oc),We(Co,\"SetTab\",Qe,Po),We(xo,\"Model\",Qe),We(jo,\"Tab\",Qe,Ui),We(Ko,\"ConfigurationCacheReportPage\",iu),We(nf,\"ImportedProblem\",Qe),We(tf,\"ImportedDiagnostics\",Qe),We(_f,\"sam$kotlin_Comparator$0\",Qe),We(Bf,\"LearnMore\",Qe),We(jf,\"Fragment\",Qe),We(Cf,\"Text\",Qe,jf),We(xf,\"Reference\",Qe,jf),We(Pf,\"Builder\",Qe),We(If,\"Companion\",iu),We(zf,\"PrettyText\",Qe),We(Tf,\"Companion\",iu),We(Af,\"Trie\",Qe),We(Kf,\"ViewFactory\",Qe),We(Xf,\"Companion\",iu),We(es,\"View\",Qe),We(ns,\"Empty\",iu,es),We(rs,\"Element\",Qe,es),We(is,\"MappedView\",Qe,es),We(os,\"Attributes\",Qe),We(as,\"Attribute\",Qe),We(fs,\"OnEvent\",Qe,as),We(ss,\"ClassName\",Qe,as),We(cs,\"Named\",Qe,as),We(gs,\"Intent\",Qe),We(vs,\"Toggle\",Qe,gs),We(ds,\"Model\",Qe),We(bs,\"TreeView\",iu),We(Bs,\"Focus\",Qe),We(ks,\"Original\",Qe,Bs),We(qs,\"Child\",Qe,Bs),We(ys,\"ViewState\",Qe,Ui),We(js,\"Tree\",Qe),We(Ss,\"Tab\",Qe,Ui),We(zs,\"ProblemIdElement\",Qe),We(Ts,\"ProblemSummary\",Qe),We(Ms,\"ProblemNodeGroup\",Qe),We(sc,\"ProblemApiNode\",Qe,to),We(uc,\"Text\",Qe,sc),We(oc,\"ProblemIdNode\",Qe,sc),We(fc,\"Advice\",Qe,to),We(cc,\"MessageTreeIntent\",Qe,Fc),We(ac,\"ProblemIdTreeIntent\",Qe,Fc),We(hc,\"FileLocationTreeIntent\",Qe,Fc),We(lc,\"PluginLocationTreeIntent\",Qe,Fc),We(_c,\"TaskLocationTreeIntent\",Qe,Fc),We(gc,\"Intent\",Qe,Oc),We(vc,\"SetTab\",Qe,gc),We(dc,\"Model\",Qe),We(Lc,\"ProblemsReportPage\",iu),We(Mc,\"Copy\",Qe,Oc),We(Dc,\"ToggleStackTracePart\",Qe,Oc),ve(tt).f=function(){return this.n_1.f()},ve(ft).f=function(){return this.r_1.f()},ve(st).f=function(){var n,t,r=function(n,t){for(var r=n.f();r.g();){var i=r.h();t.d(i)}return t}(this.s_1,Or());return n=r,t=this.t_1,function(n,t){if(n.k()<=1)return br();var r=Br(n);!function(n,t){if(function(){if(null!=l)return l;l=!1;var n=[],t=0;if(t<600)do{var r=t;t=t+1|0,n.push(r)}while(t<600);var i=Vr;n.sort(i);var e=1,u=n.length;if(e<u)do{var o=e;e=e+1|0;var f=n[o-1|0],s=n[o];if((3&f)==(3&s)&&f>=s)return!1}while(e<u);return l=!0,!0}()){var r=(i=t,function(n,t){return i.compare(n,t)});n.sort(r)}else!function(n,t,r,i){var e=n.length,u=function(n){var t=0,r=n.length-1|0;if(t<=r)do{var i=t;t=t+1|0,n[i]=null}while(i!==r);return n}(Array(e)),o=Ur(n,u,0,r,i);if(o!==n){var f=0;if(f<=r)do{var s=f;f=f+1|0,n[s]=o[s]}while(s!==r)}}(n,0,Rn(n),t);var i}(r,t);var i=0,e=r.length;if(i<e)do{var u=i;i=i+1|0,n.f4(u,r[u])}while(i<e)}(n,t),r.f()},ve(ct).u=function(n){var t;n:if(Ke(this,Di)&&this.i())t=!1;else{for(var r=this.f();r.g();)if(le(r.h(),n)){t=!0;break n}t=!1}return t},ve(ct).v=function(n){var t;n:if(Ke(n,Di)&&n.i())t=!0;else{for(var r=n.f();r.g();){var i=r.h();if(!this.u(i)){t=!1;break n}}t=!0}return t},ve(ct).i=function(){return 0===this.k()},ve(ct).toString=function(){return $n(this,\", \",\"[\",\"]\",A,A,(n=this,function(t){return t===n?\"(this Collection)\":Vi(t)}));var n},ve(ct).toArray=function(){return kr(this)},ve(at).g=function(){return this.w_1<this.x_1.k()},ve(at).h=function(){if(!this.g())throw Cu();var n=this.w_1;return this.w_1=n+1|0,this.x_1.j(n)},ve(ht).c1=function(){return this.w_1>0},ve(ht).d1=function(){if(!this.c1())throw Cu();return this.w_1=this.w_1-1|0,this.a1_1.j(this.w_1)},ve(lt).e1=function(n,t){if(n<0||n>=t)throw du(\"index: \"+n+\", size: \"+t)},ve(lt).b1=function(n,t){if(n<0||n>t)throw du(\"index: \"+n+\", size: \"+t)},ve(lt).f1=function(n){for(var t=1,r=n.f();r.g();){var i=r.h(),e=Mn(31,t),u=null==i?null:ae(i);t=e+(null==u?0:u)|0}return t},ve(lt).g1=function(n,t){if(n.k()!==t.k())return!1;for(var r=t.f(),i=n.f();i.g();)if(!le(i.h(),r.h()))return!1;return!0},ve(vt).f=function(){return new at(this)},ve(vt).l=function(n){return new ht(this,n)},ve(vt).equals=function(n){return n===this||!(null==n||!Ke(n,Fi))&&_t().g1(this,n)},ve(vt).hashCode=function(){return _t().f1(this)},ve(dt).g=function(){return this.h1_1.g()},ve(dt).h=function(){return this.h1_1.h().i1()},ve(bt).k1=function(n){var t=n.j1(),r=null==t?null:ae(t),i=null==r?0:r,e=n.i1(),u=null==e?null:ae(e);return i^(null==u?0:u)},ve(bt).l1=function(n){return Vi(n.j1())+\"=\"+Vi(n.i1())},ve(bt).m1=function(n,t){return!(null==t||!Ke(t,Oi))&&!!le(n.j1(),t.j1())&&le(n.i1(),t.i1())},ve(mt).r1=function(n){return this.q1_1.s1(n)},ve(mt).u=function(n){return!(null!=n&&!Je(n))&&this.r1(null==n||Je(n)?n:pe())},ve(mt).f=function(){return new dt(this.q1_1.o().f())},ve(mt).k=function(){return this.q1_1.k()},ve(kt).t1=function(n){return!(null==wt(this,n))},ve(kt).s1=function(n){var t;n:{var r=this.o();if(Ke(r,Di)&&r.i())t=!1;else{for(var i=r.f();i.g();)if(le(i.h().i1(),n)){t=!0;break n}t=!1}}return t},ve(kt).u1=function(n){if(null==n||!Ke(n,Oi))return!1;var t=n.j1(),r=n.i1(),i=(Ke(this,Ri)?this:pe()).v1(t);return!(!le(r,i)||null==i&&!(Ke(this,Ri)?this:pe()).t1(t))},ve(kt).equals=function(n){if(n===this)return!0;if(null==n||!Ke(n,Ri))return!1;if(this.k()!==n.k())return!1;var t;n:{var r=n.o();if(Ke(r,Di)&&r.i())t=!0;else{for(var i=r.f();i.g();){var e=i.h();if(!this.u1(e)){t=!1;break n}}t=!0}}return t},ve(kt).v1=function(n){var t=wt(this,n);return null==t?null:t.i1()},ve(kt).hashCode=function(){return ae(this.o())},ve(kt).i=function(){return 0===this.k()},ve(kt).k=function(){return this.o().k()},ve(kt).toString=function(){var n;return $n(this.o(),\", \",\"{\",\"}\",A,A,(n=this,function(t){return n.p1(t)}))},ve(kt).p1=function(n){return gt(this,n.j1())+\"=\"+gt(this,n.i1())},ve(kt).w1=function(){return null==this.o1_1&&(this.o1_1=new mt(this)),we(this.o1_1)},ve(qt).x1=function(n){for(var t=0,r=n.f();r.g();){var i=r.h(),e=t,u=null==i?null:ae(i);t=e+(null==u?0:u)|0}return t},ve(qt).y1=function(n,t){return n.k()===t.k()&&n.v(t)},ve(Pt).equals=function(n){return!(null==n||!Ke(n,Fi))&&n.i()},ve(Pt).hashCode=function(){return 1},ve(Pt).toString=function(){return\"[]\"},ve(Pt).k=function(){return 0},ve(Pt).i=function(){return!0},ve(Pt).a2=function(n){return n.i()},ve(Pt).v=function(n){return this.a2(n)},ve(Pt).j=function(n){throw du(\"Empty list doesn't contain element at index \"+n+\".\")},ve(Pt).f=function(){return zt()},ve(Pt).l=function(n){if(0!==n)throw du(\"Index: \"+n);return zt()},ve(It).k=function(){return this.b2_1.length},ve(It).i=function(){return 0===this.b2_1.length},ve(It).d2=function(n){return function(n,t){return Hn(n,t)>=0}(this.b2_1,n)},ve(It).e2=function(n){var t;n:if(Ke(n,Di)&&n.i())t=!0;else{for(var r=n.f();r.g();){var i=r.h();if(!this.d2(i)){t=!1;break n}}t=!0}return t},ve(It).v=function(n){return this.e2(n)},ve(It).f=function(){return Qi(this.b2_1)},ve(St).g=function(){return!1},ve(St).c1=function(){return!1},ve(St).h=function(){throw Cu()},ve(St).d1=function(){throw Cu()},ve(Lt).h=function(){return this.f2()},ve(Nt).g=function(){return this.g2_1.c1()},ve(Nt).c1=function(){return this.g2_1.g()},ve(Nt).h=function(){return this.g2_1.d1()},ve(Nt).d1=function(){return this.g2_1.h()},ve(At).k=function(){return this.i2_1.k()},ve(At).j=function(n){return this.i2_1.j(function(n,t){if(!(0<=t&&t<=jt(n)))throw du(\"Element index \"+t+\" must be in range [\"+Ve(0,jt(n))+\"].\");return jt(n)-t|0}(this,n))},ve(At).f=function(){return this.l(0)},ve(At).l=function(n){return new Nt(this,n)},ve(Mt).h=function(){return this.k2_1.m2_1(this.j2_1.h())},ve(Mt).g=function(){return this.j2_1.g()},ve(Ft).f=function(){return new Mt(this)},ve(Ot).h=function(){if(-1===this.o2_1&&Dt(this),0===this.o2_1)throw Cu();var n=this.p2_1;return this.p2_1=null,this.o2_1=-1,null==n||Je(n)?n:pe()},ve(Ot).g=function(){return-1===this.o2_1&&Dt(this),1===this.o2_1},ve(Rt).f=function(){return new Ot(this)},ve($t).equals=function(n){return!(null==n||!Ke(n,$i))&&n.i()},ve($t).hashCode=function(){return 0},ve($t).toString=function(){return\"[]\"},ve($t).k=function(){return 0},ve($t).i=function(){return!0},ve($t).a2=function(n){return n.i()},ve($t).v=function(n){return this.a2(n)},ve($t).f=function(){return zt()},ve(Zt).y2=function(){return this.z2_1},ve(Zt).c3=function(){return this.a3_1},ve(Zt).i=function(){return this.z2_1>this.a3_1},ve(Zt).equals=function(n){return n instanceof Zt&&(!(!this.i()||!n.i())||this.z2_1===n.z2_1&&this.a3_1===n.a3_1)},ve(Zt).hashCode=function(){return this.i()?-1:Mn(31,this.z2_1)+this.a3_1|0},ve(Zt).toString=function(){return this.z2_1+\"..\"+this.a3_1},ve(Yt).g=function(){return this.f3_1},ve(Yt).f2=function(){var n=this.g3_1;if(n===this.e3_1){if(!this.f3_1)throw Cu();this.f3_1=!1}else this.g3_1=this.g3_1+this.d3_1|0;return n},ve(Wt).q=function(n,t,r){return new Xt(n,t,r)},ve(Xt).f=function(){return new Yt(this.z2_1,this.a3_1,this.b3_1)},ve(Xt).i=function(){return this.b3_1>0?this.z2_1>this.a3_1:this.z2_1<this.a3_1},ve(Xt).equals=function(n){return n instanceof Xt&&(!(!this.i()||!n.i())||this.z2_1===n.z2_1&&this.a3_1===n.a3_1&&this.b3_1===n.b3_1)},ve(Xt).hashCode=function(){return this.i()?-1:Mn(31,Mn(31,this.z2_1)+this.a3_1|0)+this.b3_1|0},ve(Xt).toString=function(){return this.b3_1>0?this.z2_1+\"..\"+this.a3_1+\" step \"+this.b3_1:this.z2_1+\" downTo \"+this.a3_1+\" step \"+(0|-this.b3_1)},ve(ar).h=function(){if(-1===this.j3_1&&cr(this),0===this.j3_1)throw Cu();var n=this.m3_1,t=n instanceof Zt?n:pe();return this.m3_1=null,this.j3_1=-1,t},ve(ar).g=function(){return-1===this.j3_1&&cr(this),1===this.j3_1},ve(hr).f=function(){return new ar(this)},ve(lr).toString=function(){return\"(\"+this.t3_1+\", \"+this.u3_1+\")\"},ve(lr).v3=function(){return this.t3_1},ve(lr).w3=function(){return this.u3_1},ve(lr).hashCode=function(){var n=null==this.t3_1?0:ae(this.t3_1);return Mn(n,31)+(null==this.u3_1?0:ae(this.u3_1))|0},ve(lr).equals=function(n){if(this===n)return!0;if(!(n instanceof lr))return!1;var t=n instanceof lr?n:pe();return!!le(this.t3_1,t.t3_1)&&!!le(this.u3_1,t.u3_1)},ve(wr).toString=function(){return\"kotlin.Unit\"},ve(pr).b4=function(){return this.MIN_VALUE},ve(pr).c4=function(){return this.MAX_VALUE},ve(pr).d4=function(){return this.SIZE_BYTES},ve(pr).e4=function(){return this.SIZE_BITS},ve(xr).m=function(n){this.g4();for(var t=!1,r=n.f();r.g();){var i=r.h();this.d(i)&&(t=!0)}return t},ve(xr).toJSON=function(){return this.toArray()},ve(xr).g4=function(){},ve(jr).g=function(){return this.h4_1<this.j4_1.k()},ve(jr).h=function(){if(!this.g())throw Cu();var n=this.h4_1;return this.h4_1=n+1|0,this.i4_1=n,this.j4_1.j(this.i4_1)},ve(Pr).c1=function(){return this.h4_1>0},ve(Pr).d1=function(){if(!this.c1())throw Cu();return this.h4_1=this.h4_1-1|0,this.i4_1=this.h4_1,this.n4_1.j(this.i4_1)},ve(Ir).d=function(n){return this.g4(),this.p4(this.k(),n),!0},ve(Ir).f=function(){return new jr(this)},ve(Ir).u=function(n){return this.q4(n)>=0},ve(Ir).q4=function(n){var t=0,r=jt(this);if(t<=r)do{var i=t;if(t=t+1|0,le(this.j(i),n))return i}while(i!==r);return-1},ve(Ir).l=function(n){return new Pr(this,n)},ve(Ir).equals=function(n){return n===this||!(null==n||!Ke(n,Fi))&&_t().g1(this,n)},ve(Ir).hashCode=function(){return _t().f1(this)},ve(Sr).g=function(){return this.r4_1.g()},ve(Sr).h=function(){return this.r4_1.h().j1()},ve(zr).g=function(){return this.s4_1.g()},ve(zr).h=function(){return this.s4_1.h().i1()},ve(Tr).j1=function(){return this.t4_1},ve(Tr).i1=function(){return this.u4_1},ve(Tr).v4=function(n){var t=this.u4_1;return this.u4_1=n,t},ve(Tr).hashCode=function(){return pt().k1(this)},ve(Tr).toString=function(){return pt().l1(this)},ve(Tr).equals=function(n){return pt().m1(this,n)},ve(Er).u=function(n){return this.w4(n)},ve(Lr).y4=function(n){throw zu(\"Add is not supported on keys\")},ve(Lr).d=function(n){return this.y4(null==n||Je(n)?n:pe())},ve(Lr).z4=function(n){return this.x4_1.t1(n)},ve(Lr).u=function(n){return!(null!=n&&!Je(n))&&this.z4(null==n||Je(n)?n:pe())},ve(Lr).f=function(){return new Sr(this.x4_1.o().f())},ve(Lr).k=function(){return this.x4_1.k()},ve(Lr).g4=function(){return this.x4_1.g4()},ve(Nr).f5=function(n){throw zu(\"Add is not supported on values\")},ve(Nr).d=function(n){return this.f5(null==n||Je(n)?n:pe())},ve(Nr).r1=function(n){return this.e5_1.s1(n)},ve(Nr).u=function(n){return!(null!=n&&!Je(n))&&this.r1(null==n||Je(n)?n:pe())},ve(Nr).f=function(){return new zr(this.e5_1.o().f())},ve(Nr).k=function(){return this.e5_1.k()},ve(Nr).g4=function(){return this.e5_1.g4()},ve(Ar).g5=function(){return null==this.c5_1&&(this.c5_1=new Lr(this)),we(this.c5_1)},ve(Ar).w1=function(){return null==this.d5_1&&(this.d5_1=new Nr(this)),we(this.d5_1)},ve(Ar).g4=function(){},ve(Mr).equals=function(n){return n===this||!(null==n||!Ke(n,$i))&&yt().y1(this,n)},ve(Mr).hashCode=function(){return yt().x1(this)},ve(Gr).j5=function(){return this.g4(),this.c_1=!0,this.k()>0?this:Dr().i5_1},ve(Gr).k=function(){return this.b_1.length},ve(Gr).j=function(n){var t=this.b_1[$r(this,n)];return null==t||Je(t)?t:pe()},ve(Gr).f4=function(n,t){this.g4(),$r(this,n);var r=this.b_1[n];this.b_1[n]=t;var i=r;return null==i||Je(i)?i:pe()},ve(Gr).d=function(n){return this.g4(),this.b_1.push(n),this.o4_1=this.o4_1+1|0,!0},ve(Gr).p4=function(n,t){this.g4(),this.b_1.splice(function(n,t){return _t().b1(t,n.k()),t}(this,n),0,t),this.o4_1=this.o4_1+1|0},ve(Gr).m=function(n){if(this.g4(),n.i())return!1;for(var t,r,i,e=(t=this,r=n.k(),i=t.k(),t.b_1.length=t.k()+r|0,i),u=0,o=n.f();o.g();){var f=o.h(),s=u;u=s+1|0;var c=yr(s);this.b_1[e+c|0]=f}return this.o4_1=this.o4_1+1|0,!0},ve(Gr).q4=function(n){return Hn(this.b_1,n)},ve(Gr).toString=function(){return On(this.b_1,\", \",\"[\",\"]\",A,A,ee)},ve(Gr).k5=function(){return[].slice.call(this.b_1)},ve(Gr).toArray=function(){return this.k5()},ve(Gr).g4=function(){if(this.c_1)throw Su()},ve(Qr).l5=function(n,t){return le(n,t)},ve(Qr).m5=function(n){var t=null==n?null:ae(n);return null==t?0:t},ve(Zr).o5=function(n){throw zu(\"Add is not supported on entries\")},ve(Zr).d=function(n){return this.o5(null!=n&&Ke(n,Hi)?n:pe())},ve(Zr).w4=function(n){return this.n5_1.u1(n)},ve(Zr).f=function(){return this.n5_1.t5_1.f()},ve(Zr).k=function(){return this.n5_1.k()},ve(Xr).t1=function(n){return this.t5_1.z4(n)},ve(Xr).s1=function(n){var t;n:{var r=this.t5_1;if(Ke(r,Di)&&r.i())t=!1;else{for(var i=r.f();i.g();){var e=i.h();if(this.u5_1.l5(e.i1(),n)){t=!0;break n}}t=!1}}return t},ve(Xr).o=function(){return null==this.v5_1&&(this.v5_1=this.x5()),we(this.v5_1)},ve(Xr).x5=function(){return new Zr(this)},ve(Xr).v1=function(n){return this.t5_1.v1(n)},ve(Xr).h5=function(n,t){return this.t5_1.h5(n,t)},ve(Xr).k=function(){return this.t5_1.k()},ve(ni).d=function(n){return null==this.y5_1.h5(n,this)},ve(ni).u=function(n){return this.y5_1.t1(n)},ve(ni).i=function(){return this.y5_1.i()},ve(ni).f=function(){return this.y5_1.g5().f()},ve(ni).k=function(){return this.y5_1.k()},ve(ei).g=function(){return-1===this.z5_1&&(this.z5_1=function(n){if(null!=n.c6_1&&n.d6_1){var t=n.c6_1.length;if(n.e6_1=n.e6_1+1|0,n.e6_1<t)return 0}if(n.b6_1=n.b6_1+1|0,n.b6_1<n.a6_1.length){n.c6_1=n.g6_1.i6_1[n.a6_1[n.b6_1]];var r=n,i=n.c6_1;return r.d6_1=null!=i&&Xe(i),n.e6_1=0,0}return n.c6_1=null,1}(this)),0===this.z5_1},ve(ei).h=function(){if(!this.g())throw Cu();var n=this.d6_1?this.c6_1[this.e6_1]:this.c6_1;return this.f6_1=n,this.z5_1=-1,n},ve(ui).w5=function(){return this.h6_1},ve(ui).k=function(){return this.j6_1},ve(ui).h5=function(n,t){var r=this.h6_1.m5(n),i=ii(this,r);if(null==i)this.i6_1[r]=new Tr(n,t);else{if(null==i||!Xe(i)){var e,u=i;return this.h6_1.l5(u.j1(),n)?u.v4(t):(e=[u,new Tr(n,t)],this.i6_1[r]=e,this.j6_1=this.j6_1+1|0,null)}var o=i,f=ri(o,this,n);if(null!=f)return f.v4(t);o.push(new Tr(n,t))}return this.j6_1=this.j6_1+1|0,null},ve(ui).z4=function(n){return!(null==ti(this,n))},ve(ui).v1=function(n){var t=ti(this,n);return null==t?null:t.i1()},ve(ui).f=function(){return new ei(this)},ve(fi).g=function(){return!(null===this.m6_1)},ve(fi).h=function(){if(!this.g())throw Cu();var n=we(this.m6_1);this.l6_1=n;var t,r=n.b7_1;return t=r!==this.n6_1.y6_1.v6_1?r:null,this.m6_1=t,n},ve(ai).v4=function(n){return this.d7_1.g4(),ve(Tr).v4.call(this,n)},ve(hi).o5=function(n){throw zu(\"Add is not supported on entries\")},ve(hi).d=function(n){return this.o5(null!=n&&Ke(n,Hi)?n:pe())},ve(hi).w4=function(n){return this.y6_1.u1(n)},ve(hi).f=function(){return new fi(this)},ve(hi).k=function(){return this.y6_1.k()},ve(hi).g4=function(){return this.y6_1.g4()},ve(vi).j5=function(){var n;if(this.g4(),this.x6_1=!0,this.k()>0)n=this;else{var t=ci().e7_1;n=Ke(t,Ri)?t:pe()}return n},ve(vi).t1=function(n){return this.w6_1.t1(n)},ve(vi).s1=function(n){var t=this.v6_1;if(null==t)return!1;var r=t;do{if(le(r.i1(),n))return!0;r=we(r.b7_1)}while(r!==this.v6_1);return!1},ve(vi).x5=function(){return new hi(this)},ve(vi).v1=function(n){var t=this.w6_1.v1(n);return null==t?null:t.i1()},ve(vi).h5=function(n,t){this.g4();var r=this.w6_1.v1(n);if(null==r){var i=new ai(this,n,t);return this.w6_1.h5(n,i),function(n,t){if(null!=n.b7_1||null!=n.c7_1)throw wu(ce(\"Check failed.\"));var r=t.v6_1;if(null==r)t.v6_1=n,n.b7_1=n,n.c7_1=n;else{var i=r.c7_1;if(null==i)throw wu(ce(\"Required value was null.\"));var e=i;n.c7_1=e,n.b7_1=r,r.c7_1=n,e.b7_1=n}}(i,this),null}return r.v4(t)},ve(vi).k=function(){return this.w6_1.k()},ve(vi).g4=function(){if(this.x6_1)throw Su()},ve(wi).g4=function(){return this.y5_1.g4()},ve(pi).h7=function(){this.i7(\"\\n\")},ve(pi).j7=function(n){this.i7(n),this.h7()},ve(mi).i7=function(n){var t=String(n);this.k7_1.write(t)},ve(ki).i7=function(n){var t=String(n),r=t.lastIndexOf(\"\\n\",0);if(r>=0){var i=this.m7_1;this.m7_1=i+t.substring(0,r),this.n7();var e=r+1|0;t=t.substring(e)}this.m7_1=this.m7_1+t},ve(ki).n7=function(){console.log(this.m7_1),this.m7_1=\"\"},ve(qi).i7=function(n){var t=this.m7_1;this.m7_1=t+String(n)},ve(Ci).x3=function(){return this.o7_1.length},ve(Ci).y3=function(n){var t=this.o7_1;if(!(n>=0&&n<=tr(t)))throw du(\"index: \"+n+\", length: \"+this.x3()+\"}\");return ne(t,n)},ve(Ci).z3=function(n,t){return this.o7_1.substring(n,t)},ve(Ci).i3=function(n){return this.o7_1=this.o7_1+new Mi(n),this},ve(Ci).e=function(n){return this.o7_1=this.o7_1+Vi(n),this},ve(Ci).p7=function(n){var t=this.o7_1;return this.o7_1=t+(null==n?\"null\":n),this},ve(Ci).toString=function(){return this.o7_1},ve(Pi).t7=function(n){var t=this.q7_1;return n.replace(t,\"\\\\$&\")},ve(Pi).u7=function(n){var t=this.s7_1;return n.replace(t,\"$$$$\")},ve(Si).a8=function(n){this.x7_1.lastIndex=0;var t=this.x7_1.exec(ce(n));return null!=t&&0===t.index&&this.x7_1.lastIndex===re(n)},ve(Si).toString=function(){return this.x7_1.toString()},ve(Mi).o8=function(n){return Ei(this.h3_1,n)},ve(Mi).a4=function(n){return function(n,t){return Ei(n.h3_1,t instanceof Mi?t.h3_1:pe())}(this,n)},ve(Mi).equals=function(n){return function(n,t){return t instanceof Mi&&n===t.h3_1}(this.h3_1,n)},ve(Mi).hashCode=function(){return this.h3_1},ve(Mi).toString=function(){return Li(this.h3_1)},ve(Ui).r8=function(n){return ue(this.q8_1,n.q8_1)},ve(Ui).a4=function(n){return this.r8(n instanceof Ui?n:pe())},ve(Ui).equals=function(n){return this===n},ve(Ui).hashCode=function(){return se(this)},ve(Ui).toString=function(){return this.p8_1},ve(Zi).g=function(){return!(this.s8_1===this.t8_1.length)},ve(Zi).h=function(){if(this.s8_1===this.t8_1.length)throw xu(\"\"+this.s8_1);var n=this.s8_1;return this.s8_1=n+1|0,this.t8_1[n]},ve(ke).b9=function(n){return je(this,n)},ve(ke).a4=function(n){return this.b9(n instanceof ke?n:pe())},ve(ke).c9=function(n){return Pe(this,n)},ve(ke).d9=function(n){return function(n,t){if($e(),Ae(t))throw mu(\"division by zero\");if(Ae(n))return qe();if(Te(n,Ce())){if(Te(t,ye())||Te(t,Be()))return Ce();if(Te(t,Ce()))return ye();var r=function(n){$e();return new ke(n.u8_1>>>1|n.v8_1<<31,n.v8_1>>1)}(n),i=function(n){$e();return new ke(n.u8_1<<1,n.v8_1<<1|n.u8_1>>>31)}(r.d9(t));return Te(i,qe())?Ne(t)?ye():Be():Pe(i,Ie(n,Se(t,i)).d9(t))}if(Te(t,Ce()))return qe();if(Ne(n))return Ne(t)?Fe(n).d9(Fe(t)):Fe(Fe(n).d9(t));if(Ne(t))return Fe(n.d9(Fe(t)));for(var e=qe(),u=n;He(u,t);){for(var o=ze(u)/ze(t),f=Math.max(1,Math.floor(o)),s=Math.ceil(Math.log(f)/Math.LN2),c=s<=48?1:Math.pow(2,s-48),a=Oe(f),h=Se(a,t);Ne(h)||Re(h,u);)h=Se(a=Oe(f-=c),t);Ae(a)&&(a=ye()),e=Pe(e,a),u=Ie(u,h)}return e}(this,n)},ve(ke).e9=function(){return this.f9().c9(new ke(1,0))},ve(ke).f9=function(){return new ke(~this.u8_1,~this.v8_1)},ve(ke).g9=function(){return this.u8_1},ve(ke).w8=function(){return ze(this)},ve(ke).valueOf=function(){return this.w8()},ve(ke).equals=function(n){return n instanceof ke&&Te(this,n)},ve(ke).hashCode=function(){return $e(),this.u8_1^this.v8_1},ve(ke).toString=function(){return Ee(this,10)},ve(Hu).toString=function(){return\"Model(text=\"+this.o9_1+\", tooltip=\"+this.p9_1+\")\"},ve(Hu).hashCode=function(){var n=he(this.o9_1);return Mn(n,31)+he(this.p9_1)|0},ve(Hu).equals=function(n){if(this===n)return!0;if(!(n instanceof Hu))return!1;var t=n instanceof Hu?n:pe();return this.o9_1===t.o9_1&&this.p9_1===t.p9_1},ve($u).v9=function(n,t){return this.w9(new Hu(n,t))},ve($u).w9=function(n){var t,r;return Uf().y9(us((t=n,r=this,function(n){return n.s9(t.p9_1),n.t9(\"copy-button\"),n.u9(function(n,t){return function(r){return n.q9_1(t.o9_1)}}(r,t)),br()})),[])},ve($u).z9=function(n){return this.w9(n instanceof Hu?n:pe())},ve($u).aa=function(n,t){return t},ve($u).ba=function(n,t){var r=null==n||Je(n)?n:pe();return this.aa(r,t instanceof Hu?t:pe())},ve(Uu).ka=function(n){return function(n,t){for(var r=Gf(),i=t.ca_1,e=Rr(Tt(i,10)),u=i.f();u.g();){var o,f,s=u.h();s instanceof Cf?f=Gf().ga(s.fa_1):s instanceof xf?f=Gu(n,s.da_1,s.ea_1):be(),o=f,e.d(o)}return r.ha(e)}(this,n)},ve(Uu).z9=function(n){return this.ka(n instanceof zf?n:pe())},ve(Uu).la=function(n,t){return t},ve(Uu).ba=function(n,t){var r=null==n||Je(n)?n:pe();return this.la(r,t instanceof zf?t:pe())},ve(Vu).pa=function(n,t,r){return new Vu(n,t,r)},ve(Vu).qa=function(n,t,r,i){return n=n===A?this.ma_1:n,t=t===A?this.na_1:t,r=r===A?this.oa_1:r,i===A?this.pa(n,t,r):i.pa.call(this,n,t,r)},ve(Vu).toString=function(){return\"Exception(summary=\"+this.ma_1+\", fullText=\"+this.na_1+\", parts=\"+this.oa_1+\")\"},ve(Vu).hashCode=function(){var n=null==this.ma_1?0:this.ma_1.hashCode();return n=Mn(n,31)+he(this.na_1)|0,Mn(n,31)+ae(this.oa_1)|0},ve(Vu).equals=function(n){if(this===n)return!0;if(!(n instanceof Vu))return!1;var t=n instanceof Vu?n:pe();return!!le(this.ma_1,t.ma_1)&&this.na_1===t.na_1&&!!le(this.oa_1,t.oa_1)},ve(Qu).ta=function(n,t){return new Qu(n,t)},ve(Qu).ua=function(n,t,r){return n=n===A?this.ra_1:n,t=t===A?this.sa_1:t,r===A?this.ta(n,t):r.ta.call(this,n,t)},ve(Qu).toString=function(){return\"StackTracePart(lines=\"+this.ra_1+\", state=\"+this.sa_1+\")\"},ve(Qu).hashCode=function(){var n=ae(this.ra_1);return Mn(n,31)+(null==this.sa_1?0:this.sa_1.hashCode())|0},ve(Qu).equals=function(n){if(this===n)return!0;if(!(n instanceof Qu))return!1;var t=n instanceof Qu?n:pe();return!!le(this.ra_1,t.ra_1)&&!!le(this.sa_1,t.sa_1)},ve(Zu).toString=function(){return\"Error(label=\"+this.va_1+\", docLink=\"+this.wa_1+\")\"},ve(Zu).hashCode=function(){var n=ae(this.va_1);return Mn(n,31)+(null==this.wa_1?0:ae(this.wa_1))|0},ve(Zu).equals=function(n){if(this===n)return!0;if(!(n instanceof Zu))return!1;var t=n instanceof Zu?n:pe();return!!le(this.va_1,t.va_1)&&!!le(this.wa_1,t.wa_1)},ve(Yu).toString=function(){return\"Warning(label=\"+this.xa_1+\", docLink=\"+this.ya_1+\")\"},ve(Yu).hashCode=function(){var n=ae(this.xa_1);return Mn(n,31)+(null==this.ya_1?0:ae(this.ya_1))|0},ve(Yu).equals=function(n){if(this===n)return!0;if(!(n instanceof Yu))return!1;var t=n instanceof Yu?n:pe();return!!le(this.xa_1,t.xa_1)&&!!le(this.ya_1,t.ya_1)},ve(Wu).toString=function(){return\"Message(prettyText=\"+this.za_1+\")\"},ve(Wu).hashCode=function(){return this.za_1.hashCode()},ve(Wu).equals=function(n){if(this===n)return!0;if(!(n instanceof Wu))return!1;var t=n instanceof Wu?n:pe();return!!this.za_1.equals(t.za_1)},ve(Ku).toString=function(){return\"ListElement(prettyText=\"+this.ab_1+\")\"},ve(Ku).hashCode=function(){return this.ab_1.hashCode()},ve(Ku).equals=function(n){if(this===n)return!0;if(!(n instanceof Ku))return!1;var t=n instanceof Ku?n:pe();return!!this.ab_1.equals(t.ab_1)},ve(Xu).toString=function(){return\"TreeNode(prettyText=\"+this.bb_1+\")\"},ve(Xu).hashCode=function(){return this.bb_1.hashCode()},ve(Xu).equals=function(n){if(this===n)return!0;if(!(n instanceof Xu))return!1;var t=n instanceof Xu?n:pe();return!!this.bb_1.equals(t.bb_1)},ve(Ju).toString=function(){return\"Link(href=\"+this.cb_1+\", label=\"+this.db_1+\")\"},ve(Ju).hashCode=function(){var n=he(this.cb_1);return Mn(n,31)+he(this.db_1)|0},ve(Ju).equals=function(n){if(this===n)return!0;if(!(n instanceof Ju))return!1;var t=n instanceof Ju?n:pe();return this.cb_1===t.cb_1&&this.db_1===t.db_1},ve(no).toString=function(){return\"Label(text=\"+this.eb_1+\")\"},ve(no).hashCode=function(){return he(this.eb_1)},ve(no).equals=function(n){if(this===n)return!0;if(!(n instanceof no))return!1;var t=n instanceof no?n:pe();return this.eb_1===t.eb_1},ve(co).toString=function(){return\"Info(label=\"+this.hb_1+\", docLink=\"+this.ib_1+\")\"},ve(co).hashCode=function(){var n=ae(this.hb_1);return Mn(n,31)+(null==this.ib_1?0:ae(this.ib_1))|0},ve(co).equals=function(n){if(this===n)return!0;if(!(n instanceof co))return!1;var t=n instanceof co?n:pe();return!!le(this.hb_1,t.hb_1)&&!!le(this.ib_1,t.ib_1)},ve(ao).toString=function(){return\"Project(path=\"+this.jb_1+\")\"},ve(ao).hashCode=function(){return he(this.jb_1)},ve(ao).equals=function(n){if(this===n)return!0;if(!(n instanceof ao))return!1;var t=n instanceof ao?n:pe();return this.jb_1===t.jb_1},ve(ho).toString=function(){return\"Task(path=\"+this.kb_1+\", type=\"+this.lb_1+\")\"},ve(ho).hashCode=function(){var n=he(this.kb_1);return Mn(n,31)+he(this.lb_1)|0},ve(ho).equals=function(n){if(this===n)return!0;if(!(n instanceof ho))return!1;var t=n instanceof ho?n:pe();return this.kb_1===t.kb_1&&this.lb_1===t.lb_1},ve(lo).toString=function(){return\"TaskPath(path=\"+this.mb_1+\")\"},ve(lo).hashCode=function(){return he(this.mb_1)},ve(lo).equals=function(n){if(this===n)return!0;if(!(n instanceof lo))return!1;var t=n instanceof lo?n:pe();return this.mb_1===t.mb_1},ve(_o).toString=function(){return\"Bean(type=\"+this.nb_1+\")\"},ve(_o).hashCode=function(){return he(this.nb_1)},ve(_o).equals=function(n){if(this===n)return!0;if(!(n instanceof _o))return!1;var t=n instanceof _o?n:pe();return this.nb_1===t.nb_1},ve(vo).toString=function(){return\"SystemProperty(name=\"+this.ob_1+\")\"},ve(vo).hashCode=function(){return he(this.ob_1)},ve(vo).equals=function(n){if(this===n)return!0;if(!(n instanceof vo))return!1;var t=n instanceof vo?n:pe();return this.ob_1===t.ob_1},ve(go).toString=function(){return\"Property(kind=\"+this.pb_1+\", name=\"+this.qb_1+\", owner=\"+this.rb_1+\")\"},ve(go).hashCode=function(){var n=he(this.pb_1);return n=Mn(n,31)+he(this.qb_1)|0,Mn(n,31)+he(this.rb_1)|0},ve(go).equals=function(n){if(this===n)return!0;if(!(n instanceof go))return!1;var t=n instanceof go?n:pe();return this.pb_1===t.pb_1&&this.qb_1===t.qb_1&&this.rb_1===t.rb_1},ve(wo).toString=function(){return\"BuildLogic(location=\"+this.sb_1+\")\"},ve(wo).hashCode=function(){return he(this.sb_1)},ve(wo).equals=function(n){if(this===n)return!0;if(!(n instanceof wo))return!1;var t=n instanceof wo?n:pe();return this.sb_1===t.sb_1},ve(bo).toString=function(){return\"BuildLogicClass(type=\"+this.tb_1+\")\"},ve(bo).hashCode=function(){return he(this.tb_1)},ve(bo).equals=function(n){if(this===n)return!0;if(!(n instanceof bo))return!1;var t=n instanceof bo?n:pe();return this.tb_1===t.tb_1},ve(ko).zb=function(){return this.yb_1},ve(ko).toString=function(){return\"TaskTreeIntent(delegate=\"+this.yb_1+\")\"},ve(ko).hashCode=function(){return ae(this.yb_1)},ve(ko).equals=function(n){if(this===n)return!0;if(!(n instanceof ko))return!1;var t=n instanceof ko?n:pe();return!!le(this.yb_1,t.yb_1)},ve(qo).zb=function(){return this.ac_1},ve(qo).toString=function(){return\"MessageTreeIntent(delegate=\"+this.ac_1+\")\"},ve(qo).hashCode=function(){return ae(this.ac_1)},ve(qo).equals=function(n){if(this===n)return!0;if(!(n instanceof qo))return!1;var t=n instanceof qo?n:pe();return!!le(this.ac_1,t.ac_1)},ve(yo).zb=function(){return this.bc_1},ve(yo).toString=function(){return\"InputTreeIntent(delegate=\"+this.bc_1+\")\"},ve(yo).hashCode=function(){return ae(this.bc_1)},ve(yo).equals=function(n){if(this===n)return!0;if(!(n instanceof yo))return!1;var t=n instanceof yo?n:pe();return!!le(this.bc_1,t.bc_1)},ve(Bo).zb=function(){return this.cc_1},ve(Bo).toString=function(){return\"IncompatibleTaskTreeIntent(delegate=\"+this.cc_1+\")\"},ve(Bo).hashCode=function(){return ae(this.cc_1)},ve(Bo).equals=function(n){if(this===n)return!0;if(!(n instanceof Bo))return!1;var t=n instanceof Bo?n:pe();return!!le(this.cc_1,t.cc_1)},ve(Co).toString=function(){return\"SetTab(tab=\"+this.dc_1+\")\"},ve(Co).hashCode=function(){return this.dc_1.hashCode()},ve(Co).equals=function(n){if(this===n)return!0;if(!(n instanceof Co))return!1;var t=n instanceof Co?n:pe();return!!this.dc_1.equals(t.dc_1)},ve(xo).mc=function(n,t,r,i,e,u,o,f){return new xo(n,t,r,i,e,u,o,f)},ve(xo).nc=function(n,t,r,i,e,u,o,f,s){return n=n===A?this.ec_1:n,t=t===A?this.fc_1:t,r=r===A?this.gc_1:r,i=i===A?this.hc_1:i,e=e===A?this.ic_1:e,u=u===A?this.jc_1:u,o=o===A?this.kc_1:o,f=f===A?this.lc_1:f,s===A?this.mc(n,t,r,i,e,u,o,f):s.mc.call(this,n,t,r,i,e,u,o,f)},ve(xo).toString=function(){return\"Model(heading=\"+this.ec_1+\", summary=\"+this.fc_1+\", learnMore=\"+this.gc_1+\", messageTree=\"+this.hc_1+\", locationTree=\"+this.ic_1+\", inputTree=\"+this.jc_1+\", incompatibleTaskTree=\"+this.kc_1+\", tab=\"+this.lc_1+\")\"},ve(xo).hashCode=function(){var n=this.ec_1.hashCode();return n=Mn(n,31)+ae(this.fc_1)|0,n=Mn(n,31)+this.gc_1.hashCode()|0,n=Mn(n,31)+this.hc_1.hashCode()|0,n=Mn(n,31)+this.ic_1.hashCode()|0,n=Mn(n,31)+this.jc_1.hashCode()|0,n=Mn(n,31)+this.kc_1.hashCode()|0,Mn(n,31)+this.lc_1.hashCode()|0},ve(xo).equals=function(n){if(this===n)return!0;if(!(n instanceof xo))return!1;var t=n instanceof xo?n:pe();return!!(this.ec_1.equals(t.ec_1)&&le(this.fc_1,t.fc_1)&&this.gc_1.equals(t.gc_1)&&this.hc_1.equals(t.hc_1)&&this.ic_1.equals(t.ic_1)&&this.jc_1.equals(t.jc_1)&&this.kc_1.equals(t.kc_1)&&this.lc_1.equals(t.lc_1))},ve(Ko).gd=function(n,t){var r,i;return n instanceof ko?r=t.nc(A,A,A,A,ps().id(n.yb_1,t.ic_1)):n instanceof qo?r=t.nc(A,A,A,ps().id(n.ac_1,t.hc_1)):n instanceof yo?r=t.nc(A,A,A,A,A,ps().id(n.bc_1,t.jc_1)):n instanceof Bo?r=t.nc(A,A,A,A,A,A,ps().id(n.cc_1,t.kc_1)):n instanceof Dc?r=function(n,t,r,i){var e;return r instanceof qo?e=n.nc(A,A,A,Ac(n.hc_1,r,i)):r instanceof ko?e=n.nc(A,A,A,A,Ac(n.ic_1,r,i)):r instanceof yo?e=n.nc(A,A,A,A,A,Ac(n.jc_1,r,i)):r instanceof Bo?e=n.nc(A,A,A,A,A,A,Ac(n.kc_1,r,i)):(console.error(\"Unhandled tree intent: \"+r),e=n),e}(t,0,n.xc_1,(i=n,function(n){var t;if(!(n instanceof Vu))throw _u(ce(\"Failed requirement.\"));for(var r=n.oa_1,e=i.wc_1,u=Rr(Tt(r,10)),o=0,f=r.f();f.g();){var s,c,a=f.h(),h=o;if(o=h+1|0,e===yr(h)){var l=a.sa_1;c=a.ua(A,null==l?null:l.ad())}else c=a;s=c,u.d(s)}return t=u,n.qa(A,A,t)})):n instanceof Mc?(window.navigator.clipboard.writeText(n.hd_1),r=t):n instanceof Co?r=t.nc(A,A,A,A,A,A,A,n.dc_1):(console.error(\"Unhandled intent: \"+n),r=t),r},ve(Ko).ba=function(n,t){var r=n instanceof Oc?n:pe();return this.gd(r,t instanceof xo?t:pe())},ve(Ko).jd=function(n){return Hf().y9(us(Do),[Io(0,n),So(0,n)])},ve(Ko).z9=function(n){return this.jd(n instanceof xo?n:pe())},ve(nf).toString=function(){return\"ImportedProblem(problem=\"+this.kd_1+\", message=\"+this.ld_1+\", trace=\"+this.md_1+\")\"},ve(nf).hashCode=function(){var n=ae(this.kd_1);return n=Mn(n,31)+this.ld_1.hashCode()|0,Mn(n,31)+ae(this.md_1)|0},ve(nf).equals=function(n){if(this===n)return!0;if(!(n instanceof nf))return!1;var t=n instanceof nf?n:pe();return!!le(this.kd_1,t.kd_1)&&!!this.ld_1.equals(t.ld_1)&&!!le(this.md_1,t.md_1)},ve(_f).ud=function(n,t){return this.td_1(n,t)},ve(_f).compare=function(n,t){return this.ud(n,t)},ve(Bf).toString=function(){return\"LearnMore(text=\"+this.sc_1+\", documentationLink=\"+this.tc_1+\")\"},ve(Bf).hashCode=function(){var n=he(this.sc_1);return Mn(n,31)+he(this.tc_1)|0},ve(Bf).equals=function(n){if(this===n)return!0;if(!(n instanceof Bf))return!1;var t=n instanceof Bf?n:pe();return this.sc_1===t.sc_1&&this.tc_1===t.tc_1},ve(Cf).toString=function(){return\"Text(text=\"+this.fa_1+\")\"},ve(Cf).hashCode=function(){return he(this.fa_1)},ve(Cf).equals=function(n){if(this===n)return!0;if(!(n instanceof Cf))return!1;var t=n instanceof Cf?n:pe();return this.fa_1===t.fa_1},ve(xf).toString=function(){return\"Reference(name=\"+this.da_1+\", clipboardString=\"+this.ea_1+\")\"},ve(xf).hashCode=function(){var n=he(this.da_1);return Mn(n,31)+he(this.ea_1)|0},ve(xf).equals=function(n){if(this===n)return!0;if(!(n instanceof xf))return!1;var t=n instanceof xf?n:pe();return this.da_1===t.da_1&&this.ea_1===t.ea_1},ve(Pf).ed=function(n){return this.dd_1.d(new Cf(n)),this},ve(Pf).xd=function(n,t){return this.dd_1.d(new xf(n,t)),this},ve(Pf).fd=function(n,t,r){return t=t===A?n:t,r===A?this.xd(n,t):r.xd.call(this,n,t)},ve(Pf).j5=function(){return new zf(Qn(this.dd_1))},ve(If).rd=function(n){return new zf(qr(new Cf(n)))},ve(If).qd=function(n){var t=new Pf;return n(t),t.j5()},ve(zf).vd=function(n){return new zf(n)},ve(zf).toString=function(){return\"PrettyText(fragments=\"+this.ca_1+\")\"},ve(zf).hashCode=function(){return ae(this.ca_1)},ve(zf).equals=function(n){if(this===n)return!0;if(!(n instanceof zf))return!1;var t=n instanceof zf?n:pe();return!!le(this.ca_1,t.ca_1)},ve(Tf).sd=function(n){return function(n){for(var t=Wr(),r=n.f();r.g();)for(var i=t,e=r.h().f();e.g();){var u,o=e.h(),f=i,s=f.v1(o);if(null==s){var c=Wr();f.h5(o,c),u=c}else u=s;i=u instanceof Xr?u:pe()}return t}(n)},ve(Af).toString=function(){return\"Trie(nestedMaps=\"+this.wd_1+\")\"},ve(Af).hashCode=function(){return ae(this.wd_1)},ve(Af).equals=function(n){return function(n,t){return t instanceof Af&&!!le(n,t instanceof Af?t.wd_1:pe())}(this.wd_1,n)},ve(Kf).ga=function(n){return Jf().yd(this.x9_1,A,n)},ve(Kf).ha=function(n){return Jf().yd(this.x9_1,A,A,n)},ve(Kf).ja=function(n){return Jf().yd(this.x9_1,A,A,ou(n))},ve(Kf).y9=function(n,t){return Jf().yd(this.x9_1,n,A,ou(t))},ve(Kf).zd=function(n,t){return Jf().yd(this.x9_1,n,A,t)},ve(Kf).fb=function(n,t){return Jf().yd(this.x9_1,n,t)},ve(Kf).rc=function(n,t){return Jf().yd(this.x9_1,A,n,ou(t))},ve(Kf).toString=function(){return\"ViewFactory(elementName=\"+this.x9_1+\")\"},ve(Kf).hashCode=function(){return he(this.x9_1)},ve(Kf).equals=function(n){if(this===n)return!0;if(!(n instanceof Kf))return!1;var t=n instanceof Kf?n:pe();return this.x9_1===t.x9_1},ve(Xf).ae=function(n,t,r,i){return new rs(n,t,r,i)},ve(Xf).yd=function(n,t,r,i,e){return t=t===A?Ct():t,r=r===A?null:r,i=i===A?Ct():i,e===A?this.ae(n,t,r,i):e.ae.call(this,n,t,r,i)},ve(rs).toString=function(){return\"Element(elementName=\"+this.be_1+\", attributes=\"+this.ce_1+\", innerText=\"+this.de_1+\", children=\"+this.ee_1+\")\"},ve(rs).hashCode=function(){var n=he(this.be_1);return n=Mn(n,31)+ae(this.ce_1)|0,n=Mn(n,31)+(null==this.de_1?0:he(this.de_1))|0,Mn(n,31)+ae(this.ee_1)|0},ve(rs).equals=function(n){if(this===n)return!0;if(!(n instanceof rs))return!1;var t=n instanceof rs?n:pe();return this.be_1===t.be_1&&!!le(this.ce_1,t.ce_1)&&this.de_1==t.de_1&&!!le(this.ee_1,t.ee_1)},ve(os).u9=function(n){return this.r9_1(new fs(\"click\",n))},ve(os).t9=function(n){return this.r9_1(new ss(n))},ve(os).gb=function(n){for(var t=0,r=n.length;t<r;){var i=n[t];t=t+1|0,this.r9_1(new ss(i))}return br()},ve(os).s9=function(n){return this.r9_1(new cs(\"title\",n))},ve(os).bd=function(n){return this.r9_1(new cs(\"href\",n))},ve(vs).oe=function(){return this.ne_1},ve(vs).toString=function(){return\"Toggle(focus=\"+this.ne_1+\")\"},ve(vs).hashCode=function(){return ae(this.ne_1)},ve(vs).equals=function(n){if(this===n)return!0;if(!(n instanceof vs))return!1;var t=n instanceof vs?n:pe();return!!le(this.ne_1,t.ne_1)},ve(ds).pe=function(n,t){return this.re(n.qe((r=t,function(n){return n.me(r(n.ub_1))})));var r},ve(ds).re=function(n){return new ds(n)},ve(ds).toString=function(){return\"Model(tree=\"+this.xb_1+\")\"},ve(ds).hashCode=function(){return this.xb_1.hashCode()},ve(ds).equals=function(n){if(this===n)return!0;if(!(n instanceof ds))return!1;var t=n instanceof ds?n:pe();return!!this.xb_1.equals(t.xb_1)},ve(bs).id=function(n,t){var r;if(n instanceof vs){var i=n.oe();r=t.re(i.qe(ws))}else be();return r},ve(ks).cd=function(){return this.ve_1},ve(ks).we=function(){return 0},ve(ks).qe=function(n){return n(this.ve_1)},ve(ks).toString=function(){return\"Original(tree=\"+this.ve_1+\")\"},ve(ks).hashCode=function(){return this.ve_1.hashCode()},ve(ks).equals=function(n){if(this===n)return!0;if(!(n instanceof ks))return!1;var t=n instanceof ks?n:pe();return!!this.ve_1.equals(t.ve_1)},ve(qs).cd=function(){return this.ue_1},ve(qs).we=function(){return this.se_1.we()+1|0},ve(qs).qe=function(n){return this.se_1.qe((t=this,r=n,function(n){for(var i,e=n.vb_1,u=t.te_1,o=Rr(Tt(e,10)),f=0,s=e.f();s.g();){var c,a=s.h(),h=f;f=h+1|0,c=u===yr(h)?r(a):a,o.d(c)}return i=o,n.me(A,i)}));var t,r},ve(qs).toString=function(){return\"Child(parent=\"+this.se_1+\", index=\"+this.te_1+\", tree=\"+this.ue_1+\")\"},ve(qs).hashCode=function(){var n=ae(this.se_1);return n=Mn(n,31)+this.te_1|0,Mn(n,31)+this.ue_1.hashCode()|0},ve(qs).equals=function(n){if(this===n)return!0;if(!(n instanceof qs))return!1;var t=n instanceof qs?n:pe();return!!le(this.se_1,t.se_1)&&this.te_1===t.te_1&&!!this.ue_1.equals(t.ue_1)},ve(ys).ad=function(){var n;switch(this.q8_1){case 0:n=xs();break;case 1:n=Cs();break;default:be()}return n},ve(Bs).vc=function(){var n,t;return ut(Vn(Ve(0,this.cd().vb_1.k()-1|0)),(n=this,(t=function(t){return n.xe(t)}).callableName=\"child\",t))},ve(Bs).xe=function(n){return new qs(this,n,this.cd().vb_1.j(n))},ve(js).uc=function(){return new ks(this)},ve(js).ye=function(){return!this.vb_1.i()},ve(js).ze=function(n,t,r){return new js(n,t,r)},ve(js).me=function(n,t,r,i){return n=n===A?this.ub_1:n,t=t===A?this.vb_1:t,r=r===A?this.wb_1:r,i===A?this.ze(n,t,r):i.ze.call(this,n,t,r)},ve(js).toString=function(){return\"Tree(label=\"+this.ub_1+\", children=\"+this.vb_1+\", state=\"+this.wb_1+\")\"},ve(js).hashCode=function(){var n=null==this.ub_1?0:ae(this.ub_1);return n=Mn(n,31)+ae(this.vb_1)|0,Mn(n,31)+this.wb_1.hashCode()|0},ve(js).equals=function(n){if(this===n)return!0;if(!(n instanceof js))return!1;var t=n instanceof js?n:pe();return!!le(this.ub_1,t.ub_1)&&!!le(this.vb_1,t.vb_1)&&!!this.wb_1.equals(t.wb_1)},ve(zs).toString=function(){return\"ProblemIdElement(name=\"+this.df_1+\", displayName=\"+this.ef_1+\")\"},ve(zs).hashCode=function(){var n=he(this.df_1);return Mn(n,31)+he(this.ef_1)|0},ve(zs).equals=function(n){if(this===n)return!0;if(!(n instanceof zs))return!1;var t=n instanceof zs?n:pe();return this.df_1===t.df_1&&this.ef_1===t.ef_1},ve(Ts).toString=function(){return\"ProblemSummary(problemId=\"+this.ff_1+\", count=\"+this.gf_1+\")\"},ve(Ts).hashCode=function(){var n=ae(this.ff_1);return Mn(n,31)+this.gf_1|0},ve(Ts).equals=function(n){if(this===n)return!0;if(!(n instanceof Ts))return!1;var t=n instanceof Ts?n:pe();return!!le(this.ff_1,t.ff_1)&&this.gf_1===t.gf_1},ve(Ms).toString=function(){return\"ProblemNodeGroup(tree=\"+this.hf_1+\", children=\"+this.if_1+\", childGroups=\"+this.jf_1+\", id=\"+this.kf_1+\")\"},ve(Ms).hashCode=function(){var n=this.hf_1.hashCode();return n=Mn(n,31)+ae(this.if_1)|0,n=Mn(n,31)+ae(this.jf_1)|0,Mn(n,31)+this.kf_1|0},ve(Ms).equals=function(n){if(this===n)return!0;if(!(n instanceof Ms))return!1;var t=n instanceof Ms?n:pe();return!!this.hf_1.equals(t.hf_1)&&!!le(this.if_1,t.if_1)&&!!le(this.jf_1,t.jf_1)&&this.kf_1===t.kf_1},ve(uc).toString=function(){return\"Text(text=\"+this.lf_1+\")\"},ve(uc).hashCode=function(){return he(this.lf_1)},ve(uc).equals=function(n){if(this===n)return!0;if(!(n instanceof uc))return!1;var t=n instanceof uc?n:pe();return this.lf_1===t.lf_1},ve(oc).toString=function(){return\"ProblemIdNode(prettyText=\"+this.mf_1+\", separator=\"+this.nf_1+\")\"},ve(oc).hashCode=function(){var n=this.mf_1.hashCode();return Mn(n,31)+(0|this.nf_1)|0},ve(oc).equals=function(n){if(this===n)return!0;if(!(n instanceof oc))return!1;var t=n instanceof oc?n:pe();return!!this.mf_1.equals(t.mf_1)&&this.nf_1===t.nf_1},ve(fc).toString=function(){return\"Advice(label=\"+this.of_1+\", docLink=\"+this.pf_1+\")\"},ve(fc).hashCode=function(){var n=ae(this.of_1);return Mn(n,31)+(null==this.pf_1?0:ae(this.pf_1))|0},ve(fc).equals=function(n){if(this===n)return!0;if(!(n instanceof fc))return!1;var t=n instanceof fc?n:pe();return!!le(this.of_1,t.of_1)&&!!le(this.pf_1,t.pf_1)},ve(cc).zb=function(){return this.qf_1},ve(cc).toString=function(){return\"MessageTreeIntent(delegate=\"+this.qf_1+\")\"},ve(cc).hashCode=function(){return ae(this.qf_1)},ve(cc).equals=function(n){if(this===n)return!0;if(!(n instanceof cc))return!1;var t=n instanceof cc?n:pe();return!!le(this.qf_1,t.qf_1)},ve(ac).zb=function(){return this.rf_1},ve(ac).toString=function(){return\"ProblemIdTreeIntent(delegate=\"+this.rf_1+\")\"},ve(ac).hashCode=function(){return ae(this.rf_1)},ve(ac).equals=function(n){if(this===n)return!0;if(!(n instanceof ac))return!1;var t=n instanceof ac?n:pe();return!!le(this.rf_1,t.rf_1)},ve(hc).zb=function(){return this.sf_1},ve(hc).toString=function(){return\"FileLocationTreeIntent(delegate=\"+this.sf_1+\")\"},ve(hc).hashCode=function(){return ae(this.sf_1)},ve(hc).equals=function(n){if(this===n)return!0;if(!(n instanceof hc))return!1;var t=n instanceof hc?n:pe();return!!le(this.sf_1,t.sf_1)},ve(lc).zb=function(){return this.tf_1},ve(lc).toString=function(){return\"PluginLocationTreeIntent(delegate=\"+this.tf_1+\")\"},ve(lc).hashCode=function(){return ae(this.tf_1)},ve(lc).equals=function(n){if(this===n)return!0;if(!(n instanceof lc))return!1;var t=n instanceof lc?n:pe();return!!le(this.tf_1,t.tf_1)},ve(_c).zb=function(){return this.uf_1},ve(_c).toString=function(){return\"TaskLocationTreeIntent(delegate=\"+this.uf_1+\")\"},ve(_c).hashCode=function(){return ae(this.uf_1)},ve(_c).equals=function(n){if(this===n)return!0;if(!(n instanceof _c))return!1;var t=n instanceof _c?n:pe();return!!le(this.uf_1,t.uf_1)},ve(vc).toString=function(){return\"SetTab(tab=\"+this.vf_1+\")\"},ve(vc).hashCode=function(){return this.vf_1.hashCode()},ve(vc).equals=function(n){if(this===n)return!0;if(!(n instanceof vc))return!1;var t=n instanceof vc?n:pe();return!!this.vf_1.equals(t.vf_1)},ve(dc).gg=function(n,t,r,i,e,u,o,f,s,c){return new dc(n,t,r,i,e,u,o,f,s,c)},ve(dc).hg=function(n,t,r,i,e,u,o,f,s,c,a){return n=n===A?this.wf_1:n,t=t===A?this.xf_1:t,r=r===A?this.yf_1:r,i=i===A?this.zf_1:i,e=e===A?this.ag_1:e,u=u===A?this.bg_1:u,o=o===A?this.cg_1:o,f=f===A?this.dg_1:f,s=s===A?this.eg_1:s,c=c===A?this.fg_1:c,a===A?this.gg(n,t,r,i,e,u,o,f,s,c):a.gg.call(this,n,t,r,i,e,u,o,f,s,c)},ve(dc).toString=function(){return\"Model(heading=\"+this.wf_1+\", summary=\"+this.xf_1+\", learnMore=\"+this.yf_1+\", messageTree=\"+this.zf_1+\", problemIdTree=\"+this.ag_1+\", fileLocationTree=\"+this.bg_1+\", pluginLocationTree=\"+this.cg_1+\", taskLocationTree=\"+this.dg_1+\", problemCount=\"+this.eg_1+\", tab=\"+this.fg_1+\")\"},ve(dc).hashCode=function(){var n=this.wf_1.hashCode();return n=Mn(n,31)+ae(this.xf_1)|0,n=Mn(n,31)+this.yf_1.hashCode()|0,n=Mn(n,31)+this.zf_1.hashCode()|0,n=Mn(n,31)+this.ag_1.hashCode()|0,n=Mn(n,31)+this.bg_1.hashCode()|0,n=Mn(n,31)+this.cg_1.hashCode()|0,n=Mn(n,31)+this.dg_1.hashCode()|0,n=Mn(n,31)+this.eg_1|0,Mn(n,31)+this.fg_1.hashCode()|0},ve(dc).equals=function(n){if(this===n)return!0;if(!(n instanceof dc))return!1;var t=n instanceof dc?n:pe();return!!(this.wf_1.equals(t.wf_1)&&le(this.xf_1,t.xf_1)&&this.yf_1.equals(t.yf_1)&&this.zf_1.equals(t.zf_1)&&this.ag_1.equals(t.ag_1)&&this.bg_1.equals(t.bg_1)&&this.cg_1.equals(t.cg_1)&&this.dg_1.equals(t.dg_1)&&this.eg_1===t.eg_1&&this.fg_1.equals(t.fg_1))},ve(Lc).ig=function(n,t){var r,i;return n instanceof hc?r=t.hg(A,A,A,A,A,ps().id(n.sf_1,t.bg_1)):n instanceof lc?r=t.hg(A,A,A,A,A,A,ps().id(n.tf_1,t.cg_1)):n instanceof _c?r=t.hg(A,A,A,A,A,A,A,ps().id(n.uf_1,t.dg_1)):n instanceof ac?r=t.hg(A,A,A,A,ps().id(n.rf_1,t.ag_1)):n instanceof cc?r=t.hg(A,A,A,ps().id(n.qf_1,t.zf_1)):n instanceof Dc?r=function(n,t,r,i){var e;return r instanceof cc?e=n.hg(A,A,A,Ac(n.zf_1,r,i)):r instanceof ac?e=n.hg(A,A,A,A,Ac(n.ag_1,r,i)):r instanceof hc?e=n.hg(A,A,A,A,A,Ac(n.bg_1,r,i)):r instanceof lc?e=n.hg(A,A,A,A,A,A,Ac(n.cg_1,r,i)):r instanceof _c?e=n.hg(A,A,A,A,A,A,A,Ac(n.dg_1,r,i)):(console.error(\"Unhandled tree intent: \"+r),e=n),e}(t,0,n.xc_1,(i=n,function(n){var t;if(!(n instanceof Vu))throw _u(ce(\"Failed requirement.\"));for(var r=n.oa_1,e=i.wc_1,u=Rr(Tt(r,10)),o=0,f=r.f();f.g();){var s,c,a=f.h(),h=o;if(o=h+1|0,e===yr(h)){var l=a.sa_1;c=a.ua(A,null==l?null:l.ad())}else c=a;s=c,u.d(s)}return t=u,n.qa(A,A,t)})):n instanceof Mc?(window.navigator.clipboard.writeText(n.hd_1),r=t):n instanceof vc?r=t.hg(A,A,A,A,A,A,A,A,A,n.vf_1):(console.error(\"Unhandled intent: \"+n),r=t),r},ve(Lc).ba=function(n,t){var r=n instanceof Oc?n:pe();return this.ig(r,t instanceof dc?t:pe())},ve(Lc).jg=function(n){return Hf().y9(us(xc),[wc(0,n),bc(0,n)])},ve(Lc).z9=function(n){return this.jg(n instanceof dc?n:pe())},ve(Mc).toString=function(){return\"Copy(text=\"+this.hd_1+\")\"},ve(Mc).hashCode=function(){return he(this.hd_1)},ve(Mc).equals=function(n){if(this===n)return!0;if(!(n instanceof Mc))return!1;var t=n instanceof Mc?n:pe();return this.hd_1===t.hd_1},ve(Dc).toString=function(){return\"ToggleStackTracePart(partIndex=\"+this.wc_1+\", location=\"+this.xc_1+\")\"},ve(Dc).hashCode=function(){var n=this.wc_1;return Mn(n,31)+ae(this.xc_1)|0},ve(Dc).equals=function(n){if(this===n)return!0;if(!(n instanceof Dc))return!1;var t=n instanceof Dc?n:pe();return this.wc_1===t.wc_1&&!!le(this.xc_1,t.xc_1)},ve(ui).k6=function(){var n=Object.create(null);return n.foo=1,delete n.foo,br(),n},l=null,function(){var n=configurationCacheProblems();if(null==n.problemsReport)Mf(Ff(\"report\"),Xo(),function(n){var t,r,i,e,u,o,f,s,c,a,h=function(n){for(var t=Or(),r=Or(),i=Or(),e=0,u=n.length;e<u;){var o=n[e];e=e+1|0;var f,s=o.input,c=null==s?null:r.d(rf(s,o));if(null==c){var a=o.incompatibleTask;f=null==a?null:i.d(rf(a,o))}else f=c;if(null==f){var h=we(o.problem);t.d(rf(h,o))}}return new tf(t,r,i)}(n.diagnostics),l=n.totalProblemCount;return new xo((f=(t=n).buildName,s=t.requestedTasks,c=null==s?null:rr(s,\" \",A,r=r!==A&&r)>=0,a=null==c||c,Sf().qd((i=t,e=f,u=s,o=a,function(n){n.ed(function(n){var t;if(re(n)>0){var r,i=ne(n,0);r=function(n){return 97<=n&&n<=122||!(Ei(n,128)<0)&&function(n){var t;return t=1===function(n){var t=n,r=su(au().h9_1,t),i=au().h9_1[r],e=(i+au().i9_1[r]|0)-1|0,u=au().j9_1[r];if(t>e)return 0;var o=3&u;if(0===o){var f=2,s=i,c=0;if(c<=1)do{if(c=c+1|0,(s=s+(u>>f&127)|0)>t)return 3;if((s=s+(u>>(f=f+7|0)&127)|0)>t)return 0;f=f+7|0}while(c<=1);return 3}if(u<=7)return o;var a=t-i|0;return u>>Mn(2,u<=31?a%2|0:a)&3}(n)||function(n){var t=su(lu().k9_1,n);return t>=0&&n<(lu().k9_1[t]+lu().l9_1[t]|0)}(n),t}(n)}(i)?function(n){return function(n){var t=Li(n).toUpperCase();if(t.length>1){var r;if(329===n)r=t;else{var i=ne(t,0),e=t.substring(1).toLowerCase();r=Li(i)+e}return r}return Li(function(n){return function(n){var t=n;return 452<=t&&t<=460||497<=t&&t<=499?Ue(Mn(3,(t+1|0)/3|0)):4304<=t&&t<=4346||4349<=t&&t<=4351?n:xi(n)}(n)}(n))}(n)}(i):Li(i),t=ce(r)+n.substring(1)}else t=n;return t}(i.cacheAction)+\" the configuration cache for \");var t=e;null==t||n.fd(t),null==e||n.ed(\" build and \");var r=u;return null==(null==r?null:n.fd(r))&&n.ed(\"default\"),n.ed(o?\" tasks\":\" task\"),br()}))),function(n,t){var r=n.cacheActionDescription,i=null==r?null:mf(r),e=Sf().rd(function(n){var t=n.od_1.k(),r=kf(t,\"build configuration input\");return t>0?r+\" and will cause the cache to be discarded when \"+(t<=1?\"its\":\"their\")+\" value change\":r}(t)),u=Sf().rd(function(n,t){var r=n.totalProblemCount,i=t.nd_1.k(),e=kf(r,\"problem\");return r>i?e+\", only the first \"+i+\" \"+yf(i)+\" included in this report\":e}(n,t));return function(n,t){for(var r=0,i=n.length;r<i;){var e=n[r];r=r+1|0,null!=e&&t.d(e)}return t}([i,e,u],Or())}(n,h),new Bf(\"Gradle Configuration Cache\",n.documentationLink),hf(new no(Zo().qc_1),ut(Vn(h.nd_1),gf)),hf(new no(Yo().qc_1),function(n){return ut(Vn(n),wf)}(h.nd_1)),hf(new no(Qo().qc_1),ut(Vn(h.od_1),vf)),hf(new no(Wo().qc_1),ut(Vn(h.pd_1),df)),0===l?Qo():Zo())}(n));else{var t=n.problemsReport;Mf(Ff(\"report\"),Nc(),function(n,t){ec();for(var r=n.summaries,i=Rr(r.length),e=0,u=r.length;e<u;){var o,f=r[e];e=e+1|0;for(var s=f.problemId,c=Rr(s.length),a=0,h=s.length;a<h;){var l,_=s[a];a=a+1|0,l=new zs(_.name,_.displayName),c.d(l)}o=new Ts(c,f.count),i.d(o)}for(var v=i,d=function(n,t){ec();for(var r=li(),i=0,e=n.length;i<e;){var u=n[i];i=i+1|0;var o,f=(a=u.problemId,ec(),On(a,\":\",A,A,A,A,Ws)),s=r.v1(f);if(null==s){var c=Or();r.h5(f,c),o=c}else o=s;o.d(u)}for(var a,h=r.o(),l=Rr(Tt(h,10)),_=h.f();_.g();){for(var v,d=_.h(),g=d.i1(),w=Rr(Tt(g,10)),b=g.f();b.g();){var p;p=Os(b.h(),null,!0),w.d(p)}var m,k=Zn(w),q=Un(d.i1()),y=Hs(q,new Wu($s(Rs(q)).ed(\" (\"+d.i1().k()+\")\").j5()));n:{for(var B=t.f();B.g();){var C=B.h();if(Ds(C.ff_1,q.problemId)){m=C;break n}}m=null}var x=m;null==x||k.d(Ns(x.gf_1)),v=new js(y,k),l.d(v)}var j=l;return new ds(new js(new uc(\"message tree root\"),j))}(t,v),g=function(n,t){ec();for(var r=function(){ec();var n=Or();return new Ms(new js(new oc(Sf().rd(\"Ungrouped\"),!0),n),n,li())}(),i=li(),e=0,u=n.length;e<u;){var o=n[e];e=e+1|0;for(var f=Yn(Dn(o.problemId,1)),s=Rr(Tt(f,10)),c=f.f();c.g();){var a,h=c.h();a=new zs(h.name,h.displayName),s.d(a)}var l=Fs(i,s),_=Os(o);null==l?r.if_1.d(_):l.if_1.d(_)}for(var v=Rr(Tt(t,10)),d=t.f();d.g();){var g,w=d.h();g=new Ts(Yn(Wn(w.ff_1,1)),w.gf_1),v.d(g)}for(var b=li(),p=v.f();p.g();){var m,k=p.h(),q=k.ff_1,y=b.v1(q);if(null==y){var B=Or();b.h5(q,B),m=B}else m=y;m.d(k)}for(var C=b.o(),x=Rr(Tt(C,10)),j=C.f();j.g();){for(var P,I=j.h(),S=I.j1(),z=0,T=I.i1().f();T.g();)z=z+T.h().gf_1|0;P=new Ts(S,z),x.d(P)}for(var E=x.f();E.g();){var L=E.h(),N=Fs(i,L.ff_1),A=null==N?null:N.if_1;null==A||A.d(Ns(L.gf_1))}for(var M=i.w1(),F=Rr(Tt(M,10)),D=M.f();D.g();){var O;O=D.h().hf_1,F.d(O)}var R=function(n,t){var r=Rr(n.k());return r.m(n),r.d(t),r}(F,r.hf_1);return new ds(new js(new uc(\"group tree root\"),R))}(t,v),w=0,b=v.f();b.g();)w=w+b.h().gf_1|0;for(var p=Es(t,w,(ec(),wn)),m=0,k=v.f();k.g();)m=m+k.h().gf_1|0;for(var q=Es(t,m,(ec(),bn)),y=0,B=v.f();B.g();)y=y+B.h().gf_1|0;var C=Es(t,y,(ec(),pn));return new dc(Sf().rd(\"Problems Report\"),function(n,t){ec();var r,i,e,u=n.description,o=null==u?null:qr(mf(u));return r=null==o?qr(Sf().qd((i=t,e=n,function(n){n.ed(i.length+\" problems have been reported during the execution\");var t=e.buildName;null==t||(n.ed(\" of build \"),n.fd(t));var r=e.requestedTasks;return null==r||(n.ed(\" for the following tasks:\"),n.fd(r),br()),br()}))):o,r}(n,t),new Bf(\"reporting problems\",n.documentationLink),d,g,p,q,C,t.length,function(n,t,r,i,e){return ec(),po(r)>0?tc():po(n)>0?Js():po(t)>0?nc():po(i)>0?rc():po(e)>0?ic():Js()}(d,g,p,q,C))}(t,n.diagnostics))}}(),n}(void 0===this[\"configuration-cache-report\"]?{}:this[\"configuration-cache-report\"])}}[70](),{}))));\n//# sourceMappingURL=configuration-cache-report.js.map\n                </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "android/build.gradle.kts",
    "content": "allprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nval newBuildDir: Directory =\n    rootProject.layout.buildDirectory\n        .dir(\"../../build\")\n        .get()\nrootProject.layout.buildDirectory.value(newBuildDir)\n\nsubprojects {\n    val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)\n    project.layout.buildDirectory.value(newSubprojectBuildDir)\n}\nsubprojects {\n    project.evaluationDependsOn(\":app\")\n}\n\ntasks.register<Delete>(\"clean\") {\n    delete(rootProject.layout.buildDirectory)\n}\n"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14-all.zip\norg.gradle.jvmargs=--enable-native-access=ALL-UNNAMED\n"
  },
  {
    "path": "android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError\nandroid.useAndroidX=true\n"
  },
  {
    "path": "android/settings.gradle.kts",
    "content": "pluginManagement {\n    val flutterSdkPath =\n        run {\n            val properties = java.util.Properties()\n            file(\"local.properties\").inputStream().use { properties.load(it) }\n            val flutterSdkPath = properties.getProperty(\"flutter.sdk\")\n            require(flutterSdkPath != null) { \"flutter.sdk not set in local.properties\" }\n            flutterSdkPath\n        }\n\n    includeBuild(\"$flutterSdkPath/packages/flutter_tools/gradle\")\n\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\nplugins {\n    id(\"dev.flutter.flutter-plugin-loader\") version \"1.0.0\"\n    id(\"com.android.application\") version \"8.11.1\" apply false\n    id(\"org.jetbrains.kotlin.android\") version \"2.2.20\" apply false\n}\n\ninclude(\":app\")\n"
  },
  {
    "path": "build-flatpak.sh",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\n\nAPP_ID=\"com.gyawun.music\"\nAPP_NAME=\"GyawunMusic\"\nVERSION=\"2.0.16\"\nBUNDLE_DIR=\"build/linux/x64/release/bundle\"\nOLD_BIN=\"gyawun\"\nNEW_BIN=\"gmusic\"\nMANIFEST=\"com.gyawun.music.yml\"\nBUILD_DIR=\"build-dir\"\nREPO_DIR=\"repo\"\nOUT_FILE=\"${APP_NAME}-${VERSION}.flatpak\"\n\necho \"▶ Building Flutter Linux release…\"\nflutter build linux --release\n\necho \"▶ Verifying Flutter bundle…\"\nif [[ ! -d \"$BUNDLE_DIR\" ]]; then\n  echo \"ERROR: Flutter bundle not found at $BUNDLE_DIR\"\n  exit 1\nfi\n\nif [[ ! -f \"$BUNDLE_DIR/$OLD_BIN\" && ! -f \"$BUNDLE_DIR/$NEW_BIN\" ]]; then\n  echo \"ERROR: Neither '$OLD_BIN' nor '$NEW_BIN' found in bundle\"\n  exit 1\nfi\n\nif [[ -f \"$BUNDLE_DIR/$OLD_BIN\" ]]; then\n  echo \"▶ Renaming binary: $OLD_BIN → $NEW_BIN\"\n  mv -f \"$BUNDLE_DIR/$OLD_BIN\" \"$BUNDLE_DIR/$NEW_BIN\"\nfi\n\nchmod +x \"$BUNDLE_DIR/$NEW_BIN\"\n\necho \"▶ Building Flatpak (clean)…\"\nflatpak-builder --force-clean \"$BUILD_DIR\" \"$MANIFEST\"\n\necho \"▶ Exporting Flatpak repository…\"\nrm -rf \"$REPO_DIR\"\nflatpak-builder --force-clean --repo=\"$REPO_DIR\" \"$BUILD_DIR\" \"$MANIFEST\"\n\necho \"▶ Creating .flatpak bundle…\"\nflatpak build-bundle \\\n  \"$REPO_DIR\" \\\n  \"$OUT_FILE\" \\\n  \"$APP_ID\" \\\n  --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo\n\necho \"✔ Done.\"\necho \"✔ Bundle created: $OUT_FILE\"\n"
  },
  {
    "path": "devtools_options.yaml",
    "content": "description: This file stores settings for Dart & Flutter DevTools.\ndocumentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states\nextensions:\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/full_description.txt",
    "content": "Immerse yourself in the world of Gyawun, where music knows no bounds. Enjoy uninterrupted, ad-free streaming with an extensive library of songs spanning genres and artists from every corner of the globe. With Gyawun, the rhythm never fades. Download now and let the music carry you away. \n"
  },
  {
    "path": "fastlane/metadata/android/en-US/short_description.txt",
    "content": "Stream music from YouTube Music"
  },
  {
    "path": "fastlane/metadata/android/en-US/title.txt",
    "content": "Gyawun Music"
  },
  {
    "path": "flutter_native_splash.yaml",
    "content": "flutter_native_splash:\n  image: assets/images/splash_icon.png\n  background_image: \"assets/images/background.png\"\n  branding: assets/images/branding.png\n  android_12:\n    image: assets/images/icon_512.png\n    branding: assets/images/branding.png"
  },
  {
    "path": "lib/core/extensions/random_material_shape.dart",
    "content": "import 'dart:math' as math;\n\nimport 'package:m3e_collection/m3e_collection.dart';\n\nextension RandomMaterialShape on MaterialShapes {\n  static List<RoundedPolygon> randomshapes = [\n    MaterialShapes.slanted,\n    MaterialShapes.pill,\n    MaterialShapes.arrow,\n    MaterialShapes.fan,\n    MaterialShapes.clover4Leaf,\n  ];\n  static RoundedPolygon get random {\n    final random = math.Random();\n    final index = random.nextInt(randomshapes.length);\n    return randomshapes[index];\n  }\n}\n"
  },
  {
    "path": "lib/core/extensions/string_extensions.dart",
    "content": "extension StringCapitalization on String {\n  String capitalize() {\n    if (isEmpty) return this;\n    return this[0].toUpperCase() + substring(1);\n  }\n}\n"
  },
  {
    "path": "lib/core/models/app_config.dart",
    "content": "class AppConfig {\n  final bool isBeta;\n  final Uri stableReleasesUri;\n  final Uri allReleasesUri;\n  final String codeName; // e.g. 2.0.16 or 2.0.16-beta.3\n\n  AppConfig({\n    required this.isBeta,\n    required this.stableReleasesUri,\n    required this.allReleasesUri,\n    required this.codeName,\n  });\n}\n"
  },
  {
    "path": "lib/core/utils/expressive_sheet.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\n\nclass ExpressiveSheetOption<T> {\n  final String label;\n  final IconData? icon;\n  final T value;\n  final bool selected;\n\n  const ExpressiveSheetOption({\n    required this.label,\n    required this.value,\n    this.icon,\n    this.selected = false,\n  });\n}\n\nclass ExpressiveSheet {\n  /// Shows a modal bottom sheet with a list of options.\n  /// Returns the value of the selected option, or null if dismissed.\n  static Future<T?> showSelection<T>(\n    BuildContext context, {\n    required String title,\n    required List<ExpressiveSheetOption<T>> options,\n  }) {\n    final theme = Theme.of(context);\n    final colorScheme = theme.colorScheme;\n\n    return showModalBottomSheet<T>(\n      context: context,\n      isScrollControlled: true,\n      showDragHandle: true,\n      useRootNavigator: true,\n      backgroundColor: colorScheme.surfaceContainer,\n      shape: const RoundedRectangleBorder(\n        borderRadius: BorderRadius.vertical(top: Radius.circular(28)),\n      ),\n      builder: (context) {\n        return DraggableScrollableSheet(\n          expand: false,\n          builder: (context, controller) {\n            return ListView(\n              controller: controller,\n              padding: const EdgeInsets.fromLTRB(16, 0, 16, 24),\n              children: [\n                Padding(\n                  padding: const EdgeInsets.only(bottom: 24, left: 8, right: 8),\n                  child: Text(\n                    title,\n                    style: theme.textTheme.headlineSmall,\n                    textAlign: TextAlign.center,\n                  ),\n                ),\n                ...options.map((option) {\n                  return Padding(\n                    padding: const EdgeInsets.only(bottom: 8.0),\n                    child: ExpressiveListTile(\n                      title: Text(option.label),\n                      leading: option.icon != null ? Icon(option.icon) : null,\n                      trailing: option.selected\n                          ? Icon(FluentIcons.checkmark_24_filled)\n                          : null,\n                      onTap: () {\n                        Navigator.pop(context, option.value);\n                      },\n                    ),\n                  );\n                }),\n              ],\n            );\n          },\n        );\n      },\n    );\n  }\n\n  /// Shows a modal bottom sheet with a color selection grid.\n  /// Returns the selected color, or null if dismissed/cancelled.\n  static Future<Color?> showColorSelection(\n    BuildContext context, {\n    required String title,\n    Color? currentColor,\n    VoidCallback? onReset,\n  }) {\n    final theme = Theme.of(context);\n    final colorScheme = theme.colorScheme;\n\n    // Preset M3-style colors\n    final List<Color> presets = [\n      Colors.red,\n      Colors.pink,\n      Colors.purple,\n      Colors.deepPurple,\n      Colors.indigo,\n      Colors.blue,\n      Colors.lightBlue,\n      Colors.cyan,\n      Colors.teal,\n      Colors.green,\n      Colors.lightGreen,\n      Colors.lime,\n      Colors.yellow,\n      Colors.amber,\n      Colors.orange,\n      Colors.deepOrange,\n      Colors.brown,\n      Colors.grey,\n      Colors.blueGrey,\n      const Color(0xFF000000),\n    ];\n\n    Color? selectedColor = currentColor;\n\n    return showModalBottomSheet<Color>(\n      context: context,\n      isScrollControlled: false,\n      showDragHandle: true,\n      useRootNavigator: true,\n      backgroundColor: colorScheme.surfaceContainer,\n      shape: const RoundedRectangleBorder(\n        borderRadius: BorderRadius.vertical(top: Radius.circular(28)),\n      ),\n      builder: (context) {\n        return StatefulBuilder(\n          builder: (context, setState) {\n            return Column(\n              children: [\n                Padding(\n                  padding: const EdgeInsets.only(\n                    bottom: 16,\n                    left: 16,\n                    right: 16,\n                  ),\n                  child: Text(\n                    title,\n                    style: theme.textTheme.headlineSmall,\n                    textAlign: TextAlign.center,\n                  ),\n                ),\n                Expanded(\n                  child: GridView.builder(\n                    padding: const EdgeInsets.symmetric(\n                      horizontal: 24,\n                      vertical: 8,\n                    ),\n                    gridDelegate:\n                        const SliverGridDelegateWithFixedCrossAxisCount(\n                          crossAxisCount: 5,\n                          crossAxisSpacing: 16,\n                          mainAxisSpacing: 16,\n                        ),\n                    itemCount: presets.length,\n                    itemBuilder: (context, index) {\n                      final color = presets[index];\n                      final isSelected = selectedColor?.value == color.value;\n                      return GestureDetector(\n                        onTap: () {\n                          setState(() => selectedColor = color);\n                        },\n                        child: AnimatedContainer(\n                          duration: const Duration(milliseconds: 200),\n                          curve: Curves.easeOutCubic,\n                          decoration: BoxDecoration(\n                            color: color,\n                            shape: BoxShape.circle,\n                            border: isSelected\n                                ? Border.all(\n                                    color: colorScheme.onSurface,\n                                    width: 2.5,\n                                  )\n                                : Border.all(\n                                    color: colorScheme.outline.withValues(\n                                      alpha: 0.1,\n                                    ),\n                                    width: 1,\n                                  ),\n                            boxShadow: isSelected\n                                ? [\n                                    BoxShadow(\n                                      color: color.withValues(alpha: 0.4),\n                                      blurRadius: 8,\n                                      spreadRadius: 1,\n                                    ),\n                                  ]\n                                : null,\n                          ),\n                          child: isSelected\n                              ? Icon(\n                                  Icons.check,\n                                  size: 20,\n                                  color: color.computeLuminance() > 0.5\n                                      ? Colors.black\n                                      : Colors.white,\n                                )\n                              : null,\n                        ),\n                      );\n                    },\n                  ),\n                ),\n                Padding(\n                  padding: const EdgeInsets.fromLTRB(24, 8, 24, 24),\n                  child: Row(\n                    mainAxisAlignment: MainAxisAlignment.end,\n                    children: [\n                      if (onReset != null)\n                        TextButton(\n                          onPressed: () {\n                            onReset();\n                            Navigator.pop(context, null);\n                          },\n                          child: const Text(\"Reset\"),\n                        ),\n                      const Spacer(),\n                      TextButton(\n                        onPressed: () => Navigator.pop(context),\n                        child: const Text(\"Cancel\"),\n                      ),\n                      const SizedBox(width: 12),\n                      FilledButton(\n                        onPressed: () => Navigator.pop(context, selectedColor),\n                        child: const Text(\"Done\"),\n                      ),\n                    ],\n                  ),\n                ),\n              ],\n            );\n          },\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/core/utils/service_locator.dart",
    "content": "import 'package:get_it/get_it.dart';\n\nfinal sl = GetIt.I;\n"
  },
  {
    "path": "lib/core/widgets/expressive_app_bar.dart",
    "content": "import 'dart:ui';\n\nimport 'package:flutter/material.dart';\n\nclass ExpressiveAppBar extends StatelessWidget {\n  const ExpressiveAppBar({\n    super.key,\n    this.title,\n    this.child,\n    this.hasLeading = false,\n    this.actions,\n  });\n  final bool hasLeading;\n  final String? title;\n  final Widget? child;\n  final List<Widget>? actions;\n\n  @override\n  Widget build(BuildContext context) {\n    return SliverAppBar(\n      pinned: true,\n      expandedHeight: 120,\n      actions: actions,\n      flexibleSpace: hasLeading\n          ? LayoutBuilder(\n              builder: (context, constraints) {\n                final maxHeight = 120.0;\n                final t = (constraints.maxHeight / (maxHeight + 30)).clamp(\n                  0.0,\n                  1.0,\n                );\n                final paddingLeft = lerpDouble(100, 16, t)!;\n\n                return _ExpressiveFlexSpaceBar(\n                  paddingLeft: paddingLeft,\n                  title: title,\n                  child: child,\n                );\n              },\n            )\n          : _ExpressiveFlexSpaceBar(\n              paddingLeft: 16,\n              title: title,\n              child: child,\n            ),\n    );\n  }\n}\n\nclass _ExpressiveFlexSpaceBar extends StatelessWidget {\n  const _ExpressiveFlexSpaceBar({\n    required this.paddingLeft,\n    this.title,\n    this.child,\n  });\n\n  final double paddingLeft;\n  final String? title;\n  final Widget? child;\n\n  @override\n  Widget build(BuildContext context) {\n    return FlexibleSpaceBar(\n      titlePadding: EdgeInsets.only(left: paddingLeft, bottom: 12),\n      title:\n          child ??\n          Text(\n            title ?? \"\",\n            maxLines: 1,\n            overflow: TextOverflow.ellipsis,\n            style: Theme.of(\n              context,\n            ).textTheme.headlineSmall?.copyWith(fontWeight: .w600),\n          ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/core/widgets/expressive_list_group.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass ExpressiveListGroupScope extends InheritedWidget {\n  const ExpressiveListGroupScope({super.key, required super.child});\n\n  static ExpressiveListGroupScope? of(BuildContext context) {\n    return context\n        .dependOnInheritedWidgetOfExactType<ExpressiveListGroupScope>();\n  }\n\n  @override\n  bool updateShouldNotify(ExpressiveListGroupScope oldWidget) => false;\n}\n\nclass ExpressiveListGroup extends StatelessWidget {\n  final List<Widget> children;\n  final String? title;\n  final Widget? header;\n\n  const ExpressiveListGroup({\n    super.key,\n    required this.children,\n    this.title,\n    this.header,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final colorScheme = Theme.of(context).colorScheme;\n    final backgroundColor = colorScheme.surfaceContainerHigh;\n    final borderRadius = BorderRadius.circular(24);\n\n    Widget? headerWidget = header;\n\n    if (headerWidget == null && title != null) {\n      headerWidget = Padding(\n        padding: const EdgeInsets.only(left: 16, bottom: 8, top: 8),\n        child: Text(\n          title!,\n          style: Theme.of(context).textTheme.titleSmall?.copyWith(\n            color: colorScheme.primary,\n            fontWeight: FontWeight.bold,\n          ),\n        ),\n      );\n    }\n\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        if (headerWidget != null) headerWidget,\n\n        ExpressiveListGroupScope(\n          child: Container(\n            decoration: BoxDecoration(\n              color: backgroundColor.withValues(alpha: 0.5),\n              borderRadius: borderRadius,\n            ),\n            clipBehavior: Clip.hardEdge,\n            child: Column(children: _buildChildrenWithDividers(context)),\n          ),\n        ),\n      ],\n    );\n  }\n\n  List<Widget> _buildChildrenWithDividers(BuildContext context) {\n    final List<Widget> items = [];\n    final colorScheme = Theme.of(context).colorScheme;\n\n    for (int i = 0; i < children.length; i++) {\n      items.add(children[i]);\n\n      // Add Divider if not the last item\n      if (i < children.length - 1) {\n        items.add(\n          Divider(\n            height: 1,\n            thickness: 1,\n            indent: 76,\n            endIndent: 16,\n            color: colorScheme.outlineVariant.withValues(alpha: 0.4),\n          ),\n        );\n      }\n    }\n    return items;\n  }\n}\n"
  },
  {
    "path": "lib/core/widgets/expressive_list_tile.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:gyawun/core/widgets/expressive_list_group.dart';\n\nclass ExpressiveListTile extends StatelessWidget {\n  final Widget title;\n  final Widget? subtitle;\n  final Widget? leading;\n  final Widget? trailing;\n  final VoidCallback? onTap;\n  final VoidCallback? onLongPress;\n  final bool selected;\n  final bool enableFeedback;\n  final BorderRadiusGeometry? borderRadius;\n  final Color? fillColor;\n\n  const ExpressiveListTile({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.leading,\n    this.trailing,\n    this.onTap,\n    this.onLongPress,\n    this.selected = false,\n    this.enableFeedback = true,\n    this.borderRadius,\n    this.fillColor,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    final colorScheme = theme.colorScheme;\n\n    // Check if we are inside an ExpressiveListGroup\n    final isInGroup = ExpressiveListGroupScope.of(context) != null;\n\n    // Determine default values based on context (Standalone vs Grouped)\n    final effectiveBorderRadius =\n        borderRadius ??\n        (isInGroup ? BorderRadius.zero : BorderRadius.circular(24));\n\n    final effectiveFillColor =\n        fillColor ??\n        (isInGroup ? Colors.transparent : colorScheme.surfaceContainerHigh);\n\n    // Colors\n    final selectedColor = colorScheme.secondaryContainer;\n    final baseColor = selected ? selectedColor : effectiveFillColor;\n\n    // Overlay colors for InkWell (M3 specs)\n    final hoverColor = colorScheme.onSurface.withValues(alpha: 0.08);\n    final highlightColor = colorScheme.onSurface.withValues(alpha: 0.12);\n    final splashColor = colorScheme.onSurface.withValues(alpha: 0.12);\n\n    return AnimatedContainer(\n      duration: const Duration(milliseconds: 200),\n      curve: Curves.easeOut,\n      decoration: BoxDecoration(\n        color: baseColor,\n        borderRadius: effectiveBorderRadius,\n      ),\n      child: Material(\n        type: MaterialType.transparency,\n        child: InkWell(\n          onTap: onTap,\n          onLongPress: onLongPress,\n          borderRadius: effectiveBorderRadius.resolve(\n            Directionality.of(context),\n          ),\n          hoverColor: hoverColor,\n          highlightColor: highlightColor,\n          splashColor: splashColor,\n          enableFeedback: enableFeedback,\n          child: Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),\n            child: Row(\n              children: [\n                // Leading\n                if (leading != null) ...[\n                  IconTheme(\n                    data: IconThemeData(\n                      color: selected\n                          ? colorScheme.onSecondaryContainer\n                          : colorScheme.onSurfaceVariant,\n                      size: 24,\n                    ),\n                    child: leading!,\n                  ),\n                  const SizedBox(width: 16),\n                ],\n\n                // Text Content\n                Expanded(\n                  child: Column(\n                    crossAxisAlignment: CrossAxisAlignment.start,\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      DefaultTextStyle(\n                        style: theme.textTheme.bodyLarge!.copyWith(\n                          color: selected\n                              ? colorScheme.onSecondaryContainer\n                              : colorScheme.onSurface,\n                          fontWeight: FontWeight.w600,\n                        ),\n                        child: title,\n                      ),\n                      if (subtitle != null) ...[\n                        const SizedBox(height: 2),\n                        DefaultTextStyle(\n                          style: theme.textTheme.bodyMedium!.copyWith(\n                            color: selected\n                                ? colorScheme.onSecondaryContainer.withValues(\n                                    alpha: 0.8,\n                                  )\n                                : colorScheme.onSurfaceVariant,\n                            fontSize: 13,\n                            fontWeight: FontWeight.w600,\n                          ),\n                          maxLines: 1,\n                          overflow: TextOverflow.ellipsis,\n                          child: subtitle!,\n                        ),\n                      ],\n                    ],\n                  ),\n                ),\n\n                // Trailing\n                if (trailing != null) ...[\n                  const SizedBox(width: 16),\n                  IconTheme(\n                    data: IconThemeData(\n                      color: colorScheme.onSurfaceVariant,\n                      size: 24,\n                    ),\n                    child: trailing!,\n                  ),\n                ],\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/core/widgets/expressive_switch_list_tile.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\n\nclass ExpressiveSwitchListTile extends StatelessWidget {\n  final bool value;\n  final ValueChanged<bool>? onChanged;\n  final Widget title;\n  final Widget? subtitle;\n  final Widget? leading;\n  final bool enableFeedback;\n  final VoidCallback? onLongPress;\n  final bool selected;\n\n  const ExpressiveSwitchListTile({\n    super.key,\n    required this.value,\n    required this.onChanged,\n    required this.title,\n    this.subtitle,\n    this.leading,\n    this.enableFeedback = true,\n    this.onLongPress,\n    this.selected = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return ExpressiveListTile(\n      title: title,\n      subtitle: subtitle,\n      leading: leading,\n      onTap: onChanged != null ? () => onChanged!(!value) : null,\n      onLongPress: onLongPress,\n      enableFeedback: enableFeedback,\n      selected: selected,\n      trailing: Switch(value: value, onChanged: onChanged),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/core/widgets/internet_guard.dart",
    "content": "import 'dart:async';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:connectivity_plus/connectivity_plus.dart';\n\nimport 'package:gyawun/generated/l10n.dart';\nimport 'package:gyawun/themes/colors.dart';\nimport 'package:gyawun/utils/adaptive_widgets/buttons.dart';\nimport 'package:gyawun/utils/adaptive_widgets/icons.dart';\n\nclass InternetGuard extends StatefulWidget {\n  final Widget child;\n  final VoidCallback? onConnectivityRestored;\n\n  const InternetGuard({\n    super.key,\n    required this.child,\n    this.onConnectivityRestored,\n  });\n\n  @override\n  State<InternetGuard> createState() => _InternetGuardState();\n}\n\nclass _InternetGuardState extends State<InternetGuard> {\n  final Connectivity _connectivity = Connectivity();\n\n  bool _isOnline = true;\n  bool _wasOffline = false;\n  StreamSubscription? _subscription;\n\n  @override\n  void initState() {\n    super.initState();\n    _initConnectivity();\n  }\n\n  @override\n  void dispose() {\n    _subscription?.cancel();\n    super.dispose();\n  }\n\n  Future<void> _initConnectivity() async {\n    // Initial check\n    final result = await _connectivity.checkConnectivity();\n    _updateStatus(result);\n\n    // Listen for changes\n    _subscription =\n        _connectivity.onConnectivityChanged.listen(_updateStatus);\n  }\n\n  void _updateStatus(dynamic value) {\n    final bool online = !_isOffline(value);\n\n    if (online != _isOnline && mounted) {\n      setState(() {\n        if (!online) {\n          _wasOffline = true;\n        }\n        \n        if (online && _wasOffline) {\n          _wasOffline = false;\n          widget.onConnectivityRestored?.call();\n        }\n        \n        _isOnline = online;\n      });\n    }\n  }\n\n  bool _isOffline(dynamic value) {\n    if (value is ConnectivityResult) {\n      return value == ConnectivityResult.none;\n    }\n\n    if (value is List<ConnectivityResult>) {\n      return value.contains(ConnectivityResult.none);\n    }\n\n    return true;\n  }\n\n  Future<void> _retry() async {\n    final result = await _connectivity.checkConnectivity();\n    _updateStatus(result);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    if (_isOnline) {\n      return widget.child;\n    }\n\n    return Scaffold(\n      backgroundColor: Theme.of(context).scaffoldBackgroundColor,\n      body: Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            Icon(\n              AdaptiveIcons.wifi_off_rounded,\n              size: 80,\n              color: greyColor,\n            ),\n            const SizedBox(height: 20),\n            Text(\n              S.of(context).No_Internet_Connection,\n              textAlign: TextAlign.center,\n              style: const TextStyle(\n                fontSize: 18,\n                fontWeight: FontWeight.bold,\n              ),\n            ),\n            const SizedBox(height: 20),\n            AdaptiveFilledButton(\n              onPressed: () => context.go('/library/downloads'),\n              child: Text(S.of(context).Go_To_Downloads),\n            ),\n            const SizedBox(height: 20),\n            OutlinedButton.icon(\n              icon: const Icon(Icons.refresh),\n              label: Text(S.of(context).Retry),\n              onPressed: _retry,\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}"
  },
  {
    "path": "lib/core/widgets/library_tile.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass LibraryTile extends StatelessWidget {\n  const LibraryTile({\n    this.title,\n    this.leading,\n    this.subtitle,\n    this.trailing,\n    this.onTap,\n    this.onLongPress,\n    this.isFirst = true,\n    this.isLast = true,\n    super.key,\n  });\n  final Widget? title;\n  final Widget? leading;\n  final Widget? subtitle;\n  final Widget? trailing;\n  final void Function()? onTap;\n  final void Function()? onLongPress;\n  final bool isFirst;\n  final bool isLast;\n\n  @override\n  Widget build(BuildContext context) {\n    return Material(\n      color: Theme.of(context).colorScheme.surfaceContainer,\n      borderRadius: BorderRadiusGeometry.only(\n        topLeft: Radius.circular(isFirst ? 20 : 4),\n        topRight: Radius.circular(isFirst ? 20 : 4),\n        bottomLeft: Radius.circular(isLast ? 20 : 4),\n        bottomRight: Radius.circular(isLast ? 20 : 4),\n      ),\n      child: InkWell(\n        onTap: onTap,\n        onLongPress: onLongPress,\n        child: Padding(\n          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),\n          child: Row(\n            children: [\n              if (leading != null) ...[leading!, const SizedBox(width: 16)],\n              Expanded(\n                child: Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    if (title != null)\n                      DefaultTextStyle(\n                        style: Theme.of(context).textTheme.bodyLarge!,\n                        child: title!,\n                      ),\n                    if (subtitle != null) ...[\n                      const SizedBox(height: 4),\n                      DefaultTextStyle(\n                        style: Theme.of(context).textTheme.bodyMedium!.copyWith(\n                          color: Theme.of(context).textTheme.bodySmall!.color,\n                        ),\n                        child: subtitle!,\n                      ),\n                    ],\n                  ],\n                ),\n              ),\n              if (trailing != null) ...[const SizedBox(width: 16), trailing!],\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/core/widgets/rounded_polygon_icon.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:m3e_collection/m3e_collection.dart';\n\nclass RoundedPolygonIcon extends StatelessWidget {\n  final RoundedPolygon polygon;\n  final Color? color;\n  final double size;\n\n  const RoundedPolygonIcon({\n    super.key,\n    required this.polygon,\n    this.color,\n    this.size = 24.0,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final iconColor = color ?? Theme.of(context).iconTheme.color ?? Colors.black;\n    \n    return CustomPaint(\n      size: Size(size, size),\n      painter: _RoundedPolygonPainter(\n        polygon: polygon,\n        color: iconColor,\n      ),\n    );\n  }\n}\n\nclass _RoundedPolygonPainter extends CustomPainter {\n  final RoundedPolygon polygon;\n  final Color color;\n\n  _RoundedPolygonPainter({\n    required this.polygon,\n    required this.color,\n  });\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    final paint = Paint()\n      ..color = color\n      ..style = PaintingStyle.fill;\n\n    final path = polygon.toPath();\n    \n    // Get the bounds of the polygon\n    final bounds = path.getBounds();\n    \n    // Scale and center the polygon to fit the icon size\n    final scaleX = size.width / bounds.width;\n    final scaleY = size.height / bounds.height;\n    final scale = scaleX < scaleY ? scaleX : scaleY;\n    \n    canvas.save();\n    canvas.translate(\n      size.width / 2 - bounds.center.dx * scale,\n      size.height / 2 - bounds.center.dy * scale,\n    );\n    canvas.scale(scale);\n    canvas.drawPath(path, paint);\n    canvas.restore();\n  }\n\n  @override\n  bool shouldRepaint(_RoundedPolygonPainter oldDelegate) {\n    return oldDelegate.polygon != polygon || oldDelegate.color != color;\n  }\n}"
  },
  {
    "path": "lib/core/widgets/section_item.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/core/widgets/sections/section_multi_column.dart';\nimport 'package:gyawun/core/widgets/sections/section_row.dart';\nimport 'package:gyawun/core/widgets/tiles/section_list_tile.dart';\nimport 'package:m3e_collection/m3e_collection.dart';\nimport 'package:yt_music/ytmusic.dart';\n\nimport '../../generated/l10n.dart';\nimport '../../services/bottom_message.dart';\nimport '../../utils/adaptive_widgets/adaptive_widgets.dart';\nimport '../../services/media_player.dart';\n\nclass SectionItem extends StatefulWidget {\n  const SectionItem({required this.section, this.isMore = false, super.key});\n  final Map section;\n  final bool isMore;\n\n  @override\n  State<SectionItem> createState() => _SectionItemState();\n}\n\nclass _SectionItemState extends State<SectionItem> {\n  final ScrollController horizontalScrollController = ScrollController();\n  PageController horizontalPageController = PageController();\n  bool loadingMore = false;\n\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    horizontalPageController.dispose();\n    horizontalScrollController.dispose();\n    super.dispose();\n  }\n\n  void loadMoreItems() {\n    if (widget.section['continuation'] != null) {\n      setState(() {\n        loadingMore = true;\n      });\n      GetIt.I<YTMusic>()\n          .getMoreItems(continuation: widget.section['continuation'])\n          .then((value) {\n            setState(() {\n              widget.section['contents'].addAll(value['items']);\n              widget.section['continuation'] = value['continuation'];\n              loadingMore = false;\n            });\n          });\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    horizontalPageController = PageController(\n      viewportFraction: 350 / MediaQuery.of(context).size.width,\n    );\n    return widget.section['contents'].isEmpty\n        ? const SizedBox()\n        : Column(\n            children: [\n              if (widget.section['title'] != null)\n                SectionHeader(\n                  title: widget.section['title'],\n                  trailing: widget.section['trailing'],\n                  contents: widget.section['contents'],\n                ),\n              if (widget.section['viewType'] == 'COLUMN' && !widget.isMore)\n                SectionMultiColumn(items: widget.section['contents'])\n              else if (widget.section['viewType'] == 'SINGLE_COLUMN' ||\n                  widget.isMore)\n                SingleColumnList(songs: widget.section['contents'])\n              else\n                SectionRow(items: widget.section['contents']),\n              if (loadingMore) const ExpressiveLoadingIndicator(),\n              if (widget.section['continuation'] != null && !loadingMore)\n                AdaptiveButton(\n                  onPressed: loadMoreItems,\n                  child: const Text(\"Load More\"),\n                ),\n            ],\n          );\n  }\n}\n\nclass SectionHeader extends StatelessWidget {\n  const SectionHeader({\n    super.key,\n    required this.title,\n    required this.trailing,\n    required this.contents,\n  });\n  final String title;\n  final Map? trailing;\n  final List contents;\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),\n      child: Row(\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        children: [\n          Expanded(\n            child: Text(\n              title,\n              style: Theme.of(context).textTheme.headlineSmall?.copyWith(\n                color: Theme.of(context).colorScheme.primary,\n                fontWeight: FontWeight.w700,\n                fontSize: 18,\n              ),\n            ),\n          ),\n          if (trailing != null)\n            TextButton.icon(\n              iconAlignment: IconAlignment.end,\n              label: Text(trailing!['text']),\n              icon: const Icon(FluentIcons.play_24_filled),\n\n              onPressed: () async {\n                if (trailing!['playable'] == false &&\n                    trailing!['endpoint'] != null) {\n                  context.push(\n                    '/browse',\n                    extra: {'endpoint': trailing!['endpoint'], 'isMore': true},\n                  );\n                } else {\n                  BottomMessage.showText(\n                    context,\n                    S.of(context).Songs_Will_Start_Playing_Soon,\n                  );\n                  if (trailing!['endpoint'] != null) {\n                    await GetIt.I<MediaPlayer>().startPlaylistSongs(\n                      trailing!['endpoint'],\n                    );\n                  } else {\n                    await GetIt.I<MediaPlayer>().playAll(contents);\n                  }\n                }\n              },\n            ),\n        ],\n      ),\n    );\n  }\n}\n\nclass SingleColumnList extends StatelessWidget {\n  const SingleColumnList({required this.songs, super.key});\n  final List songs;\n  @override\n  Widget build(BuildContext context) {\n    return ListView.builder(\n      shrinkWrap: true,\n      padding: EdgeInsets.zero,\n      physics: const NeverScrollableScrollPhysics(),\n      itemCount: songs.length,\n      itemBuilder: (context, index) {\n        return Padding(\n          padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 4),\n          child: SectionListTile(\n            item: songs[index],\n            isFirst: index == 0,\n            isLast: index == (songs.length - 1),\n          ),\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/core/widgets/sections/section_multi_column.dart",
    "content": "import 'dart:math';\n\nimport 'package:expandable_page_view/expandable_page_view.dart';\nimport 'package:flutter/material.dart';\nimport 'package:gyawun/core/widgets/tiles/section_list_tile.dart';\n\nclass SectionMultiColumn extends StatefulWidget {\n  const SectionMultiColumn({super.key, required this.items, this.maxItem});\n\n  final List items;\n  final int? maxItem;\n\n  @override\n  State<SectionMultiColumn> createState() => _SectionMultiColumnState();\n}\n\nclass _SectionMultiColumnState extends State<SectionMultiColumn> {\n  late PageController controller;\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    controller = PageController(\n      viewportFraction:\n          min(450, MediaQuery.sizeOf(context).width - 50) / MediaQuery.sizeOf(context).width,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final num = widget.maxItem ?? (widget.items.length <= 5 ? widget.items.length : 4);\n    var pages = widget.items.length ~/ num;\n    pages = (pages * num) < widget.items.length ? pages + 1 : pages;\n    return ExpandablePageView.builder(\n        controller: controller,\n        padEnds: false,\n        itemCount: pages,\n        itemBuilder: (context, index) {\n          int start = index * num;\n          int end = start + num;\n          return Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 8),\n            child: Column(\n              children: widget.items.sublist(start, min(end, widget.items.length)).indexed.map((\n                entry,\n              ) {\n                return Padding(\n                  padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 4),\n                  child: SectionListTile(\n                    item: entry.$2,\n                    isFirst: entry.$1 == 0,\n                    isLast: entry.$1 == (num - 1) || entry.$2 == widget.items.last,\n                  ),\n                );\n              }).toList(),\n            ),\n          );\n        },\n      );\n    \n  }\n}"
  },
  {
    "path": "lib/core/widgets/sections/section_row.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:gyawun/core/widgets/tiles/section_row_tile.dart';\n\nclass SectionRow extends StatelessWidget {\n  const SectionRow({super.key,required this.items});\n  final List items;\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n        // height: context.isWideScreen ? 270 : 216,\n        height: 216,\n        child: ListView.separated(\n          addAutomaticKeepAlives: false,\n          padding: const EdgeInsets.symmetric(horizontal: 8),\n          scrollDirection: Axis.horizontal,\n          itemCount: items.length,\n          separatorBuilder: (context, index) => const SizedBox(width: 4),\n          itemBuilder: (context, index) {\n            final item = items[index];\n\n            return SectionRowTile(item: item);\n          },\n        ),\n      );\n  }\n}"
  },
  {
    "path": "lib/core/widgets/song_tile.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/core/widgets/library_tile.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/utils/bottom_modals.dart';\n\nimport '../../utils/song_thumbnail.dart';\n\nclass SongTile extends StatelessWidget {\n  const SongTile({\n    required this.song,\n    this.playlistId,\n    this.onTap,\n    this.onLongPress,\n    this.icon,\n    this.onIconPress,\n    this.isFirst = true,\n    this.isLast = true,\n    super.key,\n  });\n  final Map song;\n  final String? playlistId;\n  final Function? onTap;\n  final Function? onLongPress;\n  final IconData? icon;\n  final Function? onIconPress;\n  final bool isFirst;\n  final bool isLast;\n\n  void _onTap(BuildContext context, Map song) async {\n    if (song['endpoint'] != null && song['videoId'] == null) {\n      context.push('/browse', extra: {'endpoint': song['endpoint']});\n    } else {\n      await GetIt.I<MediaPlayer>().playSong(Map.from(song));\n    }\n  }\n\n  void _onLongPress(BuildContext context, Map song) {\n    if (song['videoId'] != null) {\n      Modals.showSongBottomModal(context, song);\n    }\n  }\n\n  void _onIconPress(BuildContext context, Map song) {\n    if (song['videoId'] != null) {\n      Modals.showSongBottomModal(context, song);\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    double height =\n        (song['aspectRatio'] != null ? 50 / song['aspectRatio'] : 50)\n            .toDouble();\n    return LibraryTile(\n      onTap: () => (onTap ?? _onTap)(context, song),\n      onLongPress: () => (onLongPress ?? _onLongPress)(context, song),\n      title: Text(\n        song['title'] ?? \"\",\n        maxLines: 1,\n        style: Theme.of(\n          context,\n        ).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w700),\n      ),\n      leading: ClipRRect(\n        borderRadius: BorderRadius.circular(8),\n        child: SongThumbnail(\n          song: song,\n          height: height,\n          width: 50,\n          fit: BoxFit.cover,\n        ),\n      ),\n      subtitle: Row(\n        crossAxisAlignment: CrossAxisAlignment.center,\n        children: [\n          if (song['explicit'] == true)\n            Padding(\n              padding: const EdgeInsets.only(right: 2),\n              child: Icon(\n                Icons.explicit,\n                size: 18,\n                color: Colors.grey.withValues(alpha: 0.9),\n              ),\n            ),\n          Expanded(\n            child: Text(\n              song['subtitle'] ??\n                  song['artists']?.map((e) => e['name'])?.join(',') ??\n                  '',\n              maxLines: 1,\n              style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n                fontWeight: FontWeight.w600,\n                color: Theme.of(context).colorScheme.onSurface.withAlpha(150),\n              ),\n              overflow: TextOverflow.ellipsis,\n            ),\n          ),\n        ],\n      ),\n      trailing: song['videoId'] == null\n          ? null\n          : IconButton.filledTonal(\n              onPressed: () => (onIconPress ?? _onIconPress)(context, song),\n              icon: Icon(icon ?? FluentIcons.more_vertical_24_filled),\n            ),\n      isFirst: isFirst,\n      isLast: isLast,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/core/widgets/tiles/section_list_tile.dart",
    "content": "import 'package:cached_network_image/cached_network_image.dart';\nimport 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/utils/bottom_modals.dart';\n\nclass SectionListTile extends StatelessWidget {\n  const SectionListTile({\n    super.key,\n    required this.item,\n    this.onTap,\n    this.items,\n    this.isFirst = false,\n    this.isLast = false,\n  });\n  final Map item;\n  final List<Map>? items;\n  final void Function()? onTap;\n  final bool isFirst;\n  final bool isLast;\n\n  @override\n  Widget build(BuildContext context) {\n    final pixelRatio = MediaQuery.devicePixelRatioOf(context);\n    final thumbnail = item['thumbnails'][0];\n\n    return Material(\n      color: Colors.transparent,\n      elevation: 0,\n      shadowColor: Colors.transparent,\n      surfaceTintColor: Colors.transparent,\n      child: ListTile(\n        onTap: () async {\n          if (item['endpoint'] != null && item['videoId'] == null) {\n            context.push('/browse', extra: {'endpoint': item['endpoint']});\n          } else {\n            await GetIt.I<MediaPlayer>().playSong(Map.from(item));\n          }\n        },\n        onLongPress: () {\n          if (item['videoId'] != null) {\n            Modals.showSongBottomModal(context, item);\n          }\n        },\n        enableFeedback: true,\n        shape: RoundedRectangleBorder(\n          borderRadius: BorderRadiusGeometry.only(\n            topLeft: Radius.circular(isFirst ? 20 : 4),\n            topRight: Radius.circular(isFirst ? 20 : 4),\n            bottomLeft: Radius.circular(isLast ? 20 : 4),\n            bottomRight: Radius.circular(isLast ? 20 : 4),\n          ),\n        ),\n        tileColor: Theme.of(context).colorScheme.surfaceContainer,\n        contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n        leading: thumbnail?['url'] == null\n            ? null\n            : SizedBox(\n                width: 50,\n                height: 50,\n                child: Container(\n                  decoration: BoxDecoration(\n                    borderRadius: BorderRadius.circular(\n                      item['type'] == 'ARTIST'\n                          ? ((50 * pixelRatio).round() / 2)\n                          : 8,\n                    ),\n                    image: DecorationImage(\n                      image: CachedNetworkImageProvider(\n                        thumbnail!['url'],\n                        maxHeight: (50 * pixelRatio).round(),\n                        maxWidth: (50 * pixelRatio).round(),\n                      ),\n                      fit: BoxFit.fitWidth,\n                    ),\n                  ),\n                ),\n              ),\n        title: Text(\n          item['title'],\n          maxLines: 1,\n          style: Theme.of(\n            context,\n          ).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w700),\n        ),\n        subtitle: Text(\n          item['subtitle'] ??\n              item['artists']?.map((e) => e['name'])?.join(',') ??\n              '',\n          maxLines: 1,\n          style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n            fontWeight: FontWeight.w600,\n            color: Theme.of(context).colorScheme.onSurface.withAlpha(150),\n          ),\n          overflow: TextOverflow.ellipsis,\n        ),\n        trailing: item['videoId'] != null\n            ? IconButton(\n                onPressed: () {\n                  Modals.showSongBottomModal(context, item);\n                },\n                icon: const Icon(Icons.more_vert_rounded),\n              )\n            : Icon(FluentIcons.chevron_right_24_filled),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/core/widgets/tiles/section_row_tile.dart",
    "content": "import 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/utils/bottom_modals.dart';\n\nclass SectionRowTile extends StatelessWidget {\n  const SectionRowTile({super.key, required this.item});\n\n  final Map item;\n\n  @override\n  Widget build(BuildContext context) {\n    final pixelRatio = MediaQuery.devicePixelRatioOf(context);\n    final imageHeight = 150;\n    final isHorizontal =\n        item['aspectRatio'] != null && item['aspectRatio'] != 1;\n    final imageWidth = (isHorizontal ? imageHeight * (16 / 9) : imageHeight)\n        .toInt();\n    final thumbnail = (item['thumbnails'] as List).length > 2\n        ? item['thumbnails'][1]['url']\n        : item['thumbnails'][0]['url'];\n    return Material(\n      color: Colors.transparent,\n      elevation: 0,\n      shadowColor: Colors.transparent,\n      surfaceTintColor: Colors.transparent,\n      child: InkWell(\n        borderRadius: BorderRadius.circular(8),\n        enableFeedback: true,\n        onTap: () async {\n          if (item['endpoint'] != null && item['videoId'] == null) {\n            context.push('/browse', extra: {'endpoint': item['endpoint']});\n          } else {\n            await GetIt.I<MediaPlayer>().playSong(Map.from(item));\n          }\n        },\n        onLongPress: () {\n          if (item['videoId'] != null) {\n            Modals.showSongBottomModal(context, item);\n          } else if (item['playlistId'] != null) {\n            Modals.showPlaylistBottomModal(context, item);\n          }\n        },\n        onSecondaryTap: () {\n          if (item['videoId'] != null) {\n            Modals.showSongBottomModal(context, item);\n          } else if (item['playlistId'] != null) {\n            Modals.showPlaylistBottomModal(context, item);\n          }\n        },\n        child: RepaintBoundary(\n          child: SizedBox(\n            height: 216,\n            width: imageWidth.toDouble() + 16,\n            child: Padding(\n              padding: const EdgeInsets.all(8.0),\n              child: Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: [\n                  Ink(\n                    height: imageHeight.toDouble(),\n                    width: imageWidth.toDouble(),\n                    decoration: BoxDecoration(\n                      borderRadius: BorderRadius.circular(\n                        (item['type'] == 'ARTIST')\n                            ? (imageWidth * pixelRatio)\n                            : 8,\n                      ),\n                      image: DecorationImage(\n                        image: CachedNetworkImageProvider(\n                          thumbnail,\n                          maxHeight: (imageHeight * pixelRatio).round(),\n                          maxWidth: (imageWidth * pixelRatio).round(),\n                        ),\n                        fit: BoxFit.fill,\n                      ),\n                    ),\n                  ),\n                  Text(\n                    item['title'],\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: Theme.of(context).textTheme.bodyLarge?.copyWith(\n                      fontWeight: FontWeight.w700,\n                    ),\n                  ),\n                  if (item['subtitle'] != null && item['subtitle']!.isNotEmpty)\n                    Text(\n                      item['subtitle']!,\n                      maxLines: 1,\n                      overflow: TextOverflow.ellipsis,\n                      style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n                        fontWeight: FontWeight.w600,\n                        color: Theme.of(\n                          context,\n                        ).colorScheme.onSurface.withAlpha(150),\n                      ),\n                    ),\n                ],\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_all.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that looks up messages for specific locales by\n// delegating to the appropriate library.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:implementation_imports, file_names, unnecessary_new\n// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering\n// ignore_for_file:argument_type_not_assignable, invalid_assignment\n// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases\n// ignore_for_file:comment_references\n\nimport 'dart:async';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\nimport 'package:intl/src/intl_helpers.dart';\n\nimport 'messages_en.dart' as messages_en;\nimport 'messages_es.dart' as messages_es;\nimport 'messages_fr.dart' as messages_fr;\nimport 'messages_hi.dart' as messages_hi;\nimport 'messages_it.dart' as messages_it;\nimport 'messages_tr.dart' as messages_tr;\nimport 'messages_ur.dart' as messages_ur;\n\ntypedef Future<dynamic> LibraryLoader();\nMap<String, LibraryLoader> _deferredLibraries = {\n  'en': () => new SynchronousFuture(null),\n  'es': () => new SynchronousFuture(null),\n  'fr': () => new SynchronousFuture(null),\n  'hi': () => new SynchronousFuture(null),\n  'it': () => new SynchronousFuture(null),\n  'tr': () => new SynchronousFuture(null),\n  'ur': () => new SynchronousFuture(null),\n};\n\nMessageLookupByLibrary? _findExact(String localeName) {\n  switch (localeName) {\n    case 'en':\n      return messages_en.messages;\n    case 'es':\n      return messages_es.messages;\n    case 'fr':\n      return messages_fr.messages;\n    case 'hi':\n      return messages_hi.messages;\n    case 'it':\n      return messages_it.messages;\n    case 'tr':\n      return messages_tr.messages;\n    case 'ur':\n      return messages_ur.messages;\n    default:\n      return null;\n  }\n}\n\n/// User programs should call this before using [localeName] for messages.\nFuture<bool> initializeMessages(String localeName) {\n  var availableLocale = Intl.verifiedLocale(\n    localeName,\n    (locale) => _deferredLibraries[locale] != null,\n    onFailure: (_) => null,\n  );\n  if (availableLocale == null) {\n    return new SynchronousFuture(false);\n  }\n  var lib = _deferredLibraries[availableLocale];\n  lib == null ? new SynchronousFuture(false) : lib();\n  initializeInternalMessageLookup(() => new CompositeMessageLookup());\n  messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);\n  return new SynchronousFuture(true);\n}\n\nbool _messagesExistFor(String locale) {\n  try {\n    return _findExact(locale) != null;\n  } catch (e) {\n    return false;\n  }\n}\n\nMessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {\n  var actualLocale = Intl.verifiedLocale(\n    locale,\n    _messagesExistFor,\n    onFailure: (_) => null,\n  );\n  if (actualLocale == null) return null;\n  return _findExact(actualLocale);\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_en.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a en locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'en';\n\n  static String m0(count) => \"Queued (${count})\";\n\n  static String m1(count) =>\n      \"${Intl.plural(count, zero: 'No Songs', one: '1 Song', other: '${count} Songs')}\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"About\": MessageLookupByLibrary.simpleMessage(\"About\"),\n    \"Add_To_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Add To Favourites\",\n    ),\n    \"Add_To_Library\": MessageLookupByLibrary.simpleMessage(\"Add To Library\"),\n    \"Add_To_Playlist\": MessageLookupByLibrary.simpleMessage(\"Add To Playlist\"),\n    \"Add_To_Queue\": MessageLookupByLibrary.simpleMessage(\"Add To Queue\"),\n    \"Album\": MessageLookupByLibrary.simpleMessage(\"Album\"),\n    \"Albums\": MessageLookupByLibrary.simpleMessage(\"Albums\"),\n    \"App_Folder\": MessageLookupByLibrary.simpleMessage(\"App Folder\"),\n    \"Appearence\": MessageLookupByLibrary.simpleMessage(\"Appearence\"),\n    \"Artists\": MessageLookupByLibrary.simpleMessage(\"Artists\"),\n    \"Audio_And_Playback\": MessageLookupByLibrary.simpleMessage(\n      \"Audio and Playback\",\n    ),\n    \"Autofetch_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Autoplay Similar Songs\",\n    ),\n    \"Backup\": MessageLookupByLibrary.simpleMessage(\"Backup\"),\n    \"Backup_And_Restore\": MessageLookupByLibrary.simpleMessage(\n      \"Backup and Restore\",\n    ),\n    \"Backup_Failed\": MessageLookupByLibrary.simpleMessage(\n      \"Failed to back up Data\",\n    ),\n    \"Backup_Success\": MessageLookupByLibrary.simpleMessage(\n      \"Backed up successfully at\",\n    ),\n    \"Battery_Optimisation_message\": MessageLookupByLibrary.simpleMessage(\n      \"Click here disable battery optimisation for Gyawun to work properly\",\n    ),\n    \"Battery_Optimisation_title\": MessageLookupByLibrary.simpleMessage(\n      \"Battery Optimisation Detected\",\n    ),\n    \"Bug_Report\": MessageLookupByLibrary.simpleMessage(\"Bug Report\"),\n    \"Buy_Me_A_Coffee\": MessageLookupByLibrary.simpleMessage(\"Buy me a Coffee\"),\n    \"Cancel\": MessageLookupByLibrary.simpleMessage(\"Cancel\"),\n    \"Check_For_Update\": MessageLookupByLibrary.simpleMessage(\n      \"Check for Update\",\n    ),\n    \"Confirm\": MessageLookupByLibrary.simpleMessage(\"Confirm\"),\n    \"Confirm_Delete_All_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Are you sure you want to delete them?\",\n    ),\n    \"Content\": MessageLookupByLibrary.simpleMessage(\"Content\"),\n    \"Contributors\": MessageLookupByLibrary.simpleMessage(\"Contributors\"),\n    \"Copied_To_Clipboard\": MessageLookupByLibrary.simpleMessage(\n      \"Copied to Clipboard\",\n    ),\n    \"Country\": MessageLookupByLibrary.simpleMessage(\"Country\"),\n    \"Create\": MessageLookupByLibrary.simpleMessage(\"Create\"),\n    \"Create_Playlist\": MessageLookupByLibrary.simpleMessage(\"Create Playlist\"),\n    \"DOwnload_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"Download Quality\",\n    ),\n    \"Delete_All_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Delete All Songs\",\n    ),\n    \"Delete_Item_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Are you sure you want to delete this item?\",\n    ),\n    \"Delete_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Delete Playback History\",\n    ),\n    \"Delete_Playback_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"Are you sure you want to delete Playback History.\",\n        ),\n    \"Delete_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Delete Search History\",\n    ),\n    \"Delete_Search_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"Are you sure you want to delete Search History.\",\n        ),\n    \"Deleting_Songs\": MessageLookupByLibrary.simpleMessage(\"Deleting Songs...\"),\n    \"Developer\": MessageLookupByLibrary.simpleMessage(\"Developer\"),\n    \"Donate\": MessageLookupByLibrary.simpleMessage(\"Donate\"),\n    \"Donate_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Support the development of Gyawun\",\n    ),\n    \"Done\": MessageLookupByLibrary.simpleMessage(\"Done\"),\n    \"Download\": MessageLookupByLibrary.simpleMessage(\"Download\"),\n    \"Download_Started\": MessageLookupByLibrary.simpleMessage(\n      \"Download started...\",\n    ),\n    \"Downloading\": MessageLookupByLibrary.simpleMessage(\"Downloading\"),\n    \"Downloads\": MessageLookupByLibrary.simpleMessage(\"Downloads\"),\n    \"Dynamic_Colors\": MessageLookupByLibrary.simpleMessage(\"Dynamic Colors\"),\n    \"Edit\": MessageLookupByLibrary.simpleMessage(\"Edit\"),\n    \"Edit_Playlist\": MessageLookupByLibrary.simpleMessage(\"Edit Playlist\"),\n    \"Enable_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Enable Equalizer\",\n    ),\n    \"Enable_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Enable Playback History\",\n    ),\n    \"Enable_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Enable Search History\",\n    ),\n    \"Enter_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Enter Visitor Id\",\n    ),\n    \"Equalizer\": MessageLookupByLibrary.simpleMessage(\"Equalizer\"),\n    \"Favourites\": MessageLookupByLibrary.simpleMessage(\"Favourites\"),\n    \"Feature_Request\": MessageLookupByLibrary.simpleMessage(\"Feature Request\"),\n    \"File_Not_Found\": MessageLookupByLibrary.simpleMessage(\"File not found\"),\n    \"Go_To_Downloads\": MessageLookupByLibrary.simpleMessage(\"Go to Downloads\"),\n    \"Google_Account\": MessageLookupByLibrary.simpleMessage(\"Google Account\"),\n    \"Gyawun\": MessageLookupByLibrary.simpleMessage(\"Gyawun\"),\n    \"High\": MessageLookupByLibrary.simpleMessage(\"High\"),\n    \"History\": MessageLookupByLibrary.simpleMessage(\"History\"),\n    \"Home\": MessageLookupByLibrary.simpleMessage(\"Home\"),\n    \"Import\": MessageLookupByLibrary.simpleMessage(\"Import\"),\n    \"Import_Playlist\": MessageLookupByLibrary.simpleMessage(\"Import Playlist\"),\n    \"In_Progress\": MessageLookupByLibrary.simpleMessage(\"In Progress\"),\n    \"Jhelum_Corp\": MessageLookupByLibrary.simpleMessage(\"Jhelum Corp\"),\n    \"Language\": MessageLookupByLibrary.simpleMessage(\"Language\"),\n    \"Loudness_And_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Loudness And Equalizer\",\n    ),\n    \"Loudness_Enhancer\": MessageLookupByLibrary.simpleMessage(\n      \"Loudness Enhancer\",\n    ),\n    \"Low\": MessageLookupByLibrary.simpleMessage(\"Low\"),\n    \"Made_In_Kashmir\": MessageLookupByLibrary.simpleMessage(\"Made in Kashmir\"),\n    \"Name\": MessageLookupByLibrary.simpleMessage(\"Name\"),\n    \"Next_Up\": MessageLookupByLibrary.simpleMessage(\"Next Up\"),\n    \"No\": MessageLookupByLibrary.simpleMessage(\"No\"),\n    \"No_Internet_Connection\": MessageLookupByLibrary.simpleMessage(\n      \"No Internet Connection\",\n    ),\n    \"No_Offline_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"No offline songs available\",\n    ),\n    \"Organisation\": MessageLookupByLibrary.simpleMessage(\"Organisation\"),\n    \"Other_Results\": MessageLookupByLibrary.simpleMessage(\"Other Results\"),\n    \"Pay_With_UPI\": MessageLookupByLibrary.simpleMessage(\"Pay with UPI\"),\n    \"Payment_Methods\": MessageLookupByLibrary.simpleMessage(\"Payment Methods\"),\n    \"Personalised_Content\": MessageLookupByLibrary.simpleMessage(\n      \"Personalised Content\",\n    ),\n    \"Play_All\": MessageLookupByLibrary.simpleMessage(\"Play All\"),\n    \"Play_Next\": MessageLookupByLibrary.simpleMessage(\"Play Next\"),\n    \"Playback_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Playback History Deleted\",\n    ),\n    \"Playlist_Name\": MessageLookupByLibrary.simpleMessage(\"Playlist Name\"),\n    \"Playlist_Not_Available\": MessageLookupByLibrary.simpleMessage(\n      \"Playlist not available\",\n    ),\n    \"Playlists\": MessageLookupByLibrary.simpleMessage(\"Playlists\"),\n    \"Progress\": MessageLookupByLibrary.simpleMessage(\"Progress\"),\n    \"Queued\": MessageLookupByLibrary.simpleMessage(\"Queued\"),\n    \"Queued_Count\": m0,\n    \"Remove\": MessageLookupByLibrary.simpleMessage(\"Remove\"),\n    \"Remove_All_History_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Are you sure you want to clear all history?\",\n    ),\n    \"Remove_From_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Remove From Favourites\",\n    ),\n    \"Remove_From_Library\": MessageLookupByLibrary.simpleMessage(\n      \"Remove From Library\",\n    ),\n    \"Remove_From_YTMusic_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Are you sure you want to remove it from YTMusic?\",\n    ),\n    \"Remove_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Are you sure you want to remove it?\",\n    ),\n    \"Rename\": MessageLookupByLibrary.simpleMessage(\"Rename\"),\n    \"Rename_Playlist\": MessageLookupByLibrary.simpleMessage(\"Rename Playlist\"),\n    \"Reset_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Reset Visitor Id\",\n    ),\n    \"Restore\": MessageLookupByLibrary.simpleMessage(\"Restore\"),\n    \"Restore_Failed\": MessageLookupByLibrary.simpleMessage(\n      \"Failed to restore Data\",\n    ),\n    \"Restore_Missing_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Restore Missing Songs\",\n    ),\n    \"Restore_Success\": MessageLookupByLibrary.simpleMessage(\n      \"Data successfully restored\",\n    ),\n    \"Restoring_Missing_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Restoring Missing Songs...\",\n    ),\n    \"Retry\": MessageLookupByLibrary.simpleMessage(\"Retry\"),\n    \"Rotate_Device\": MessageLookupByLibrary.simpleMessage(\n      \"Rotate your device to type.\",\n    ),\n    \"Save\": MessageLookupByLibrary.simpleMessage(\"Save\"),\n    \"Saved\": MessageLookupByLibrary.simpleMessage(\"Saved\"),\n    \"Search_Gyawun\": MessageLookupByLibrary.simpleMessage(\"Search Gyawun\"),\n    \"Search_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Search History Deleted\",\n    ),\n    \"Search_Settings\": MessageLookupByLibrary.simpleMessage(\"Search Settings\"),\n    \"Select_Backup\": MessageLookupByLibrary.simpleMessage(\"Select Backup\"),\n    \"Select_Playlist_Icon\": MessageLookupByLibrary.simpleMessage(\n      \"Select Playlist Icon\",\n    ),\n    \"Settings\": MessageLookupByLibrary.simpleMessage(\"Settings\"),\n    \"Share\": MessageLookupByLibrary.simpleMessage(\"Share\"),\n    \"Sheikh_Haziq\": MessageLookupByLibrary.simpleMessage(\"Sheikh Haziq\"),\n    \"Show_Less\": MessageLookupByLibrary.simpleMessage(\"Show Less\"),\n    \"Show_More\": MessageLookupByLibrary.simpleMessage(\"Show More\"),\n    \"Shuffle\": MessageLookupByLibrary.simpleMessage(\"Shuffle\"),\n    \"Skip_Silence\": MessageLookupByLibrary.simpleMessage(\"Skip Silence\"),\n    \"Sleep_Timer\": MessageLookupByLibrary.simpleMessage(\"Sleep Timer\"),\n    \"Songs\": MessageLookupByLibrary.simpleMessage(\"Songs\"),\n    \"Songs_Will_Start_Playing_Soon\": MessageLookupByLibrary.simpleMessage(\n      \"Songs will start playing soon.\",\n    ),\n    \"Source_Code\": MessageLookupByLibrary.simpleMessage(\"Source Code\"),\n    \"Start_Radio\": MessageLookupByLibrary.simpleMessage(\"Start Radio\"),\n    \"Streaming_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"Streaming Quality\",\n    ),\n    \"Subscriptions\": MessageLookupByLibrary.simpleMessage(\"Subscriptions\"),\n    \"Support_Me_On_Kofi\": MessageLookupByLibrary.simpleMessage(\n      \"Support me on Ko-fi\",\n    ),\n    \"Telegram\": MessageLookupByLibrary.simpleMessage(\"Telegram\"),\n    \"Theme_Mode\": MessageLookupByLibrary.simpleMessage(\"Theme Mode\"),\n    \"Top_Results\": MessageLookupByLibrary.simpleMessage(\"Top Results\"),\n    \"Translate_Lyrics\": MessageLookupByLibrary.simpleMessage(\n      \"Translate Lyrics\",\n    ),\n    \"Version\": MessageLookupByLibrary.simpleMessage(\"Version\"),\n    \"View_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Play a song to see the equalizer.\",\n    ),\n    \"Visitor_Id\": MessageLookupByLibrary.simpleMessage(\"Visitor Id\"),\n    \"Window_Effect\": MessageLookupByLibrary.simpleMessage(\"Window Effect\"),\n    \"YTMusic\": MessageLookupByLibrary.simpleMessage(\"YTMusic\"),\n    \"Yes\": MessageLookupByLibrary.simpleMessage(\"Yes\"),\n    \"nSongs\": m1,\n  };\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_es.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a es locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'es';\n\n  static String m1(count) =>\n      \"${Intl.plural(count, zero: 'No hay canciones', one: '1 canción', other: '${count} canciones')}\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"About\": MessageLookupByLibrary.simpleMessage(\"Acerca de\"),\n    \"Add_To_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Añadir a favoritos\",\n    ),\n    \"Add_To_Library\": MessageLookupByLibrary.simpleMessage(\n      \"Añadir a la biblioteca\",\n    ),\n    \"Add_To_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Añadir a la lista de reproducción\",\n    ),\n    \"Add_To_Queue\": MessageLookupByLibrary.simpleMessage(\"Añadir a la cola\"),\n    \"Album\": MessageLookupByLibrary.simpleMessage(\"Álbum\"),\n    \"Albums\": MessageLookupByLibrary.simpleMessage(\"Álbumes\"),\n    \"Appearence\": MessageLookupByLibrary.simpleMessage(\"Apariencia\"),\n    \"Artists\": MessageLookupByLibrary.simpleMessage(\"Artistas\"),\n    \"Audio_And_Playback\": MessageLookupByLibrary.simpleMessage(\n      \"Audio y reproducción\",\n    ),\n    \"Backup\": MessageLookupByLibrary.simpleMessage(\"Copia de seguridad\"),\n    \"Backup_And_Restore\": MessageLookupByLibrary.simpleMessage(\n      \"Copia de seguridad y restauración\",\n    ),\n    \"Battery_Optimisation_message\": MessageLookupByLibrary.simpleMessage(\n      \"Haz clic aquí para desactivar la optimización de batería para que Gyawun funcione correctamente\",\n    ),\n    \"Battery_Optimisation_title\": MessageLookupByLibrary.simpleMessage(\n      \"Optimización de batería detectada\",\n    ),\n    \"Bug_Report\": MessageLookupByLibrary.simpleMessage(\"Reporte de errores\"),\n    \"Buy_Me_A_Coffee\": MessageLookupByLibrary.simpleMessage(\"Cómprame un café\"),\n    \"Cancel\": MessageLookupByLibrary.simpleMessage(\"Cancelar\"),\n    \"Check_For_Update\": MessageLookupByLibrary.simpleMessage(\n      \"Buscar actualizaciones\",\n    ),\n    \"Confirm\": MessageLookupByLibrary.simpleMessage(\"Confirmar\"),\n    \"Content\": MessageLookupByLibrary.simpleMessage(\"Contenido\"),\n    \"Contributors\": MessageLookupByLibrary.simpleMessage(\"Colaboradores\"),\n    \"Copied_To_Clipboard\": MessageLookupByLibrary.simpleMessage(\n      \"Copiado al portapapeles\",\n    ),\n    \"Country\": MessageLookupByLibrary.simpleMessage(\"País\"),\n    \"Create\": MessageLookupByLibrary.simpleMessage(\"Crear\"),\n    \"Create_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Crear lista de reproducción\",\n    ),\n    \"DOwnload_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"Calidad de descarga\",\n    ),\n    \"Delete_Item_Message\": MessageLookupByLibrary.simpleMessage(\n      \"¿Estás seguro de que quieres eliminar este elemento?\",\n    ),\n    \"Delete_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Eliminar historial de reproducción\",\n    ),\n    \"Delete_Playback_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"¿Estás seguro de que quieres eliminar el historial de reproducción?\",\n        ),\n    \"Delete_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Eliminar historial de búsqueda\",\n    ),\n    \"Delete_Search_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"¿Estás seguro de que quieres eliminar el historial de búsqueda?\",\n        ),\n    \"Developer\": MessageLookupByLibrary.simpleMessage(\"Desarrollador\"),\n    \"Donate\": MessageLookupByLibrary.simpleMessage(\"Donar\"),\n    \"Donate_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Apoya el desarrollo de Gyawun\",\n    ),\n    \"Done\": MessageLookupByLibrary.simpleMessage(\"Hecho\"),\n    \"Download\": MessageLookupByLibrary.simpleMessage(\"Descargar\"),\n    \"Downloads\": MessageLookupByLibrary.simpleMessage(\"Descargas\"),\n    \"Dynamic_Colors\": MessageLookupByLibrary.simpleMessage(\"Colores dinámicos\"),\n    \"Enable_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Activar ecualizador\",\n    ),\n    \"Enable_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Activar historial de reproducción\",\n    ),\n    \"Enable_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Activar historial de búsqueda\",\n    ),\n    \"Enter_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Introducir ID de visitante\",\n    ),\n    \"Equalizer\": MessageLookupByLibrary.simpleMessage(\"Ecualizador\"),\n    \"Favourites\": MessageLookupByLibrary.simpleMessage(\"Favoritos\"),\n    \"Feature_Request\": MessageLookupByLibrary.simpleMessage(\n      \"Solicitud de características\",\n    ),\n    \"Google_Account\": MessageLookupByLibrary.simpleMessage(\"Cuenta de Google\"),\n    \"Gyawun\": MessageLookupByLibrary.simpleMessage(\"Gyawun\"),\n    \"High\": MessageLookupByLibrary.simpleMessage(\"Alto\"),\n    \"History\": MessageLookupByLibrary.simpleMessage(\"Historial\"),\n    \"Home\": MessageLookupByLibrary.simpleMessage(\"Inicio\"),\n    \"Import\": MessageLookupByLibrary.simpleMessage(\"Importar\"),\n    \"Import_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Importar lista de reproducción\",\n    ),\n    \"Jhelum_Corp\": MessageLookupByLibrary.simpleMessage(\"Jhelum Corp\"),\n    \"Language\": MessageLookupByLibrary.simpleMessage(\"Idioma\"),\n    \"Loudness_And_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Volumen y ecualizador\",\n    ),\n    \"Loudness_Enhancer\": MessageLookupByLibrary.simpleMessage(\n      \"Mejorador de volumen\",\n    ),\n    \"Low\": MessageLookupByLibrary.simpleMessage(\"Bajo\"),\n    \"Made_In_Kashmir\": MessageLookupByLibrary.simpleMessage(\n      \"Hecho en Cachemira\",\n    ),\n    \"Name\": MessageLookupByLibrary.simpleMessage(\"Nombre\"),\n    \"Next_Up\": MessageLookupByLibrary.simpleMessage(\"Siguiente\"),\n    \"No\": MessageLookupByLibrary.simpleMessage(\"No\"),\n    \"Organisation\": MessageLookupByLibrary.simpleMessage(\"Organización\"),\n    \"Pay_With_UPI\": MessageLookupByLibrary.simpleMessage(\"Pagar con UPI\"),\n    \"Payment_Methods\": MessageLookupByLibrary.simpleMessage(\"Métodos de pago\"),\n    \"Personalised_Content\": MessageLookupByLibrary.simpleMessage(\n      \"Contenido personalizado\",\n    ),\n    \"Play_Next\": MessageLookupByLibrary.simpleMessage(\"Reproducir siguiente\"),\n    \"Playback_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Historial de reproducción eliminado\",\n    ),\n    \"Playlist_Name\": MessageLookupByLibrary.simpleMessage(\"Nombre de la lista\"),\n    \"Playlists\": MessageLookupByLibrary.simpleMessage(\"Listas de reproducción\"),\n    \"Progress\": MessageLookupByLibrary.simpleMessage(\"Progreso\"),\n    \"Remove\": MessageLookupByLibrary.simpleMessage(\"Eliminar\"),\n    \"Remove_All_History_Message\": MessageLookupByLibrary.simpleMessage(\n      \"¿Estás seguro de que quieres borrar todo el historial?\",\n    ),\n    \"Remove_From_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Eliminar de favoritos\",\n    ),\n    \"Remove_From_Library\": MessageLookupByLibrary.simpleMessage(\n      \"Eliminar de la biblioteca\",\n    ),\n    \"Remove_From_YTMusic_Message\": MessageLookupByLibrary.simpleMessage(\n      \"¿Estás seguro de que quieres eliminarlo de YTMusic?\",\n    ),\n    \"Remove_Message\": MessageLookupByLibrary.simpleMessage(\n      \"¿Estás seguro de que quieres eliminarlo?\",\n    ),\n    \"Rename\": MessageLookupByLibrary.simpleMessage(\"Renombrar\"),\n    \"Rename_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Renombrar lista de reproducción\",\n    ),\n    \"Reset_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Restablecer ID de visitante\",\n    ),\n    \"Restore\": MessageLookupByLibrary.simpleMessage(\"Restaurar\"),\n    \"Saved\": MessageLookupByLibrary.simpleMessage(\"Guardado\"),\n    \"Search_Gyawun\": MessageLookupByLibrary.simpleMessage(\"Buscar Gyawun\"),\n    \"Search_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Historial de búsqueda eliminado\",\n    ),\n    \"Search_Settings\": MessageLookupByLibrary.simpleMessage(\n      \"Configuración de búsqueda\",\n    ),\n    \"Select_Backup\": MessageLookupByLibrary.simpleMessage(\n      \"Seleccionar copia de seguridad\",\n    ),\n    \"Settings\": MessageLookupByLibrary.simpleMessage(\"Configuraciones\"),\n    \"Sheikh_Haziq\": MessageLookupByLibrary.simpleMessage(\"Sheikh Haziq\"),\n    \"Show_Less\": MessageLookupByLibrary.simpleMessage(\"Mostrar menos\"),\n    \"Show_More\": MessageLookupByLibrary.simpleMessage(\"Mostrar más\"),\n    \"Shuffle\": MessageLookupByLibrary.simpleMessage(\"Aleatorio\"),\n    \"Skip_Silence\": MessageLookupByLibrary.simpleMessage(\"Saltar silencio\"),\n    \"Sleep_Timer\": MessageLookupByLibrary.simpleMessage(\n      \"Temporizador de apagado\",\n    ),\n    \"Songs\": MessageLookupByLibrary.simpleMessage(\"Canciones\"),\n    \"Songs_Will_Start_Playing_Soon\": MessageLookupByLibrary.simpleMessage(\n      \"Las canciones comenzarán a reproducirse pronto.\",\n    ),\n    \"Source_Code\": MessageLookupByLibrary.simpleMessage(\"Código fuente\"),\n    \"Start_Radio\": MessageLookupByLibrary.simpleMessage(\"Iniciar radio\"),\n    \"Streaming_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"Calidad de transmisión\",\n    ),\n    \"Subscriptions\": MessageLookupByLibrary.simpleMessage(\"Suscripciones\"),\n    \"Support_Me_On_Kofi\": MessageLookupByLibrary.simpleMessage(\n      \"Apóyame en Ko-fi\",\n    ),\n    \"Telegram\": MessageLookupByLibrary.simpleMessage(\"Telegram\"),\n    \"Theme_Mode\": MessageLookupByLibrary.simpleMessage(\"Modo de tema\"),\n    \"Version\": MessageLookupByLibrary.simpleMessage(\"Versión\"),\n    \"Visitor_Id\": MessageLookupByLibrary.simpleMessage(\"ID de visitante\"),\n    \"Window_Effect\": MessageLookupByLibrary.simpleMessage(\"Efecto de ventana\"),\n    \"YTMusic\": MessageLookupByLibrary.simpleMessage(\"YTMusic\"),\n    \"Yes\": MessageLookupByLibrary.simpleMessage(\"Sí\"),\n    \"nSongs\": m1,\n  };\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_fr.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a fr locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'fr';\n\n  static String m1(count) =>\n      \"${Intl.plural(count, zero: 'Pas de Titres', one: '1 Titre', other: '${count} Titres')}\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"About\": MessageLookupByLibrary.simpleMessage(\"À propos\"),\n    \"Add_To_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Ajouter aux favoris\",\n    ),\n    \"Add_To_Library\": MessageLookupByLibrary.simpleMessage(\n      \"Ajouter à la bibliothèque\",\n    ),\n    \"Add_To_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Ajouter à une playlist\",\n    ),\n    \"Add_To_Queue\": MessageLookupByLibrary.simpleMessage(\n      \"Ajouter à la file d\\'attente\",\n    ),\n    \"Album\": MessageLookupByLibrary.simpleMessage(\"Album\"),\n    \"Albums\": MessageLookupByLibrary.simpleMessage(\"Albums\"),\n    \"App_Folder\": MessageLookupByLibrary.simpleMessage(\"Dossier Application\"),\n    \"Appearence\": MessageLookupByLibrary.simpleMessage(\"Apparence\"),\n    \"Artists\": MessageLookupByLibrary.simpleMessage(\"Artistes\"),\n    \"Audio_And_Playback\": MessageLookupByLibrary.simpleMessage(\n      \"Audio et Lecture\",\n    ),\n    \"Autofetch_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Lecture automatique de titres similaires\",\n    ),\n    \"Backup\": MessageLookupByLibrary.simpleMessage(\"Sauvegarde\"),\n    \"Backup_And_Restore\": MessageLookupByLibrary.simpleMessage(\n      \"Sauvegarde et Restauration\",\n    ),\n    \"Backup_Failed\": MessageLookupByLibrary.simpleMessage(\n      \"Échec de la sauvegarde des données\",\n    ),\n    \"Backup_Success\": MessageLookupByLibrary.simpleMessage(\n      \"Sauvegarde effectuée avec succès à\",\n    ),\n    \"Battery_Optimisation_message\": MessageLookupByLibrary.simpleMessage(\n      \"Cliquez ici pour désactiver l\\'optimisation de la batterie afin que Gyawun fonctionne correctement.\",\n    ),\n    \"Battery_Optimisation_title\": MessageLookupByLibrary.simpleMessage(\n      \"Optimisation de batterie détectée\",\n    ),\n    \"Bug_Report\": MessageLookupByLibrary.simpleMessage(\"Rapport de bug\"),\n    \"Buy_Me_A_Coffee\": MessageLookupByLibrary.simpleMessage(\n      \"Offrez-moi un café\",\n    ),\n    \"Cancel\": MessageLookupByLibrary.simpleMessage(\"Annuler\"),\n    \"Check_For_Update\": MessageLookupByLibrary.simpleMessage(\n      \"Vérifier les mises à jour\",\n    ),\n    \"Confirm\": MessageLookupByLibrary.simpleMessage(\"Confirmer\"),\n    \"Confirm_Delete_All_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Êtes-vous sûr de vouloir les supprimer ?\",\n    ),\n    \"Content\": MessageLookupByLibrary.simpleMessage(\"Contenu\"),\n    \"Contributors\": MessageLookupByLibrary.simpleMessage(\"Contributeurs\"),\n    \"Copied_To_Clipboard\": MessageLookupByLibrary.simpleMessage(\n      \"Copié dans le presse-papiers\",\n    ),\n    \"Country\": MessageLookupByLibrary.simpleMessage(\"Pays\"),\n    \"Create\": MessageLookupByLibrary.simpleMessage(\"Créer\"),\n    \"Create_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Créer une playlist\",\n    ),\n    \"DOwnload_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"Qualité du Téléchargement\",\n    ),\n    \"Delete_All_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Supprimer tous les titres\",\n    ),\n    \"Delete_Item_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Êtes-vous sûr de vouloir supprimer cet élément ?\",\n    ),\n    \"Delete_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Supprimer l\\'historique de lecture\",\n    ),\n    \"Delete_Playback_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"Êtes-vous sûr de vouloir supprimer l\\'historique de lecture ?\",\n        ),\n    \"Delete_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Supprimer l\\'historique de recherche\",\n    ),\n    \"Delete_Search_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"Êtes-vous sûr de vouloir supprimer l\\'historique de recherche ?\",\n        ),\n    \"Deleting_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Suppression de titres...\",\n    ),\n    \"Developer\": MessageLookupByLibrary.simpleMessage(\"Développeur\"),\n    \"Donate\": MessageLookupByLibrary.simpleMessage(\"Faire un don\"),\n    \"Donate_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Soutenez le développement de Gyawun\",\n    ),\n    \"Done\": MessageLookupByLibrary.simpleMessage(\"Terminé\"),\n    \"Download\": MessageLookupByLibrary.simpleMessage(\"Télécharger\"),\n    \"Download_Started\": MessageLookupByLibrary.simpleMessage(\n      \"Téléchargement en cours...\",\n    ),\n    \"Downloading\": MessageLookupByLibrary.simpleMessage(\"Téléchargement\"),\n    \"Downloads\": MessageLookupByLibrary.simpleMessage(\"Téléchargements\"),\n    \"Dynamic_Colors\": MessageLookupByLibrary.simpleMessage(\n      \"Couleurs Dynamiques\",\n    ),\n    \"Enable_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Activer l\\'égaliseur\",\n    ),\n    \"Enable_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Activer l\\'historique de lecture\",\n    ),\n    \"Enable_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Activer l\\'historique de recherche\",\n    ),\n    \"Enter_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Saisir l\\'identifiant du visiteur\",\n    ),\n    \"Equalizer\": MessageLookupByLibrary.simpleMessage(\"Égaliseur\"),\n    \"Favourites\": MessageLookupByLibrary.simpleMessage(\"Favoris\"),\n    \"Feature_Request\": MessageLookupByLibrary.simpleMessage(\n      \"Demande de fonctionnalité\",\n    ),\n    \"Go_To_Downloads\": MessageLookupByLibrary.simpleMessage(\n      \"Accéder aux téléchargements\",\n    ),\n    \"Google_Account\": MessageLookupByLibrary.simpleMessage(\"Compte Google\"),\n    \"Gyawun\": MessageLookupByLibrary.simpleMessage(\"Gyawun\"),\n    \"High\": MessageLookupByLibrary.simpleMessage(\"Haute\"),\n    \"History\": MessageLookupByLibrary.simpleMessage(\"Historique\"),\n    \"Home\": MessageLookupByLibrary.simpleMessage(\"Accueil\"),\n    \"Import\": MessageLookupByLibrary.simpleMessage(\"Importer\"),\n    \"Import_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Importer une playlist\",\n    ),\n    \"In_Progress\": MessageLookupByLibrary.simpleMessage(\"En cours\"),\n    \"Jhelum_Corp\": MessageLookupByLibrary.simpleMessage(\"Jhelum Corp\"),\n    \"Language\": MessageLookupByLibrary.simpleMessage(\"Langue\"),\n    \"Loudness_And_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Volume et Égaliseur\",\n    ),\n    \"Loudness_Enhancer\": MessageLookupByLibrary.simpleMessage(\n      \"Amplificateur de Volume\",\n    ),\n    \"Low\": MessageLookupByLibrary.simpleMessage(\"Basse\"),\n    \"Made_In_Kashmir\": MessageLookupByLibrary.simpleMessage(\n      \"Fabriqué au Cachemire\",\n    ),\n    \"Name\": MessageLookupByLibrary.simpleMessage(\"Nom\"),\n    \"Next_Up\": MessageLookupByLibrary.simpleMessage(\"Suivant\"),\n    \"No\": MessageLookupByLibrary.simpleMessage(\"Non\"),\n    \"No_Internet_Connection\": MessageLookupByLibrary.simpleMessage(\n      \"Aucune connexion Internet\",\n    ),\n    \"Organisation\": MessageLookupByLibrary.simpleMessage(\"Organisation\"),\n    \"Pay_With_UPI\": MessageLookupByLibrary.simpleMessage(\"Payer avec UPI\"),\n    \"Payment_Methods\": MessageLookupByLibrary.simpleMessage(\n      \"Modes de Paiement\",\n    ),\n    \"Personalised_Content\": MessageLookupByLibrary.simpleMessage(\n      \"Contenu Personnalisé\",\n    ),\n    \"Play_Next\": MessageLookupByLibrary.simpleMessage(\"Lire ensuite\"),\n    \"Playback_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Historique de lecture supprimé\",\n    ),\n    \"Playlist_Name\": MessageLookupByLibrary.simpleMessage(\"Nom de la playlist\"),\n    \"Playlist_Not_Available\": MessageLookupByLibrary.simpleMessage(\n      \"Playlist indisponible\",\n    ),\n    \"Playlists\": MessageLookupByLibrary.simpleMessage(\"Playlists\"),\n    \"Progress\": MessageLookupByLibrary.simpleMessage(\"Progrès\"),\n    \"Queued\": MessageLookupByLibrary.simpleMessage(\"En attente\"),\n    \"Remove\": MessageLookupByLibrary.simpleMessage(\"Supprimer\"),\n    \"Remove_All_History_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Êtes-vous sûr de vouloir effacer tout l\\'historique ?\",\n    ),\n    \"Remove_From_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Supprimer des favoris\",\n    ),\n    \"Remove_From_Library\": MessageLookupByLibrary.simpleMessage(\n      \"Supprimer de la bibliothèque\",\n    ),\n    \"Remove_From_YTMusic_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Êtes-vous sûr de vouloir le supprimer de YouTube Music ?\",\n    ),\n    \"Remove_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Êtes-vous sûr de vouloir le supprimer ?\",\n    ),\n    \"Rename\": MessageLookupByLibrary.simpleMessage(\"Renommer\"),\n    \"Rename_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Renommer la playlist\",\n    ),\n    \"Reset_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Réinitialiser l\\'identifiant du visiteur\",\n    ),\n    \"Restore\": MessageLookupByLibrary.simpleMessage(\"Restauration\"),\n    \"Restore_Failed\": MessageLookupByLibrary.simpleMessage(\n      \"Échec de la restauration des données\",\n    ),\n    \"Restore_Missing_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Restaurer les titres manquants\",\n    ),\n    \"Restore_Success\": MessageLookupByLibrary.simpleMessage(\n      \"Données restaurées avec succès\",\n    ),\n    \"Restoring_Missing_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Restauration des titres manquants...\",\n    ),\n    \"Retry\": MessageLookupByLibrary.simpleMessage(\"Réessayer\"),\n    \"Save\": MessageLookupByLibrary.simpleMessage(\"Enregistrer\"),\n    \"Saved\": MessageLookupByLibrary.simpleMessage(\"Enregistré\"),\n    \"Search_Gyawun\": MessageLookupByLibrary.simpleMessage(\n      \"Rechercher sur Gyawun\",\n    ),\n    \"Search_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Historique de recherche supprimé\",\n    ),\n    \"Search_Settings\": MessageLookupByLibrary.simpleMessage(\n      \"Paramètres de recherche\",\n    ),\n    \"Select_Backup\": MessageLookupByLibrary.simpleMessage(\n      \"Sélectionnez  la sauvegarde\",\n    ),\n    \"Settings\": MessageLookupByLibrary.simpleMessage(\"Paramètres\"),\n    \"Share\": MessageLookupByLibrary.simpleMessage(\"Partager\"),\n    \"Sheikh_Haziq\": MessageLookupByLibrary.simpleMessage(\"Sheikh Haziq\"),\n    \"Show_Less\": MessageLookupByLibrary.simpleMessage(\"Afficher moins\"),\n    \"Show_More\": MessageLookupByLibrary.simpleMessage(\"Afficher plus\"),\n    \"Shuffle\": MessageLookupByLibrary.simpleMessage(\"Aléatoire\"),\n    \"Skip_Silence\": MessageLookupByLibrary.simpleMessage(\"Ignorer le Silence\"),\n    \"Sleep_Timer\": MessageLookupByLibrary.simpleMessage(\"Minuterie de Sommeil\"),\n    \"Songs\": MessageLookupByLibrary.simpleMessage(\"Titres\"),\n    \"Songs_Will_Start_Playing_Soon\": MessageLookupByLibrary.simpleMessage(\n      \"Les titres commenceront bientôt à être diffusés.\",\n    ),\n    \"Source_Code\": MessageLookupByLibrary.simpleMessage(\"Code Source\"),\n    \"Start_Radio\": MessageLookupByLibrary.simpleMessage(\"Démarrer la radio\"),\n    \"Streaming_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"Qualité du Streaming\",\n    ),\n    \"Subscriptions\": MessageLookupByLibrary.simpleMessage(\"Abonnements\"),\n    \"Support_Me_On_Kofi\": MessageLookupByLibrary.simpleMessage(\n      \"Soutenez-moi sur Ko-fi\",\n    ),\n    \"Telegram\": MessageLookupByLibrary.simpleMessage(\"Telegram\"),\n    \"Theme_Mode\": MessageLookupByLibrary.simpleMessage(\"Thème\"),\n    \"Translate_Lyrics\": MessageLookupByLibrary.simpleMessage(\n      \"Traduire les paroles\",\n    ),\n    \"Version\": MessageLookupByLibrary.simpleMessage(\"Version\"),\n    \"Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Identifiant du Visiteur\",\n    ),\n    \"Window_Effect\": MessageLookupByLibrary.simpleMessage(\"Effet fenêtre\"),\n    \"YTMusic\": MessageLookupByLibrary.simpleMessage(\"YouTube Music\"),\n    \"Yes\": MessageLookupByLibrary.simpleMessage(\"Oui\"),\n    \"nSongs\": m1,\n  };\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_hi.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a hi locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'hi';\n\n  static String m1(count) =>\n      \"${Intl.plural(count, zero: 'कोई गाने नहीं', one: '1 गाना', other: '${count} गाने')}\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"About\": MessageLookupByLibrary.simpleMessage(\"के बारे में\"),\n    \"Add_To_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"पसंदीदा में जोड़ें\",\n    ),\n    \"Add_To_Library\": MessageLookupByLibrary.simpleMessage(\n      \"लाइब्रेरी में जोड़ें\",\n    ),\n    \"Add_To_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"प्लेलिस्ट में जोड़ें\",\n    ),\n    \"Add_To_Queue\": MessageLookupByLibrary.simpleMessage(\"कतार में जोड़ें\"),\n    \"Album\": MessageLookupByLibrary.simpleMessage(\"एल्बम\"),\n    \"Albums\": MessageLookupByLibrary.simpleMessage(\"एल्बम\"),\n    \"Appearence\": MessageLookupByLibrary.simpleMessage(\"दिखावट\"),\n    \"Artists\": MessageLookupByLibrary.simpleMessage(\"कलाकार\"),\n    \"Audio_And_Playback\": MessageLookupByLibrary.simpleMessage(\n      \"ऑडियो और प्लेबैक\",\n    ),\n    \"Backup\": MessageLookupByLibrary.simpleMessage(\"बैकअप\"),\n    \"Backup_And_Restore\": MessageLookupByLibrary.simpleMessage(\n      \"बैकअप और पुनर्स्थापना\",\n    ),\n    \"Battery_Optimisation_message\": MessageLookupByLibrary.simpleMessage(\n      \"ग्यावुन को ठीक से काम करने के लिए बैटरी ऑप्टिमाइजेशन को अक्षम करने के लिए यहां क्लिक करें\",\n    ),\n    \"Battery_Optimisation_title\": MessageLookupByLibrary.simpleMessage(\n      \"बैटरी ऑप्टिमाइजेशन पता चला\",\n    ),\n    \"Bug_Report\": MessageLookupByLibrary.simpleMessage(\"बग रिपोर्ट\"),\n    \"Buy_Me_A_Coffee\": MessageLookupByLibrary.simpleMessage(\n      \"मुझे एक कॉफी खरीदें\",\n    ),\n    \"Cancel\": MessageLookupByLibrary.simpleMessage(\"रद्द करें\"),\n    \"Check_For_Update\": MessageLookupByLibrary.simpleMessage(\n      \"अपडेट के लिए जाँचें\",\n    ),\n    \"Confirm\": MessageLookupByLibrary.simpleMessage(\"पुष्टि करें\"),\n    \"Content\": MessageLookupByLibrary.simpleMessage(\"सामग्री\"),\n    \"Contributors\": MessageLookupByLibrary.simpleMessage(\"योगदानकर्ता\"),\n    \"Copied_To_Clipboard\": MessageLookupByLibrary.simpleMessage(\n      \"क्लिपबोर्ड पर कॉपी किया गया\",\n    ),\n    \"Country\": MessageLookupByLibrary.simpleMessage(\"देश\"),\n    \"Create\": MessageLookupByLibrary.simpleMessage(\"बनाएं\"),\n    \"Create_Playlist\": MessageLookupByLibrary.simpleMessage(\"प्लेलिस्ट बनाएं\"),\n    \"DOwnload_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"डाउनलोड गुणवत्ता\",\n    ),\n    \"Delete_Item_Message\": MessageLookupByLibrary.simpleMessage(\n      \"क्या आप वाकई इस आइटम को हटाना चाहते हैं?\",\n    ),\n    \"Delete_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"प्लेबैक इतिहास हटाएं\",\n    ),\n    \"Delete_Playback_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"क्या आप वाकई प्लेबैक इतिहास हटाना चाहते हैं।\",\n        ),\n    \"Delete_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"खोज इतिहास हटाएं\",\n    ),\n    \"Delete_Search_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"क्या आप वाकई खोज इतिहास हटाना चाहते हैं।\",\n        ),\n    \"Developer\": MessageLookupByLibrary.simpleMessage(\"डेवलपर\"),\n    \"Donate\": MessageLookupByLibrary.simpleMessage(\"दान करें\"),\n    \"Donate_Message\": MessageLookupByLibrary.simpleMessage(\n      \"ग्यावुन के विकास का समर्थन करें\",\n    ),\n    \"Done\": MessageLookupByLibrary.simpleMessage(\"हो गया\"),\n    \"Download\": MessageLookupByLibrary.simpleMessage(\"डाउनलोड\"),\n    \"Downloads\": MessageLookupByLibrary.simpleMessage(\"डाउनलोड\"),\n    \"Dynamic_Colors\": MessageLookupByLibrary.simpleMessage(\"डायनामिक रंग\"),\n    \"Enable_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"इक्वलाइज़र सक्षम करें\",\n    ),\n    \"Enable_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"प्लेबैक इतिहास सक्षम करें\",\n    ),\n    \"Enable_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"खोज इतिहास सक्षम करें\",\n    ),\n    \"Enter_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"विज़िटर आईडी दर्ज करें\",\n    ),\n    \"Equalizer\": MessageLookupByLibrary.simpleMessage(\"इक्वलाइज़र\"),\n    \"Favourites\": MessageLookupByLibrary.simpleMessage(\"पसंदीदा\"),\n    \"Feature_Request\": MessageLookupByLibrary.simpleMessage(\"फीचर अनुरोध\"),\n    \"Google_Account\": MessageLookupByLibrary.simpleMessage(\"गूगल अकाउंट\"),\n    \"Gyawun\": MessageLookupByLibrary.simpleMessage(\"ग्यावुन\"),\n    \"High\": MessageLookupByLibrary.simpleMessage(\"उच्च\"),\n    \"History\": MessageLookupByLibrary.simpleMessage(\"इतिहास\"),\n    \"Home\": MessageLookupByLibrary.simpleMessage(\"होम\"),\n    \"Import\": MessageLookupByLibrary.simpleMessage(\"आयात करें\"),\n    \"Import_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"प्लेलिस्ट आयात करें\",\n    ),\n    \"Jhelum_Corp\": MessageLookupByLibrary.simpleMessage(\"झेलम कॉर्प\"),\n    \"Language\": MessageLookupByLibrary.simpleMessage(\"भाषा\"),\n    \"Loudness_And_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"लाउडनेस और इक्वलाइज़र\",\n    ),\n    \"Loudness_Enhancer\": MessageLookupByLibrary.simpleMessage(\n      \"लाउडनेस एन्हांसर\",\n    ),\n    \"Low\": MessageLookupByLibrary.simpleMessage(\"निम्न\"),\n    \"Made_In_Kashmir\": MessageLookupByLibrary.simpleMessage(\"कश्मीर में बना\"),\n    \"Name\": MessageLookupByLibrary.simpleMessage(\"नाम\"),\n    \"Next_Up\": MessageLookupByLibrary.simpleMessage(\"अगला\"),\n    \"No\": MessageLookupByLibrary.simpleMessage(\"नहीं\"),\n    \"Organisation\": MessageLookupByLibrary.simpleMessage(\"संगठन\"),\n    \"Pay_With_UPI\": MessageLookupByLibrary.simpleMessage(\n      \"UPI के साथ भुगतान करें\",\n    ),\n    \"Payment_Methods\": MessageLookupByLibrary.simpleMessage(\"भुगतान के तरीके\"),\n    \"Personalised_Content\": MessageLookupByLibrary.simpleMessage(\n      \"व्यक्तिगत सामग्री\",\n    ),\n    \"Play_Next\": MessageLookupByLibrary.simpleMessage(\"अगला चलाएं\"),\n    \"Playback_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"प्लेबैक इतिहास हटाया गया\",\n    ),\n    \"Playlist_Name\": MessageLookupByLibrary.simpleMessage(\"प्लेलिस्ट का नाम\"),\n    \"Playlists\": MessageLookupByLibrary.simpleMessage(\"प्लेलिस्ट\"),\n    \"Progress\": MessageLookupByLibrary.simpleMessage(\"प्रगति\"),\n    \"Remove\": MessageLookupByLibrary.simpleMessage(\"हटाएं\"),\n    \"Remove_All_History_Message\": MessageLookupByLibrary.simpleMessage(\n      \"क्या आप वाकई सभी इतिहास साफ़ करना चाहते हैं?\",\n    ),\n    \"Remove_From_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"पसंदीदा से हटाएं\",\n    ),\n    \"Remove_From_Library\": MessageLookupByLibrary.simpleMessage(\n      \"लाइब्रेरी से हटाएं\",\n    ),\n    \"Remove_From_YTMusic_Message\": MessageLookupByLibrary.simpleMessage(\n      \"क्या आप वाकई इसे YT संगीत से हटाना चाहते हैं?\",\n    ),\n    \"Remove_Message\": MessageLookupByLibrary.simpleMessage(\n      \"क्या आप वाकई इसे हटाना चाहते हैं?\",\n    ),\n    \"Rename\": MessageLookupByLibrary.simpleMessage(\"नाम बदलें\"),\n    \"Rename_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"प्लेलिस्ट का नाम बदलें\",\n    ),\n    \"Reset_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"विज़िटर आईडी रीसेट करें\",\n    ),\n    \"Restore\": MessageLookupByLibrary.simpleMessage(\"पुनर्स्थापित\"),\n    \"Saved\": MessageLookupByLibrary.simpleMessage(\"सहेजे गए\"),\n    \"Search_Gyawun\": MessageLookupByLibrary.simpleMessage(\"ग्यावुन खोजें\"),\n    \"Search_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"खोज इतिहास हटाया गया\",\n    ),\n    \"Search_Settings\": MessageLookupByLibrary.simpleMessage(\"खोज सेटिंग्स\"),\n    \"Select_Backup\": MessageLookupByLibrary.simpleMessage(\"बैकअप चुनें\"),\n    \"Settings\": MessageLookupByLibrary.simpleMessage(\"सेटिंग्स\"),\n    \"Sheikh_Haziq\": MessageLookupByLibrary.simpleMessage(\"शेख हाजिक\"),\n    \"Show_Less\": MessageLookupByLibrary.simpleMessage(\"कम दिखाएं\"),\n    \"Show_More\": MessageLookupByLibrary.simpleMessage(\"और दिखाएं\"),\n    \"Shuffle\": MessageLookupByLibrary.simpleMessage(\"शफल\"),\n    \"Skip_Silence\": MessageLookupByLibrary.simpleMessage(\"चुप्पी छोड़ें\"),\n    \"Sleep_Timer\": MessageLookupByLibrary.simpleMessage(\"स्लीप टाइमर\"),\n    \"Songs\": MessageLookupByLibrary.simpleMessage(\"गाने\"),\n    \"Songs_Will_Start_Playing_Soon\": MessageLookupByLibrary.simpleMessage(\n      \"गाने जल्द ही बजना शुरू हो जाएंगे।\",\n    ),\n    \"Source_Code\": MessageLookupByLibrary.simpleMessage(\"सोर्स कोड\"),\n    \"Start_Radio\": MessageLookupByLibrary.simpleMessage(\"रेडियो शुरू करें\"),\n    \"Streaming_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"स्ट्रीमिंग गुणवत्ता\",\n    ),\n    \"Subscriptions\": MessageLookupByLibrary.simpleMessage(\"सब्सक्रिप्शन\"),\n    \"Support_Me_On_Kofi\": MessageLookupByLibrary.simpleMessage(\n      \"को-फ़ी पर मुझे समर्थन दें\",\n    ),\n    \"Telegram\": MessageLookupByLibrary.simpleMessage(\"टेलीग्राम\"),\n    \"Theme_Mode\": MessageLookupByLibrary.simpleMessage(\"थीम मोड\"),\n    \"Version\": MessageLookupByLibrary.simpleMessage(\"संस्करण\"),\n    \"Visitor_Id\": MessageLookupByLibrary.simpleMessage(\"विज़िटर आईडी\"),\n    \"Window_Effect\": MessageLookupByLibrary.simpleMessage(\"विंडो प्रभाव\"),\n    \"YTMusic\": MessageLookupByLibrary.simpleMessage(\"YT संगीत\"),\n    \"Yes\": MessageLookupByLibrary.simpleMessage(\"हाँ\"),\n    \"nSongs\": m1,\n  };\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_it.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a it locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'it';\n\n  static String m0(count) => \"In coda (${count})\";\n\n  static String m1(count) =>\n      \"${Intl.plural(count, zero: 'Nessun brano', one: '1 brano', other: '${count} brani')}\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"About\": MessageLookupByLibrary.simpleMessage(\"Informazioni\"),\n    \"Add_To_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Aggiungi ai preferiti\",\n    ),\n    \"Add_To_Library\": MessageLookupByLibrary.simpleMessage(\n      \"Aggiungi alla libreria\",\n    ),\n    \"Add_To_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Aggiungi alla playlist\",\n    ),\n    \"Add_To_Queue\": MessageLookupByLibrary.simpleMessage(\"Aggiungi alla coda\"),\n    \"Album\": MessageLookupByLibrary.simpleMessage(\"Album\"),\n    \"Albums\": MessageLookupByLibrary.simpleMessage(\"Album\"),\n    \"App_Folder\": MessageLookupByLibrary.simpleMessage(\"Cartella App\"),\n    \"Appearence\": MessageLookupByLibrary.simpleMessage(\"Aspetto\"),\n    \"Artists\": MessageLookupByLibrary.simpleMessage(\"Artisti\"),\n    \"Audio_And_Playback\": MessageLookupByLibrary.simpleMessage(\n      \"Audio e riproduzione\",\n    ),\n    \"Autofetch_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Riproduci automaticamente brani simili\",\n    ),\n    \"Backup\": MessageLookupByLibrary.simpleMessage(\"Backup\"),\n    \"Backup_And_Restore\": MessageLookupByLibrary.simpleMessage(\n      \"Backup e ripristino\",\n    ),\n    \"Backup_Failed\": MessageLookupByLibrary.simpleMessage(\n      \"Salvataggio dati non riuscito\",\n    ),\n    \"Backup_Success\": MessageLookupByLibrary.simpleMessage(\n      \"Salvataggio dati riuscito al percorso\",\n    ),\n    \"Battery_Optimisation_message\": MessageLookupByLibrary.simpleMessage(\n      \"Clicca qui per disattivare l’ottimizzazione della batteria e permettere a Gyawun di funzionare correttamente\",\n    ),\n    \"Battery_Optimisation_title\": MessageLookupByLibrary.simpleMessage(\n      \"Ottimizzazione batteria rilevata\",\n    ),\n    \"Bug_Report\": MessageLookupByLibrary.simpleMessage(\"Segnala un bug\"),\n    \"Buy_Me_A_Coffee\": MessageLookupByLibrary.simpleMessage(\"Offrimi un caffè\"),\n    \"Cancel\": MessageLookupByLibrary.simpleMessage(\"Annulla\"),\n    \"Check_For_Update\": MessageLookupByLibrary.simpleMessage(\n      \"Verifica aggiornamenti\",\n    ),\n    \"Confirm\": MessageLookupByLibrary.simpleMessage(\"Conferma\"),\n    \"Confirm_Delete_All_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Sei sicuro di volerli eliminare?\",\n    ),\n    \"Content\": MessageLookupByLibrary.simpleMessage(\"Contenuti\"),\n    \"Contributors\": MessageLookupByLibrary.simpleMessage(\"Collaboratori\"),\n    \"Copied_To_Clipboard\": MessageLookupByLibrary.simpleMessage(\n      \"Copiato negli appunti\",\n    ),\n    \"Country\": MessageLookupByLibrary.simpleMessage(\"Paese\"),\n    \"Create\": MessageLookupByLibrary.simpleMessage(\"Crea\"),\n    \"Create_Playlist\": MessageLookupByLibrary.simpleMessage(\"Crea playlist\"),\n    \"DOwnload_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"Qualità download\",\n    ),\n    \"Delete_All_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Elimina tutti i brani\",\n    ),\n    \"Delete_Item_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Sei sicuro di voler eliminare questo elemento?\",\n    ),\n    \"Delete_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Elimina cronologia di riproduzione\",\n    ),\n    \"Delete_Playback_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"Sei sicuro di voler eliminare la cronologia di riproduzione?\",\n        ),\n    \"Delete_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Elimina cronologia di ricerca\",\n    ),\n    \"Delete_Search_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"Sei sicuro di voler eliminare la cronologia di ricerca?\",\n        ),\n    \"Deleting_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Eliminazione brani...\",\n    ),\n    \"Developer\": MessageLookupByLibrary.simpleMessage(\"Sviluppatore\"),\n    \"Donate\": MessageLookupByLibrary.simpleMessage(\"Dona\"),\n    \"Donate_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Supporta lo sviluppo di Gyawun\",\n    ),\n    \"Done\": MessageLookupByLibrary.simpleMessage(\"Fatto\"),\n    \"Download\": MessageLookupByLibrary.simpleMessage(\"Download\"),\n    \"Download_Started\": MessageLookupByLibrary.simpleMessage(\n      \"Download avviato...\",\n    ),\n    \"Downloading\": MessageLookupByLibrary.simpleMessage(\"In download\"),\n    \"Downloads\": MessageLookupByLibrary.simpleMessage(\"Download\"),\n    \"Dynamic_Colors\": MessageLookupByLibrary.simpleMessage(\"Colori dinamici\"),\n    \"Edit\": MessageLookupByLibrary.simpleMessage(\"Modifica\"),\n    \"Edit_Playlist\": MessageLookupByLibrary.simpleMessage(\"Modifica Playlist\"),\n    \"Enable_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Abilita equalizzatore\",\n    ),\n    \"Enable_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Abilita cronologia di riproduzione\",\n    ),\n    \"Enable_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Abilita cronologia di ricerca\",\n    ),\n    \"Enter_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Inserisci Visitor ID\",\n    ),\n    \"Equalizer\": MessageLookupByLibrary.simpleMessage(\"Equalizzatore\"),\n    \"Favourites\": MessageLookupByLibrary.simpleMessage(\"Preferiti\"),\n    \"Feature_Request\": MessageLookupByLibrary.simpleMessage(\n      \"Richiesta funzionalità\",\n    ),\n    \"File_Not_Found\": MessageLookupByLibrary.simpleMessage(\"File non trovato\"),\n    \"Go_To_Downloads\": MessageLookupByLibrary.simpleMessage(\"Vai a Download\"),\n    \"Google_Account\": MessageLookupByLibrary.simpleMessage(\"Account Google\"),\n    \"Gyawun\": MessageLookupByLibrary.simpleMessage(\"Gyawun\"),\n    \"High\": MessageLookupByLibrary.simpleMessage(\"Alta\"),\n    \"History\": MessageLookupByLibrary.simpleMessage(\"Cronologia\"),\n    \"Home\": MessageLookupByLibrary.simpleMessage(\"Home\"),\n    \"Import\": MessageLookupByLibrary.simpleMessage(\"Importa\"),\n    \"Import_Playlist\": MessageLookupByLibrary.simpleMessage(\"Importa playlist\"),\n    \"In_Progress\": MessageLookupByLibrary.simpleMessage(\"In corso\"),\n    \"Jhelum_Corp\": MessageLookupByLibrary.simpleMessage(\"Jhelum Corp\"),\n    \"Language\": MessageLookupByLibrary.simpleMessage(\"Lingua\"),\n    \"Loudness_And_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Volume ed equalizzatore\",\n    ),\n    \"Loudness_Enhancer\": MessageLookupByLibrary.simpleMessage(\n      \"Amplificatore volume\",\n    ),\n    \"Low\": MessageLookupByLibrary.simpleMessage(\"Bassa\"),\n    \"Made_In_Kashmir\": MessageLookupByLibrary.simpleMessage(\n      \"Realizzato in Kashmir\",\n    ),\n    \"Name\": MessageLookupByLibrary.simpleMessage(\"Nome\"),\n    \"Next_Up\": MessageLookupByLibrary.simpleMessage(\"Prossimo\"),\n    \"No\": MessageLookupByLibrary.simpleMessage(\"No\"),\n    \"No_Internet_Connection\": MessageLookupByLibrary.simpleMessage(\n      \"Nessuna Connessione Internet\",\n    ),\n    \"No_Offline_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Nessun brano scaricato\",\n    ),\n    \"Organisation\": MessageLookupByLibrary.simpleMessage(\"Organizzazione\"),\n    \"Other_Results\": MessageLookupByLibrary.simpleMessage(\"Altri Risultati\"),\n    \"Pay_With_UPI\": MessageLookupByLibrary.simpleMessage(\"Paga con UPI\"),\n    \"Payment_Methods\": MessageLookupByLibrary.simpleMessage(\n      \"Metodi di pagamento\",\n    ),\n    \"Personalised_Content\": MessageLookupByLibrary.simpleMessage(\n      \"Contenuti personalizzati\",\n    ),\n    \"Play_All\": MessageLookupByLibrary.simpleMessage(\"Riproduci\"),\n    \"Play_Next\": MessageLookupByLibrary.simpleMessage(\"Riproduci dopo\"),\n    \"Playback_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Cronologia di riproduzione eliminata\",\n    ),\n    \"Playlist_Name\": MessageLookupByLibrary.simpleMessage(\"Nome playlist\"),\n    \"Playlist_Not_Available\": MessageLookupByLibrary.simpleMessage(\n      \"Playlist non disponibile\",\n    ),\n    \"Playlists\": MessageLookupByLibrary.simpleMessage(\"Playlist\"),\n    \"Progress\": MessageLookupByLibrary.simpleMessage(\"Avanzamento\"),\n    \"Queued\": MessageLookupByLibrary.simpleMessage(\"In coda\"),\n    \"Queued_Count\": m0,\n    \"Remove\": MessageLookupByLibrary.simpleMessage(\"Rimuovi\"),\n    \"Remove_All_History_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Sei sicuro di voler cancellare tutta la cronologia?\",\n    ),\n    \"Remove_From_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Rimuovi dai preferiti\",\n    ),\n    \"Remove_From_Library\": MessageLookupByLibrary.simpleMessage(\n      \"Rimuovi dalla libreria\",\n    ),\n    \"Remove_From_YTMusic_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Sei sicuro di volerlo rimuovere da YTMusic?\",\n    ),\n    \"Remove_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Sei sicuro di volerlo rimuovere?\",\n    ),\n    \"Rename\": MessageLookupByLibrary.simpleMessage(\"Rinomina\"),\n    \"Rename_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Rinomina playlist\",\n    ),\n    \"Reset_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Reimposta Visitor ID\",\n    ),\n    \"Restore\": MessageLookupByLibrary.simpleMessage(\"Ripristina\"),\n    \"Restore_Failed\": MessageLookupByLibrary.simpleMessage(\n      \"Ripristino dati non riuscito\",\n    ),\n    \"Restore_Missing_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Ripristina brani mancanti\",\n    ),\n    \"Restore_Success\": MessageLookupByLibrary.simpleMessage(\n      \"Ripristino dati riuscito\",\n    ),\n    \"Restoring_Missing_Songs\": MessageLookupByLibrary.simpleMessage(\n      \"Ripristino brani mancanti...\",\n    ),\n    \"Retry\": MessageLookupByLibrary.simpleMessage(\"Riprova\"),\n    \"Rotate_Device\": MessageLookupByLibrary.simpleMessage(\n      \"Ruota il dispositivo per scrivere.\",\n    ),\n    \"Save\": MessageLookupByLibrary.simpleMessage(\"Salva\"),\n    \"Saved\": MessageLookupByLibrary.simpleMessage(\"Salvati\"),\n    \"Search_Gyawun\": MessageLookupByLibrary.simpleMessage(\"Cerca in Gyawun\"),\n    \"Search_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Cronologia di ricerca eliminata\",\n    ),\n    \"Search_Settings\": MessageLookupByLibrary.simpleMessage(\n      \"Impostazioni di ricerca\",\n    ),\n    \"Select_Backup\": MessageLookupByLibrary.simpleMessage(\"Seleziona backup\"),\n    \"Select_Playlist_Icon\": MessageLookupByLibrary.simpleMessage(\n      \"Seleziona Icona Playlist\",\n    ),\n    \"Settings\": MessageLookupByLibrary.simpleMessage(\"Impostazioni\"),\n    \"Share\": MessageLookupByLibrary.simpleMessage(\"Condividi\"),\n    \"Sheikh_Haziq\": MessageLookupByLibrary.simpleMessage(\"Sheikh Haziq\"),\n    \"Show_Less\": MessageLookupByLibrary.simpleMessage(\"Mostra meno\"),\n    \"Show_More\": MessageLookupByLibrary.simpleMessage(\"Mostra di più\"),\n    \"Shuffle\": MessageLookupByLibrary.simpleMessage(\"Casuale\"),\n    \"Skip_Silence\": MessageLookupByLibrary.simpleMessage(\"Salta silenzi\"),\n    \"Sleep_Timer\": MessageLookupByLibrary.simpleMessage(\"Timer di spegnimento\"),\n    \"Songs\": MessageLookupByLibrary.simpleMessage(\"Brani\"),\n    \"Songs_Will_Start_Playing_Soon\": MessageLookupByLibrary.simpleMessage(\n      \"La riproduzione dei brani inizierà a breve.\",\n    ),\n    \"Source_Code\": MessageLookupByLibrary.simpleMessage(\"Codice sorgente\"),\n    \"Start_Radio\": MessageLookupByLibrary.simpleMessage(\"Avvia radio\"),\n    \"Streaming_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"Qualità streaming\",\n    ),\n    \"Subscriptions\": MessageLookupByLibrary.simpleMessage(\"Iscrizioni\"),\n    \"Support_Me_On_Kofi\": MessageLookupByLibrary.simpleMessage(\n      \"Supportami su Ko-fi\",\n    ),\n    \"Telegram\": MessageLookupByLibrary.simpleMessage(\"Telegram\"),\n    \"Theme_Mode\": MessageLookupByLibrary.simpleMessage(\"Tema\"),\n    \"Top_Results\": MessageLookupByLibrary.simpleMessage(\"Risultati Principali\"),\n    \"Translate_Lyrics\": MessageLookupByLibrary.simpleMessage(\"Traduci testi\"),\n    \"Version\": MessageLookupByLibrary.simpleMessage(\"Versione\"),\n    \"View_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Riproduci un brano per vedere l\\'equalizzatore.\",\n    ),\n    \"Visitor_Id\": MessageLookupByLibrary.simpleMessage(\"Visitor ID\"),\n    \"Window_Effect\": MessageLookupByLibrary.simpleMessage(\"Effetto finestra\"),\n    \"YTMusic\": MessageLookupByLibrary.simpleMessage(\"YTMusic\"),\n    \"Yes\": MessageLookupByLibrary.simpleMessage(\"Sì\"),\n    \"nSongs\": m1,\n  };\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_tr.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a tr locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'tr';\n\n  static String m1(count) =>\n      \"${Intl.plural(count, zero: 'Şarkı Yok', one: '1 Şarkı', other: '${count} Şarkı')}\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"About\": MessageLookupByLibrary.simpleMessage(\"Hakkında\"),\n    \"Add_To_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Favorilere Ekle\",\n    ),\n    \"Add_To_Library\": MessageLookupByLibrary.simpleMessage(\"Kütüphaneye Ekle\"),\n    \"Add_To_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Çalma Listesine Ekle\",\n    ),\n    \"Add_To_Queue\": MessageLookupByLibrary.simpleMessage(\"Kuyruğa Ekle\"),\n    \"Album\": MessageLookupByLibrary.simpleMessage(\"Albüm\"),\n    \"Albums\": MessageLookupByLibrary.simpleMessage(\"Albümler\"),\n    \"Appearence\": MessageLookupByLibrary.simpleMessage(\"Görünüm\"),\n    \"Artists\": MessageLookupByLibrary.simpleMessage(\"Sanatçılar\"),\n    \"Audio_And_Playback\": MessageLookupByLibrary.simpleMessage(\n      \"Ses ve Yeniden Oynatma\",\n    ),\n    \"Backup\": MessageLookupByLibrary.simpleMessage(\"Yedekle\"),\n    \"Backup_And_Restore\": MessageLookupByLibrary.simpleMessage(\n      \"Yedekle ve Geri Yükle\",\n    ),\n    \"Battery_Optimisation_message\": MessageLookupByLibrary.simpleMessage(\n      \"Gyawun un düzgün çalışması için pil optimizasyonunu devre dışı bırakmak için buraya tıklayın\",\n    ),\n    \"Battery_Optimisation_title\": MessageLookupByLibrary.simpleMessage(\n      \"Pil Optimizasyonu Tespit Edildi\",\n    ),\n    \"Bug_Report\": MessageLookupByLibrary.simpleMessage(\"Hata Raporu\"),\n    \"Buy_Me_A_Coffee\": MessageLookupByLibrary.simpleMessage(\n      \"Bana Bir Kahve Al\",\n    ),\n    \"Cancel\": MessageLookupByLibrary.simpleMessage(\"İptal\"),\n    \"Check_For_Update\": MessageLookupByLibrary.simpleMessage(\n      \"Güncellemeleri Kontrol Et\",\n    ),\n    \"Confirm\": MessageLookupByLibrary.simpleMessage(\"Onayla\"),\n    \"Content\": MessageLookupByLibrary.simpleMessage(\"İçerik\"),\n    \"Contributors\": MessageLookupByLibrary.simpleMessage(\"Katkıda Bulunanlar\"),\n    \"Copied_To_Clipboard\": MessageLookupByLibrary.simpleMessage(\n      \"Panoya Kopyalandı\",\n    ),\n    \"Country\": MessageLookupByLibrary.simpleMessage(\"Ülke\"),\n    \"Create\": MessageLookupByLibrary.simpleMessage(\"Oluştur\"),\n    \"Create_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Çalma Listesi Oluştur\",\n    ),\n    \"DOwnload_Quality\": MessageLookupByLibrary.simpleMessage(\n      \"İndirme Kalitesi\",\n    ),\n    \"Delete_Item_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Bu öğeyi silmek istediğinizden emin misiniz?\",\n    ),\n    \"Delete_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Yeniden Oynatma Geçmişini Sil\",\n    ),\n    \"Delete_Playback_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"Yeniden Oynatma Geçmişini silmek istediğinizden emin misiniz.\",\n        ),\n    \"Delete_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Arama Geçmişini Sil\",\n    ),\n    \"Delete_Search_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"Arama Geçmişini silmek istediğinizden emin misiniz.\",\n        ),\n    \"Developer\": MessageLookupByLibrary.simpleMessage(\"Geliştirici\"),\n    \"Donate\": MessageLookupByLibrary.simpleMessage(\"Bağış Yap\"),\n    \"Donate_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Gyawun un geliştirilmesini destekleyin\",\n    ),\n    \"Done\": MessageLookupByLibrary.simpleMessage(\"Tamam\"),\n    \"Download\": MessageLookupByLibrary.simpleMessage(\"İndir\"),\n    \"Downloads\": MessageLookupByLibrary.simpleMessage(\"İndirilenler\"),\n    \"Dynamic_Colors\": MessageLookupByLibrary.simpleMessage(\"Dinamik Renkler\"),\n    \"Enable_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Ekolayzeri Etkinleştir\",\n    ),\n    \"Enable_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"Yeniden Oynatma Geçmişini Etkinleştir\",\n    ),\n    \"Enable_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"Arama Geçmişini Etkinleştir\",\n    ),\n    \"Enter_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Ziyaretçi Kimliği Girin\",\n    ),\n    \"Equalizer\": MessageLookupByLibrary.simpleMessage(\"Ekolayzer\"),\n    \"Favourites\": MessageLookupByLibrary.simpleMessage(\"Favoriler\"),\n    \"Feature_Request\": MessageLookupByLibrary.simpleMessage(\"Özellik İsteği\"),\n    \"Google_Account\": MessageLookupByLibrary.simpleMessage(\"Google Hesabı\"),\n    \"Gyawun\": MessageLookupByLibrary.simpleMessage(\"Gyawun\"),\n    \"High\": MessageLookupByLibrary.simpleMessage(\"Yüksek\"),\n    \"History\": MessageLookupByLibrary.simpleMessage(\"Geçmiş\"),\n    \"Home\": MessageLookupByLibrary.simpleMessage(\"Ana Sayfa\"),\n    \"Import\": MessageLookupByLibrary.simpleMessage(\"İçe Aktar\"),\n    \"Import_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Çalma Listesi İçe Aktar\",\n    ),\n    \"Jhelum_Corp\": MessageLookupByLibrary.simpleMessage(\"Jhelum Corp\"),\n    \"Language\": MessageLookupByLibrary.simpleMessage(\"Dil\"),\n    \"Loudness_And_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"Ses Yüksekliği ve Ekolayzer\",\n    ),\n    \"Loudness_Enhancer\": MessageLookupByLibrary.simpleMessage(\"Ses Yükseltici\"),\n    \"Low\": MessageLookupByLibrary.simpleMessage(\"Düşük\"),\n    \"Made_In_Kashmir\": MessageLookupByLibrary.simpleMessage(\n      \"Keşmir de Yapıldı\",\n    ),\n    \"Name\": MessageLookupByLibrary.simpleMessage(\"İsim\"),\n    \"Next_Up\": MessageLookupByLibrary.simpleMessage(\"Sıradaki\"),\n    \"No\": MessageLookupByLibrary.simpleMessage(\"Hayır\"),\n    \"Organisation\": MessageLookupByLibrary.simpleMessage(\"Organizasyon\"),\n    \"Pay_With_UPI\": MessageLookupByLibrary.simpleMessage(\"UPI ile Öde\"),\n    \"Payment_Methods\": MessageLookupByLibrary.simpleMessage(\"Ödeme Yöntemleri\"),\n    \"Personalised_Content\": MessageLookupByLibrary.simpleMessage(\n      \"Kişiselleştirilmiş İçerik\",\n    ),\n    \"Play_Next\": MessageLookupByLibrary.simpleMessage(\"Sonraki Oynat\"),\n    \"Playback_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Yeniden Oynatma Geçmişi Silindi\",\n    ),\n    \"Playlist_Name\": MessageLookupByLibrary.simpleMessage(\"Çalma Listesi Adı\"),\n    \"Playlists\": MessageLookupByLibrary.simpleMessage(\"Çalma Listeleri\"),\n    \"Progress\": MessageLookupByLibrary.simpleMessage(\"İlerleme\"),\n    \"Remove\": MessageLookupByLibrary.simpleMessage(\"Kaldır\"),\n    \"Remove_All_History_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Tüm geçmişi temizlemek istediğinizden emin misiniz?\",\n    ),\n    \"Remove_From_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"Favorilerden Çıkar\",\n    ),\n    \"Remove_From_Library\": MessageLookupByLibrary.simpleMessage(\n      \"Kütüphaneden Çıkar\",\n    ),\n    \"Remove_From_YTMusic_Message\": MessageLookupByLibrary.simpleMessage(\n      \"YTMüzikten kaldırmak istediğinizden emin misiniz?\",\n    ),\n    \"Remove_Message\": MessageLookupByLibrary.simpleMessage(\n      \"Kaldırmak istediğinizden emin misiniz?\",\n    ),\n    \"Rename\": MessageLookupByLibrary.simpleMessage(\"Yeniden Adlandır\"),\n    \"Rename_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"Çalma Listesini Yeniden Adlandır\",\n    ),\n    \"Reset_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"Ziyaretçi Kimliğini Sıfırla\",\n    ),\n    \"Restore\": MessageLookupByLibrary.simpleMessage(\"Geri Yükle\"),\n    \"Saved\": MessageLookupByLibrary.simpleMessage(\"Kaydedilenler\"),\n    \"Search_Gyawun\": MessageLookupByLibrary.simpleMessage(\"Gyawun Ara\"),\n    \"Search_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"Arama Geçmişi Silindi\",\n    ),\n    \"Search_Settings\": MessageLookupByLibrary.simpleMessage(\"Arama Ayarları\"),\n    \"Select_Backup\": MessageLookupByLibrary.simpleMessage(\"Yedeği Seç\"),\n    \"Settings\": MessageLookupByLibrary.simpleMessage(\"Ayarlar\"),\n    \"Sheikh_Haziq\": MessageLookupByLibrary.simpleMessage(\"Sheikh Haziq\"),\n    \"Show_Less\": MessageLookupByLibrary.simpleMessage(\"Daha Az Göster\"),\n    \"Show_More\": MessageLookupByLibrary.simpleMessage(\"Daha Fazla Göster\"),\n    \"Shuffle\": MessageLookupByLibrary.simpleMessage(\"Karıştır\"),\n    \"Skip_Silence\": MessageLookupByLibrary.simpleMessage(\"Sessizliği Atla\"),\n    \"Sleep_Timer\": MessageLookupByLibrary.simpleMessage(\"Uyku Zamanlayıcı\"),\n    \"Songs\": MessageLookupByLibrary.simpleMessage(\"Şarkılar\"),\n    \"Songs_Will_Start_Playing_Soon\": MessageLookupByLibrary.simpleMessage(\n      \"Şarkılar yakında çalmaya başlayacak.\",\n    ),\n    \"Source_Code\": MessageLookupByLibrary.simpleMessage(\"Kaynak Kodu\"),\n    \"Start_Radio\": MessageLookupByLibrary.simpleMessage(\"Radyo Başlat\"),\n    \"Streaming_Quality\": MessageLookupByLibrary.simpleMessage(\"Yayın Kalitesi\"),\n    \"Subscriptions\": MessageLookupByLibrary.simpleMessage(\"Abonelikler\"),\n    \"Support_Me_On_Kofi\": MessageLookupByLibrary.simpleMessage(\n      \"Ko-fi üzerinden bana destek ol\",\n    ),\n    \"Telegram\": MessageLookupByLibrary.simpleMessage(\"Telegram\"),\n    \"Theme_Mode\": MessageLookupByLibrary.simpleMessage(\"Tema Modu\"),\n    \"Version\": MessageLookupByLibrary.simpleMessage(\"Versiyon\"),\n    \"Visitor_Id\": MessageLookupByLibrary.simpleMessage(\"Ziyaretçi Kimliği\"),\n    \"Window_Effect\": MessageLookupByLibrary.simpleMessage(\"Pencere Efekti\"),\n    \"YTMusic\": MessageLookupByLibrary.simpleMessage(\"YTMüzik\"),\n    \"Yes\": MessageLookupByLibrary.simpleMessage(\"Evet\"),\n    \"nSongs\": m1,\n  };\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_ur.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a ur locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'ur';\n\n  static String m1(count) =>\n      \"${Intl.plural(count, zero: 'کوئی گانے نہیں', one: '1 گانا', other: '${count} گانے')}\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"About\": MessageLookupByLibrary.simpleMessage(\"کے بارے میں\"),\n    \"Add_To_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"پسندیدہ میں شامل کریں\",\n    ),\n    \"Add_To_Library\": MessageLookupByLibrary.simpleMessage(\n      \"لائبریری میں شامل کریں\",\n    ),\n    \"Add_To_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"پلے لسٹ میں شامل کریں\",\n    ),\n    \"Add_To_Queue\": MessageLookupByLibrary.simpleMessage(\"قطار میں شامل کریں\"),\n    \"Album\": MessageLookupByLibrary.simpleMessage(\"البم\"),\n    \"Albums\": MessageLookupByLibrary.simpleMessage(\"البمز\"),\n    \"Appearence\": MessageLookupByLibrary.simpleMessage(\"ظاہری شکل\"),\n    \"Artists\": MessageLookupByLibrary.simpleMessage(\"فنکار\"),\n    \"Audio_And_Playback\": MessageLookupByLibrary.simpleMessage(\n      \"آڈیو اور پلے بیک\",\n    ),\n    \"Backup\": MessageLookupByLibrary.simpleMessage(\"بیک اپ\"),\n    \"Backup_And_Restore\": MessageLookupByLibrary.simpleMessage(\n      \"بیک اپ اور بحالی\",\n    ),\n    \"Battery_Optimisation_message\": MessageLookupByLibrary.simpleMessage(\n      \"گیاون کو درست طریقے سے کام کرنے کے لئے بیٹری کی اصلاح کو غیر فعال کرنے کے لئے یہاں کلک کریں\",\n    ),\n    \"Battery_Optimisation_title\": MessageLookupByLibrary.simpleMessage(\n      \"بیٹری کی اصلاح کا پتہ چلا\",\n    ),\n    \"Bug_Report\": MessageLookupByLibrary.simpleMessage(\"بگ رپورٹ\"),\n    \"Buy_Me_A_Coffee\": MessageLookupByLibrary.simpleMessage(\n      \"مجھے ایک کافی خریدیں\",\n    ),\n    \"Cancel\": MessageLookupByLibrary.simpleMessage(\"منسوخ کریں\"),\n    \"Check_For_Update\": MessageLookupByLibrary.simpleMessage(\n      \"اپ ڈیٹ کے لئے چیک کریں\",\n    ),\n    \"Confirm\": MessageLookupByLibrary.simpleMessage(\"تصدیق کریں\"),\n    \"Content\": MessageLookupByLibrary.simpleMessage(\"مواد\"),\n    \"Contributors\": MessageLookupByLibrary.simpleMessage(\"شراکت دار\"),\n    \"Copied_To_Clipboard\": MessageLookupByLibrary.simpleMessage(\n      \"کلپ بورڈ پر کاپی کر دیا گیا\",\n    ),\n    \"Country\": MessageLookupByLibrary.simpleMessage(\"ملک\"),\n    \"Create\": MessageLookupByLibrary.simpleMessage(\"بنائیں\"),\n    \"Create_Playlist\": MessageLookupByLibrary.simpleMessage(\"پلے لسٹ بنائیں\"),\n    \"DOwnload_Quality\": MessageLookupByLibrary.simpleMessage(\"ڈاؤن لوڈ کوالٹی\"),\n    \"Delete_Item_Message\": MessageLookupByLibrary.simpleMessage(\n      \"کیا آپ واقعی اس آئٹم کو حذف کرنا چاہتے ہیں؟\",\n    ),\n    \"Delete_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"پلے بیک ہسٹری کو حذف کریں\",\n    ),\n    \"Delete_Playback_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"کیا آپ واقعی پلے بیک ہسٹری کو حذف کرنا چاہتے ہیں؟\",\n        ),\n    \"Delete_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"تلاش کی ہسٹری کو حذف کریں\",\n    ),\n    \"Delete_Search_History_Confirm_Message\":\n        MessageLookupByLibrary.simpleMessage(\n          \"کیا آپ واقعی تلاش کی ہسٹری کو حذف کرنا چاہتے ہیں؟\",\n        ),\n    \"Developer\": MessageLookupByLibrary.simpleMessage(\"ڈویلپر\"),\n    \"Donate\": MessageLookupByLibrary.simpleMessage(\"عطیہ\"),\n    \"Donate_Message\": MessageLookupByLibrary.simpleMessage(\n      \"گیاون کی ترقی کی حمایت کریں\",\n    ),\n    \"Done\": MessageLookupByLibrary.simpleMessage(\"ہو گیا\"),\n    \"Download\": MessageLookupByLibrary.simpleMessage(\"ڈاؤن لوڈ\"),\n    \"Downloads\": MessageLookupByLibrary.simpleMessage(\"ڈاؤن لوڈ\"),\n    \"Dynamic_Colors\": MessageLookupByLibrary.simpleMessage(\"ڈائنامک رنگ\"),\n    \"Enable_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"ایکوالائزر کو فعال کریں\",\n    ),\n    \"Enable_Playback_History\": MessageLookupByLibrary.simpleMessage(\n      \"پلے بیک ہسٹری کو فعال کریں\",\n    ),\n    \"Enable_Search_History\": MessageLookupByLibrary.simpleMessage(\n      \"تلاش کی ہسٹری کو فعال کریں\",\n    ),\n    \"Enter_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"وزیٹر آئی ڈی درج کریں\",\n    ),\n    \"Equalizer\": MessageLookupByLibrary.simpleMessage(\"ایکولائزر\"),\n    \"Favourites\": MessageLookupByLibrary.simpleMessage(\"پسندیدہ\"),\n    \"Feature_Request\": MessageLookupByLibrary.simpleMessage(\n      \"خصوصیت کی درخواست\",\n    ),\n    \"Google_Account\": MessageLookupByLibrary.simpleMessage(\"گوگل اکاؤنٹ\"),\n    \"Gyawun\": MessageLookupByLibrary.simpleMessage(\"گیاون\"),\n    \"High\": MessageLookupByLibrary.simpleMessage(\"زیادہ\"),\n    \"History\": MessageLookupByLibrary.simpleMessage(\"تاریخ\"),\n    \"Home\": MessageLookupByLibrary.simpleMessage(\"ہوم\"),\n    \"Import\": MessageLookupByLibrary.simpleMessage(\"درآمد\"),\n    \"Import_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"پلے لسٹ درآمد کریں\",\n    ),\n    \"Jhelum_Corp\": MessageLookupByLibrary.simpleMessage(\"جہلم کارپ\"),\n    \"Language\": MessageLookupByLibrary.simpleMessage(\"زبان\"),\n    \"Loudness_And_Equalizer\": MessageLookupByLibrary.simpleMessage(\n      \"لاؤڈنس اور ایکوالائزر\",\n    ),\n    \"Loudness_Enhancer\": MessageLookupByLibrary.simpleMessage(\"لاؤڈنس انہینسر\"),\n    \"Low\": MessageLookupByLibrary.simpleMessage(\"کم\"),\n    \"Made_In_Kashmir\": MessageLookupByLibrary.simpleMessage(\n      \"کشمیر میں بنایا گیا\",\n    ),\n    \"Name\": MessageLookupByLibrary.simpleMessage(\"نام\"),\n    \"Next_Up\": MessageLookupByLibrary.simpleMessage(\"اگلا\"),\n    \"No\": MessageLookupByLibrary.simpleMessage(\"نہیں\"),\n    \"Organisation\": MessageLookupByLibrary.simpleMessage(\"تنظیم\"),\n    \"Pay_With_UPI\": MessageLookupByLibrary.simpleMessage(\n      \"یو پی آئی کے ساتھ ادائیگی\",\n    ),\n    \"Payment_Methods\": MessageLookupByLibrary.simpleMessage(\"ادائیگی کے طریقے\"),\n    \"Personalised_Content\": MessageLookupByLibrary.simpleMessage(\n      \"ذاتی بنایا گیا مواد\",\n    ),\n    \"Play_Next\": MessageLookupByLibrary.simpleMessage(\"اگلا چلائیں\"),\n    \"Playback_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"پلے بیک ہسٹری حذف کر دی گئی\",\n    ),\n    \"Playlist_Name\": MessageLookupByLibrary.simpleMessage(\"پلے لسٹ کا نام\"),\n    \"Playlists\": MessageLookupByLibrary.simpleMessage(\"پلے لسٹس\"),\n    \"Progress\": MessageLookupByLibrary.simpleMessage(\"پیش رفت\"),\n    \"Remove\": MessageLookupByLibrary.simpleMessage(\"ہٹائیں\"),\n    \"Remove_All_History_Message\": MessageLookupByLibrary.simpleMessage(\n      \"کیا آپ واقعی تمام تاریخ کو صاف کرنا چاہتے ہیں؟\",\n    ),\n    \"Remove_From_Favourites\": MessageLookupByLibrary.simpleMessage(\n      \"پسندیدہ سے ہٹائیں\",\n    ),\n    \"Remove_From_Library\": MessageLookupByLibrary.simpleMessage(\n      \"لائبریری سے ہٹائیں\",\n    ),\n    \"Remove_From_YTMusic_Message\": MessageLookupByLibrary.simpleMessage(\n      \"کیا آپ واقعی اسے YTMusic سے ہٹانا چاہتے ہیں؟\",\n    ),\n    \"Remove_Message\": MessageLookupByLibrary.simpleMessage(\n      \"کیا آپ واقعی اسے ہٹانا چاہتے ہیں؟\",\n    ),\n    \"Rename\": MessageLookupByLibrary.simpleMessage(\"نام تبدیل کریں\"),\n    \"Rename_Playlist\": MessageLookupByLibrary.simpleMessage(\n      \"پلے لسٹ کا نام تبدیل کریں\",\n    ),\n    \"Reset_Visitor_Id\": MessageLookupByLibrary.simpleMessage(\n      \"وزیٹر آئی ڈی دوبارہ ترتیب دیں\",\n    ),\n    \"Restore\": MessageLookupByLibrary.simpleMessage(\"بحال کریں\"),\n    \"Saved\": MessageLookupByLibrary.simpleMessage(\"محفوظ کردہ\"),\n    \"Search_Gyawun\": MessageLookupByLibrary.simpleMessage(\"گیاون تلاش کریں\"),\n    \"Search_History_Deleted\": MessageLookupByLibrary.simpleMessage(\n      \"تلاش کی ہسٹری حذف کر دی گئی\",\n    ),\n    \"Search_Settings\": MessageLookupByLibrary.simpleMessage(\"تلاش کی ترتیبات\"),\n    \"Select_Backup\": MessageLookupByLibrary.simpleMessage(\"بیک اپ منتخب کریں\"),\n    \"Settings\": MessageLookupByLibrary.simpleMessage(\"ترتیبات\"),\n    \"Sheikh_Haziq\": MessageLookupByLibrary.simpleMessage(\"شیخ ہازق\"),\n    \"Show_Less\": MessageLookupByLibrary.simpleMessage(\"کم دکھائیں\"),\n    \"Show_More\": MessageLookupByLibrary.simpleMessage(\"مزید دکھائیں\"),\n    \"Shuffle\": MessageLookupByLibrary.simpleMessage(\"بے ترتیب\"),\n    \"Skip_Silence\": MessageLookupByLibrary.simpleMessage(\"خاموشی کو چھوڑ دیں\"),\n    \"Sleep_Timer\": MessageLookupByLibrary.simpleMessage(\"سلیپ ٹائمر\"),\n    \"Songs\": MessageLookupByLibrary.simpleMessage(\"گانے\"),\n    \"Songs_Will_Start_Playing_Soon\": MessageLookupByLibrary.simpleMessage(\n      \"گانے جلد ہی چلنا شروع ہوجائیں گے۔\",\n    ),\n    \"Source_Code\": MessageLookupByLibrary.simpleMessage(\"سورس کوڈ\"),\n    \"Start_Radio\": MessageLookupByLibrary.simpleMessage(\"ریڈیو شروع کریں\"),\n    \"Streaming_Quality\": MessageLookupByLibrary.simpleMessage(\"سٹریمنگ کوالٹی\"),\n    \"Subscriptions\": MessageLookupByLibrary.simpleMessage(\"سبسکرپشنز\"),\n    \"Support_Me_On_Kofi\": MessageLookupByLibrary.simpleMessage(\n      \"کوفی پر مجھے سپورٹ کریں\",\n    ),\n    \"Telegram\": MessageLookupByLibrary.simpleMessage(\"ٹیلیگرام\"),\n    \"Theme_Mode\": MessageLookupByLibrary.simpleMessage(\"تھیم موڈ\"),\n    \"Version\": MessageLookupByLibrary.simpleMessage(\"ورژن\"),\n    \"Visitor_Id\": MessageLookupByLibrary.simpleMessage(\"وزیٹر آئی ڈی\"),\n    \"Window_Effect\": MessageLookupByLibrary.simpleMessage(\"ونڈو ایفیکٹ\"),\n    \"YTMusic\": MessageLookupByLibrary.simpleMessage(\"وائی ٹی میوزک\"),\n    \"Yes\": MessageLookupByLibrary.simpleMessage(\"ہاں\"),\n    \"nSongs\": m1,\n  };\n}\n"
  },
  {
    "path": "lib/generated/l10n.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\nimport 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\nimport 'intl/messages_all.dart';\n\n// **************************************************************************\n// Generator: Flutter Intl IDE plugin\n// Made by Localizely\n// **************************************************************************\n\n// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars\n// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each\n// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes\n\nclass S {\n  S();\n\n  static S? _current;\n\n  static S get current {\n    assert(\n      _current != null,\n      'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.',\n    );\n    return _current!;\n  }\n\n  static const AppLocalizationDelegate delegate = AppLocalizationDelegate();\n\n  static Future<S> load(Locale locale) {\n    final name = (locale.countryCode?.isEmpty ?? false)\n        ? locale.languageCode\n        : locale.toString();\n    final localeName = Intl.canonicalizedLocale(name);\n    return initializeMessages(localeName).then((_) {\n      Intl.defaultLocale = localeName;\n      final instance = S();\n      S._current = instance;\n\n      return instance;\n    });\n  }\n\n  static S of(BuildContext context) {\n    final instance = S.maybeOf(context);\n    assert(\n      instance != null,\n      'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?',\n    );\n    return instance!;\n  }\n\n  static S? maybeOf(BuildContext context) {\n    return Localizations.of<S>(context, S);\n  }\n\n  /// `Gyawun`\n  String get Gyawun {\n    return Intl.message('Gyawun', name: 'Gyawun', desc: '', args: []);\n  }\n\n  /// `Next Up`\n  String get Next_Up {\n    return Intl.message('Next Up', name: 'Next_Up', desc: '', args: []);\n  }\n\n  /// `Shuffle`\n  String get Shuffle {\n    return Intl.message('Shuffle', name: 'Shuffle', desc: '', args: []);\n  }\n\n  /// `Home`\n  String get Home {\n    return Intl.message('Home', name: 'Home', desc: '', args: []);\n  }\n\n  /// `Saved`\n  String get Saved {\n    return Intl.message('Saved', name: 'Saved', desc: '', args: []);\n  }\n\n  /// `YTMusic`\n  String get YTMusic {\n    return Intl.message('YTMusic', name: 'YTMusic', desc: '', args: []);\n  }\n\n  /// `Settings`\n  String get Settings {\n    return Intl.message('Settings', name: 'Settings', desc: '', args: []);\n  }\n\n  /// `Search Gyawun`\n  String get Search_Gyawun {\n    return Intl.message(\n      'Search Gyawun',\n      name: 'Search_Gyawun',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Favourites`\n  String get Favourites {\n    return Intl.message('Favourites', name: 'Favourites', desc: '', args: []);\n  }\n\n  /// `Downloads`\n  String get Downloads {\n    return Intl.message('Downloads', name: 'Downloads', desc: '', args: []);\n  }\n\n  /// `History`\n  String get History {\n    return Intl.message('History', name: 'History', desc: '', args: []);\n  }\n\n  /// `{count, plural, =0{No Songs} =1{1 Song} other{{count} Songs}}`\n  String nSongs(num count) {\n    return Intl.plural(\n      count,\n      zero: 'No Songs',\n      one: '1 Song',\n      other: '$count Songs',\n      name: 'nSongs',\n      desc: 'Number of songs',\n      args: [count],\n    );\n  }\n\n  /// `Songs`\n  String get Songs {\n    return Intl.message('Songs', name: 'Songs', desc: '', args: []);\n  }\n\n  /// `Albums`\n  String get Albums {\n    return Intl.message('Albums', name: 'Albums', desc: '', args: []);\n  }\n\n  /// `Playlists`\n  String get Playlists {\n    return Intl.message('Playlists', name: 'Playlists', desc: '', args: []);\n  }\n\n  /// `Artists`\n  String get Artists {\n    return Intl.message('Artists', name: 'Artists', desc: '', args: []);\n  }\n\n  /// `Subscriptions`\n  String get Subscriptions {\n    return Intl.message(\n      'Subscriptions',\n      name: 'Subscriptions',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Search Settings`\n  String get Search_Settings {\n    return Intl.message(\n      'Search Settings',\n      name: 'Search_Settings',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Battery Optimisation Detected`\n  String get Battery_Optimisation_title {\n    return Intl.message(\n      'Battery Optimisation Detected',\n      name: 'Battery_Optimisation_title',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Click here disable battery optimisation for Gyawun to work properly`\n  String get Battery_Optimisation_message {\n    return Intl.message(\n      'Click here disable battery optimisation for Gyawun to work properly',\n      name: 'Battery_Optimisation_message',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Donate`\n  String get Donate {\n    return Intl.message('Donate', name: 'Donate', desc: '', args: []);\n  }\n\n  /// `Support the development of Gyawun`\n  String get Donate_Message {\n    return Intl.message(\n      'Support the development of Gyawun',\n      name: 'Donate_Message',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Payment Methods`\n  String get Payment_Methods {\n    return Intl.message(\n      'Payment Methods',\n      name: 'Payment_Methods',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Pay with UPI`\n  String get Pay_With_UPI {\n    return Intl.message(\n      'Pay with UPI',\n      name: 'Pay_With_UPI',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Support me on Ko-fi`\n  String get Support_Me_On_Kofi {\n    return Intl.message(\n      'Support me on Ko-fi',\n      name: 'Support_Me_On_Kofi',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Buy me a Coffee`\n  String get Buy_Me_A_Coffee {\n    return Intl.message(\n      'Buy me a Coffee',\n      name: 'Buy_Me_A_Coffee',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Google Account`\n  String get Google_Account {\n    return Intl.message(\n      'Google Account',\n      name: 'Google_Account',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Appearence`\n  String get Appearence {\n    return Intl.message('Appearence', name: 'Appearence', desc: '', args: []);\n  }\n\n  /// `Theme Mode`\n  String get Theme_Mode {\n    return Intl.message('Theme Mode', name: 'Theme_Mode', desc: '', args: []);\n  }\n\n  /// `Window Effect`\n  String get Window_Effect {\n    return Intl.message(\n      'Window Effect',\n      name: 'Window_Effect',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Dynamic Colors`\n  String get Dynamic_Colors {\n    return Intl.message(\n      'Dynamic Colors',\n      name: 'Dynamic_Colors',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Content`\n  String get Content {\n    return Intl.message('Content', name: 'Content', desc: '', args: []);\n  }\n\n  /// `Country`\n  String get Country {\n    return Intl.message('Country', name: 'Country', desc: '', args: []);\n  }\n\n  /// `Language`\n  String get Language {\n    return Intl.message('Language', name: 'Language', desc: '', args: []);\n  }\n\n  /// `Translate Lyrics`\n  String get Translate_Lyrics {\n    return Intl.message(\n      'Translate Lyrics',\n      name: 'Translate_Lyrics',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Autoplay Similar Songs`\n  String get Autofetch_Songs {\n    return Intl.message(\n      'Autoplay Similar Songs',\n      name: 'Autofetch_Songs',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Personalised Content`\n  String get Personalised_Content {\n    return Intl.message(\n      'Personalised Content',\n      name: 'Personalised_Content',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enter Visitor Id`\n  String get Enter_Visitor_Id {\n    return Intl.message(\n      'Enter Visitor Id',\n      name: 'Enter_Visitor_Id',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Visitor Id`\n  String get Visitor_Id {\n    return Intl.message('Visitor Id', name: 'Visitor_Id', desc: '', args: []);\n  }\n\n  /// `Reset Visitor Id`\n  String get Reset_Visitor_Id {\n    return Intl.message(\n      'Reset Visitor Id',\n      name: 'Reset_Visitor_Id',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Audio and Playback`\n  String get Audio_And_Playback {\n    return Intl.message(\n      'Audio and Playback',\n      name: 'Audio_And_Playback',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Loudness And Equalizer`\n  String get Loudness_And_Equalizer {\n    return Intl.message(\n      'Loudness And Equalizer',\n      name: 'Loudness_And_Equalizer',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Loudness Enhancer`\n  String get Loudness_Enhancer {\n    return Intl.message(\n      'Loudness Enhancer',\n      name: 'Loudness_Enhancer',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable Equalizer`\n  String get Enable_Equalizer {\n    return Intl.message(\n      'Enable Equalizer',\n      name: 'Enable_Equalizer',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Streaming Quality`\n  String get Streaming_Quality {\n    return Intl.message(\n      'Streaming Quality',\n      name: 'Streaming_Quality',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Download Quality`\n  String get DOwnload_Quality {\n    return Intl.message(\n      'Download Quality',\n      name: 'DOwnload_Quality',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `App Folder`\n  String get App_Folder {\n    return Intl.message('App Folder', name: 'App_Folder', desc: '', args: []);\n  }\n\n  /// `Skip Silence`\n  String get Skip_Silence {\n    return Intl.message(\n      'Skip Silence',\n      name: 'Skip_Silence',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable Playback History`\n  String get Enable_Playback_History {\n    return Intl.message(\n      'Enable Playback History',\n      name: 'Enable_Playback_History',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Delete Playback History`\n  String get Delete_Playback_History {\n    return Intl.message(\n      'Delete Playback History',\n      name: 'Delete_Playback_History',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Are you sure you want to delete Playback History.`\n  String get Delete_Playback_History_Confirm_Message {\n    return Intl.message(\n      'Are you sure you want to delete Playback History.',\n      name: 'Delete_Playback_History_Confirm_Message',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Playback History Deleted`\n  String get Playback_History_Deleted {\n    return Intl.message(\n      'Playback History Deleted',\n      name: 'Playback_History_Deleted',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable Search History`\n  String get Enable_Search_History {\n    return Intl.message(\n      'Enable Search History',\n      name: 'Enable_Search_History',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Delete Search History`\n  String get Delete_Search_History {\n    return Intl.message(\n      'Delete Search History',\n      name: 'Delete_Search_History',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Are you sure you want to delete Search History.`\n  String get Delete_Search_History_Confirm_Message {\n    return Intl.message(\n      'Are you sure you want to delete Search History.',\n      name: 'Delete_Search_History_Confirm_Message',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Search History Deleted`\n  String get Search_History_Deleted {\n    return Intl.message(\n      'Search History Deleted',\n      name: 'Search_History_Deleted',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Backup and Restore`\n  String get Backup_And_Restore {\n    return Intl.message(\n      'Backup and Restore',\n      name: 'Backup_And_Restore',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Backup`\n  String get Backup {\n    return Intl.message('Backup', name: 'Backup', desc: '', args: []);\n  }\n\n  /// `Restore`\n  String get Restore {\n    return Intl.message('Restore', name: 'Restore', desc: '', args: []);\n  }\n\n  /// `Share`\n  String get Share {\n    return Intl.message('Share', name: 'Share', desc: '', args: []);\n  }\n\n  /// `Save`\n  String get Save {\n    return Intl.message('Save', name: 'Save', desc: '', args: []);\n  }\n\n  /// `Backed up successfully at`\n  String get Backup_Success {\n    return Intl.message(\n      'Backed up successfully at',\n      name: 'Backup_Success',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Failed to back up Data`\n  String get Backup_Failed {\n    return Intl.message(\n      'Failed to back up Data',\n      name: 'Backup_Failed',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Data successfully restored`\n  String get Restore_Success {\n    return Intl.message(\n      'Data successfully restored',\n      name: 'Restore_Success',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Failed to restore Data`\n  String get Restore_Failed {\n    return Intl.message(\n      'Failed to restore Data',\n      name: 'Restore_Failed',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Select Backup`\n  String get Select_Backup {\n    return Intl.message(\n      'Select Backup',\n      name: 'Select_Backup',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `About`\n  String get About {\n    return Intl.message('About', name: 'About', desc: '', args: []);\n  }\n\n  /// `Name`\n  String get Name {\n    return Intl.message('Name', name: 'Name', desc: '', args: []);\n  }\n\n  /// `Version`\n  String get Version {\n    return Intl.message('Version', name: 'Version', desc: '', args: []);\n  }\n\n  /// `Developer`\n  String get Developer {\n    return Intl.message('Developer', name: 'Developer', desc: '', args: []);\n  }\n\n  /// `Sheikh Haziq`\n  String get Sheikh_Haziq {\n    return Intl.message(\n      'Sheikh Haziq',\n      name: 'Sheikh_Haziq',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Organisation`\n  String get Organisation {\n    return Intl.message(\n      'Organisation',\n      name: 'Organisation',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Jhelum Corp`\n  String get Jhelum_Corp {\n    return Intl.message('Jhelum Corp', name: 'Jhelum_Corp', desc: '', args: []);\n  }\n\n  /// `Telegram`\n  String get Telegram {\n    return Intl.message('Telegram', name: 'Telegram', desc: '', args: []);\n  }\n\n  /// `Contributors`\n  String get Contributors {\n    return Intl.message(\n      'Contributors',\n      name: 'Contributors',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Source Code`\n  String get Source_Code {\n    return Intl.message('Source Code', name: 'Source_Code', desc: '', args: []);\n  }\n\n  /// `Bug Report`\n  String get Bug_Report {\n    return Intl.message('Bug Report', name: 'Bug_Report', desc: '', args: []);\n  }\n\n  /// `Feature Request`\n  String get Feature_Request {\n    return Intl.message(\n      'Feature Request',\n      name: 'Feature_Request',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Made in Kashmir`\n  String get Made_In_Kashmir {\n    return Intl.message(\n      'Made in Kashmir',\n      name: 'Made_In_Kashmir',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Check for Update`\n  String get Check_For_Update {\n    return Intl.message(\n      'Check for Update',\n      name: 'Check_For_Update',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Progress`\n  String get Progress {\n    return Intl.message('Progress', name: 'Progress', desc: '', args: []);\n  }\n\n  /// `Play Next`\n  String get Play_Next {\n    return Intl.message('Play Next', name: 'Play_Next', desc: '', args: []);\n  }\n\n  /// `Add To Queue`\n  String get Add_To_Queue {\n    return Intl.message(\n      'Add To Queue',\n      name: 'Add_To_Queue',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Add To Favourites`\n  String get Add_To_Favourites {\n    return Intl.message(\n      'Add To Favourites',\n      name: 'Add_To_Favourites',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Remove From Favourites`\n  String get Remove_From_Favourites {\n    return Intl.message(\n      'Remove From Favourites',\n      name: 'Remove_From_Favourites',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Download`\n  String get Download {\n    return Intl.message('Download', name: 'Download', desc: '', args: []);\n  }\n\n  /// `Add To Playlist`\n  String get Add_To_Playlist {\n    return Intl.message(\n      'Add To Playlist',\n      name: 'Add_To_Playlist',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Start Radio`\n  String get Start_Radio {\n    return Intl.message('Start Radio', name: 'Start_Radio', desc: '', args: []);\n  }\n\n  /// `Album`\n  String get Album {\n    return Intl.message('Album', name: 'Album', desc: '', args: []);\n  }\n\n  /// `Rename`\n  String get Rename {\n    return Intl.message('Rename', name: 'Rename', desc: '', args: []);\n  }\n\n  /// `Add To Library`\n  String get Add_To_Library {\n    return Intl.message(\n      'Add To Library',\n      name: 'Add_To_Library',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Remove From Library`\n  String get Remove_From_Library {\n    return Intl.message(\n      'Remove From Library',\n      name: 'Remove_From_Library',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Are you sure you want to delete this item?`\n  String get Delete_Item_Message {\n    return Intl.message(\n      'Are you sure you want to delete this item?',\n      name: 'Delete_Item_Message',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Equalizer`\n  String get Equalizer {\n    return Intl.message('Equalizer', name: 'Equalizer', desc: '', args: []);\n  }\n\n  /// `Sleep Timer`\n  String get Sleep_Timer {\n    return Intl.message('Sleep Timer', name: 'Sleep_Timer', desc: '', args: []);\n  }\n\n  /// `Create Playlist`\n  String get Create_Playlist {\n    return Intl.message(\n      'Create Playlist',\n      name: 'Create_Playlist',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Playlist Name`\n  String get Playlist_Name {\n    return Intl.message(\n      'Playlist Name',\n      name: 'Playlist_Name',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Create`\n  String get Create {\n    return Intl.message('Create', name: 'Create', desc: '', args: []);\n  }\n\n  /// `Import Playlist`\n  String get Import_Playlist {\n    return Intl.message(\n      'Import Playlist',\n      name: 'Import_Playlist',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Import`\n  String get Import {\n    return Intl.message('Import', name: 'Import', desc: '', args: []);\n  }\n\n  /// `Rename Playlist`\n  String get Rename_Playlist {\n    return Intl.message(\n      'Rename Playlist',\n      name: 'Rename_Playlist',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Done`\n  String get Done {\n    return Intl.message('Done', name: 'Done', desc: '', args: []);\n  }\n\n  /// `Cancel`\n  String get Cancel {\n    return Intl.message('Cancel', name: 'Cancel', desc: '', args: []);\n  }\n\n  /// `Confirm`\n  String get Confirm {\n    return Intl.message('Confirm', name: 'Confirm', desc: '', args: []);\n  }\n\n  /// `Yes`\n  String get Yes {\n    return Intl.message('Yes', name: 'Yes', desc: '', args: []);\n  }\n\n  /// `No`\n  String get No {\n    return Intl.message('No', name: 'No', desc: '', args: []);\n  }\n\n  /// `Show More`\n  String get Show_More {\n    return Intl.message('Show More', name: 'Show_More', desc: '', args: []);\n  }\n\n  /// `Show Less`\n  String get Show_Less {\n    return Intl.message('Show Less', name: 'Show_Less', desc: '', args: []);\n  }\n\n  /// `Remove`\n  String get Remove {\n    return Intl.message('Remove', name: 'Remove', desc: '', args: []);\n  }\n\n  /// `High`\n  String get High {\n    return Intl.message('High', name: 'High', desc: '', args: []);\n  }\n\n  /// `Low`\n  String get Low {\n    return Intl.message('Low', name: 'Low', desc: '', args: []);\n  }\n\n  /// `Songs will start playing soon.`\n  String get Songs_Will_Start_Playing_Soon {\n    return Intl.message(\n      'Songs will start playing soon.',\n      name: 'Songs_Will_Start_Playing_Soon',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Are you sure you want to remove it?`\n  String get Remove_Message {\n    return Intl.message(\n      'Are you sure you want to remove it?',\n      name: 'Remove_Message',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Are you sure you want to remove it from YTMusic?`\n  String get Remove_From_YTMusic_Message {\n    return Intl.message(\n      'Are you sure you want to remove it from YTMusic?',\n      name: 'Remove_From_YTMusic_Message',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Are you sure you want to clear all history?`\n  String get Remove_All_History_Message {\n    return Intl.message(\n      'Are you sure you want to clear all history?',\n      name: 'Remove_All_History_Message',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Copied to Clipboard`\n  String get Copied_To_Clipboard {\n    return Intl.message(\n      'Copied to Clipboard',\n      name: 'Copied_To_Clipboard',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `No Internet Connection`\n  String get No_Internet_Connection {\n    return Intl.message(\n      'No Internet Connection',\n      name: 'No_Internet_Connection',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Go to Downloads`\n  String get Go_To_Downloads {\n    return Intl.message(\n      'Go to Downloads',\n      name: 'Go_To_Downloads',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Retry`\n  String get Retry {\n    return Intl.message('Retry', name: 'Retry', desc: '', args: []);\n  }\n\n  /// `Playlist not available`\n  String get Playlist_Not_Available {\n    return Intl.message(\n      'Playlist not available',\n      name: 'Playlist_Not_Available',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Are you sure you want to delete them?`\n  String get Confirm_Delete_All_Message {\n    return Intl.message(\n      'Are you sure you want to delete them?',\n      name: 'Confirm_Delete_All_Message',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Downloading`\n  String get Downloading {\n    return Intl.message('Downloading', name: 'Downloading', desc: '', args: []);\n  }\n\n  /// `Restore Missing Songs`\n  String get Restore_Missing_Songs {\n    return Intl.message(\n      'Restore Missing Songs',\n      name: 'Restore_Missing_Songs',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Delete All Songs`\n  String get Delete_All_Songs {\n    return Intl.message(\n      'Delete All Songs',\n      name: 'Delete_All_Songs',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Download started...`\n  String get Download_Started {\n    return Intl.message(\n      'Download started...',\n      name: 'Download_Started',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Restoring Missing Songs...`\n  String get Restoring_Missing_Songs {\n    return Intl.message(\n      'Restoring Missing Songs...',\n      name: 'Restoring_Missing_Songs',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Deleting Songs...`\n  String get Deleting_Songs {\n    return Intl.message(\n      'Deleting Songs...',\n      name: 'Deleting_Songs',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `In Progress`\n  String get In_Progress {\n    return Intl.message('In Progress', name: 'In_Progress', desc: '', args: []);\n  }\n\n  /// `Queued`\n  String get Queued {\n    return Intl.message('Queued', name: 'Queued', desc: '', args: []);\n  }\n\n  /// `Queued ({count})`\n  String Queued_Count(Object count) {\n    return Intl.message(\n      'Queued ($count)',\n      name: 'Queued_Count',\n      desc: '',\n      args: [count],\n    );\n  }\n\n  /// `File not found`\n  String get File_Not_Found {\n    return Intl.message(\n      'File not found',\n      name: 'File_Not_Found',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Play All`\n  String get Play_All {\n    return Intl.message('Play All', name: 'Play_All', desc: '', args: []);\n  }\n\n  /// `Rotate your device to type.`\n  String get Rotate_Device {\n    return Intl.message(\n      'Rotate your device to type.',\n      name: 'Rotate_Device',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Play a song to see the equalizer.`\n  String get View_Equalizer {\n    return Intl.message(\n      'Play a song to see the equalizer.',\n      name: 'View_Equalizer',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `No offline songs available`\n  String get No_Offline_Songs {\n    return Intl.message(\n      'No offline songs available',\n      name: 'No_Offline_Songs',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Edit`\n  String get Edit {\n    return Intl.message('Edit', name: 'Edit', desc: '', args: []);\n  }\n\n  /// `Edit Playlist`\n  String get Edit_Playlist {\n    return Intl.message(\n      'Edit Playlist',\n      name: 'Edit_Playlist',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Select Playlist Icon`\n  String get Select_Playlist_Icon {\n    return Intl.message(\n      'Select Playlist Icon',\n      name: 'Select_Playlist_Icon',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Top Results`\n  String get Top_Results {\n    return Intl.message('Top Results', name: 'Top_Results', desc: '', args: []);\n  }\n\n  /// `Other Results`\n  String get Other_Results {\n    return Intl.message(\n      'Other Results',\n      name: 'Other_Results',\n      desc: '',\n      args: [],\n    );\n  }\n}\n\nclass AppLocalizationDelegate extends LocalizationsDelegate<S> {\n  const AppLocalizationDelegate();\n\n  List<Locale> get supportedLocales {\n    return const <Locale>[\n      Locale.fromSubtags(languageCode: 'en'),\n      Locale.fromSubtags(languageCode: 'es'),\n      Locale.fromSubtags(languageCode: 'fr'),\n      Locale.fromSubtags(languageCode: 'hi'),\n      Locale.fromSubtags(languageCode: 'it'),\n      Locale.fromSubtags(languageCode: 'tr'),\n      Locale.fromSubtags(languageCode: 'ur'),\n    ];\n  }\n\n  @override\n  bool isSupported(Locale locale) => _isSupported(locale);\n  @override\n  Future<S> load(Locale locale) => S.load(locale);\n  @override\n  bool shouldReload(AppLocalizationDelegate old) => false;\n\n  bool _isSupported(Locale locale) {\n    for (var supportedLocale in supportedLocales) {\n      if (supportedLocale.languageCode == locale.languageCode) {\n        return true;\n      }\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "lib/l10n/intl_en.arb",
    "content": "{\n  \"@@locale\": \"en\",\n  \"Gyawun\": \"Gyawun\",\n  \"Next_Up\": \"Next Up\",\n  \"@Next_Up\": {},\n  \"Shuffle\": \"Shuffle\",\n  \"@Shuffle\": {},\n  \"@Gyawun\": {},\n  \"Home\": \"Home\",\n  \"@Home\": {},\n  \"Saved\": \"Saved\",\n  \"@Saved\": {},\n  \"YTMusic\": \"YTMusic\",\n  \"@YTMusic\": {},\n  \"Settings\": \"Settings\",\n  \"@Settings\": {},\n  \"Search_Gyawun\": \"Search Gyawun\",\n  \"@Search_Gyawun\": {},\n  \"Favourites\": \"Favourites\",\n  \"@Favourites\": {},\n  \"Downloads\": \"Downloads\",\n  \"@Downloads\": {},\n  \"History\": \"History\",\n  \"@History\": {},\n  \"nSongs\": \"{count, plural, =0{No Songs} =1{1 Song} other{{count} Songs}}\",\n  \"@nSongs\": {\n    \"description\": \"Number of songs\",\n    \"placeholders\": {\n      \"count\": {\n        \"type\": \"num\",\n        \"format\": \"compact\"\n      }\n    }\n  },\n  \"Songs\": \"Songs\",\n  \"@Songs\": {},\n  \"Albums\": \"Albums\",\n  \"@Albums\": {},\n  \"Playlists\": \"Playlists\",\n  \"@Playlists\": {},\n  \"Artists\": \"Artists\",\n  \"@Artists\": {},\n  \"Subscriptions\": \"Subscriptions\",\n  \"@Subscriptions\": {},\n  \"Search_Settings\": \"Search Settings\",\n  \"@Search_Settings\": {},\n  \"Battery_Optimisation_title\": \"Battery Optimisation Detected\",\n  \"@Battery_Optimisation_title\": {},\n  \"Battery_Optimisation_message\": \"Click here disable battery optimisation for Gyawun to work properly\",\n  \"@Battery_Optimisation_message\": {},\n  \"Donate\": \"Donate\",\n  \"@Donate\": {},\n  \"Donate_Message\": \"Support the development of Gyawun\",\n  \"@Donate_Message\": {},\n  \"Payment_Methods\": \"Payment Methods\",\n  \"@Payment_Methods\": {},\n  \"Pay_With_UPI\": \"Pay with UPI\",\n  \"@Pay_With_UPI\": {},\n  \"Support_Me_On_Kofi\": \"Support me on Ko-fi\",\n  \"@Support_Me_On_Kofi\": {},\n  \"Buy_Me_A_Coffee\": \"Buy me a Coffee\",\n  \"@Buy_Me_A_Coffee\": {},\n  \"Google_Account\": \"Google Account\",\n  \"@Google_Account\": {},\n  \"Appearence\": \"Appearence\",\n  \"Theme_Mode\": \"Theme Mode\",\n  \"@Theme_Mode\": {},\n  \"Window_Effect\": \"Window Effect\",\n  \"@Window_Effect\": {},\n  \"Dynamic_Colors\": \"Dynamic Colors\",\n  \"@Dynamic_Colors\": {},\n  \"@Appearence\": {},\n  \"Content\": \"Content\",\n  \"@Content\": {},\n  \"Country\": \"Country\",\n  \"@Country\": {},\n  \"Language\": \"Language\",\n  \"@Language\": {},\n  \"Translate_Lyrics\": \"Translate Lyrics\",\n  \"@Translate_Lyrics\": {},\n  \"Autofetch_Songs\": \"Autoplay Similar Songs\",\n  \"@Autofetch_Songs\": {},\n  \"Personalised_Content\": \"Personalised Content\",\n  \"@Personalised_Content\": {},\n  \"Enter_Visitor_Id\": \"Enter Visitor Id\",\n  \"@Enter_Visitor_Id\": {},\n  \"Visitor_Id\": \"Visitor Id\",\n  \"@Visitor_Id\": {},\n  \"Reset_Visitor_Id\": \"Reset Visitor Id\",\n  \"@Reset_Visitor_Id\": {},\n  \"Audio_And_Playback\": \"Audio and Playback\",\n  \"@Audio_And_Playback\": {},\n  \"Loudness_And_Equalizer\": \"Loudness And Equalizer\",\n  \"@Loudness_And_Equalizer\": {},\n  \"Loudness_Enhancer\": \"Loudness Enhancer\",\n  \"@Loudness_Enhancer\": {},\n  \"Enable_Equalizer\": \"Enable Equalizer\",\n  \"@Enable_Equalizer\": {},\n  \"Streaming_Quality\": \"Streaming Quality\",\n  \"@Streaming_Quality\": {},\n  \"DOwnload_Quality\": \"Download Quality\",\n  \"@DOwnload_Quality\": {},\n  \"App_Folder\": \"App Folder\",\n  \"@App_Folder\": {},\n  \"Skip_Silence\": \"Skip Silence\",\n  \"@Skip_Silence\": {},\n  \"Enable_Playback_History\": \"Enable Playback History\",\n  \"@Enable_Playback_History\": {},\n  \"Delete_Playback_History\": \"Delete Playback History\",\n  \"@Delete_Playback_History\": {},\n  \"Delete_Playback_History_Confirm_Message\": \"Are you sure you want to delete Playback History.\",\n  \"@Delete_Playback_History_Confirm_Message\": {},\n  \"Playback_History_Deleted\": \"Playback History Deleted\",\n  \"@Playback_History_Deleted\": {},\n  \"Enable_Search_History\": \"Enable Search History\",\n  \"@Enable_Search_History\": {},\n  \"Delete_Search_History\": \"Delete Search History\",\n  \"@Delete_Search_History\": {},\n  \"Delete_Search_History_Confirm_Message\": \"Are you sure you want to delete Search History.\",\n  \"@Delete_Search_History_Confirm_Message\": {},\n  \"Search_History_Deleted\": \"Search History Deleted\",\n  \"@Search_History_Deleted\": {},\n  \"Backup_And_Restore\": \"Backup and Restore\",\n  \"@Backup_And_Restore\": {},\n  \"Backup\": \"Backup\",\n  \"@Backup\": {},\n  \"Restore\": \"Restore\",\n  \"@Restore\": {},\n  \"Share\": \"Share\",\n  \"@Share\": {},\n  \"Save\": \"Save\",\n  \"@Save\": {},\n  \"Backup_Success\": \"Backed up successfully at\",\n  \"@Backup_Success\": {},\n  \"Backup_Failed\": \"Failed to back up Data\",\n  \"@Backup_Failed\": {},\n  \"Restore_Success\": \"Data successfully restored\",\n  \"@Restore_Success\": {},\n  \"Restore_Failed\": \"Failed to restore Data\",\n  \"@Restore_Failed\": {},\n  \"Select_Backup\": \"Select Backup\",\n  \"@Select_Backup\": {},\n  \"About\": \"About\",\n  \"@About\": {},\n  \"Name\": \"Name\",\n  \"@Name\": {},\n  \"Version\": \"Version\",\n  \"@Version\": {},\n  \"Developer\": \"Developer\",\n  \"@Developer\": {},\n  \"Sheikh_Haziq\": \"Sheikh Haziq\",\n  \"@Sheikh_Haziq\": {},\n  \"Organisation\": \"Organisation\",\n  \"@Organisation\": {},\n  \"Jhelum_Corp\": \"Jhelum Corp\",\n  \"@Jhelum_Corp\": {},\n  \"Telegram\": \"Telegram\",\n  \"@Telegram\": {},\n  \"Contributors\": \"Contributors\",\n  \"@Contributors\": {},\n  \"Source_Code\": \"Source Code\",\n  \"@Source_Code\": {},\n  \"Bug_Report\": \"Bug Report\",\n  \"@Bug_Report\": {},\n  \"Feature_Request\": \"Feature Request\",\n  \"@Feature_Request\": {},\n  \"Made_In_Kashmir\": \"Made in Kashmir\",\n  \"@Made_In_Kashmir\": {},\n  \"Check_For_Update\": \"Check for Update\",\n  \"@Check_For_Update\": {},\n  \"Progress\": \"Progress\",\n  \"@Progress\": {},\n  \"Play_Next\": \"Play Next\",\n  \"@Play_Next\": {},\n  \"Add_To_Queue\": \"Add To Queue\",\n  \"@Add_To_Queue\": {},\n  \"Add_To_Favourites\": \"Add To Favourites\",\n  \"@Add_To_Favourites\": {},\n  \"Remove_From_Favourites\": \"Remove From Favourites\",\n  \"@Remove_From_Favourites\": {},\n  \"Download\": \"Download\",\n  \"@Download\": {},\n  \"Add_To_Playlist\": \"Add To Playlist\",\n  \"@Add_To_Playlist\": {},\n  \"Start_Radio\": \"Start Radio\",\n  \"@Start_Radio\": {},\n  \"Album\": \"Album\",\n  \"@Album\": {},\n  \"Rename\": \"Rename\",\n  \"@Rename\": {},\n  \"Add_To_Library\": \"Add To Library\",\n  \"@Add_To_Library\": {},\n  \"Remove_From_Library\": \"Remove From Library\",\n  \"@Remove_From_Library\": {},\n  \"Delete_Item_Message\": \"Are you sure you want to delete this item?\",\n  \"@Delete_Item_Message\": {},\n  \"Equalizer\": \"Equalizer\",\n  \"@Equalizer\": {},\n  \"Sleep_Timer\": \"Sleep Timer\",\n  \"@Sleep_Timer\": {},\n  \"Create_Playlist\": \"Create Playlist\",\n  \"@Create_Playlist\": {},\n  \"Playlist_Name\": \"Playlist Name\",\n  \"@Playlist_Name\": {},\n  \"Create\": \"Create\",\n  \"@Create\": {},\n  \"Import_Playlist\": \"Import Playlist\",\n  \"@Import_Playlist\": {},\n  \"Import\": \"Import\",\n  \"@Import\": {},\n  \"Rename_Playlist\": \"Rename Playlist\",\n  \"@Rename_Playlist\": {},\n  \"Done\": \"Done\",\n  \"@Done\": {},\n  \"Cancel\": \"Cancel\",\n  \"@Cancel\": {},\n  \"Confirm\": \"Confirm\",\n  \"@Confirm\": {},\n  \"Yes\": \"Yes\",\n  \"@Yes\": {},\n  \"No\": \"No\",\n  \"@No\": {},\n  \"Show_More\": \"Show More\",\n  \"@Show_More\": {},\n  \"Show_Less\": \"Show Less\",\n  \"@Show_Less\": {},\n  \"Remove\": \"Remove\",\n  \"@Remove\": {},\n  \"High\": \"High\",\n  \"@High\": {},\n  \"Low\": \"Low\",\n  \"@Low\": {},\n  \"Songs_Will_Start_Playing_Soon\": \"Songs will start playing soon.\",\n  \"@Songs_Will_Start_Playing_Soon\": {},\n  \"Remove_Message\": \"Are you sure you want to remove it?\",\n  \"@Remove_Message\": {},\n  \"Remove_From_YTMusic_Message\": \"Are you sure you want to remove it from YTMusic?\",\n  \"@Remove_From_YTMusic_Message\": {},\n  \"Remove_All_History_Message\": \"Are you sure you want to clear all history?\",\n  \"@Remove_All_History_Message\": {},\n  \"Copied_To_Clipboard\": \"Copied to Clipboard\",\n  \"@Copied_To_Clipboard\": {},\n  \"No_Internet_Connection\": \"No Internet Connection\",\n  \"@No_Internet_Connection\": {},\n  \"Go_To_Downloads\": \"Go to Downloads\",\n  \"@Go_To_Downloads\": {},\n  \"Retry\": \"Retry\",\n  \"@Retry\": {},\n  \"Playlist_Not_Available\": \"Playlist not available\",\n  \"@Playlist_Not_Available\": {},\n  \"Confirm_Delete_All_Message\": \"Are you sure you want to delete them?\",\n  \"@Confirm_Delete_All_Message\": {},\n  \"Downloading\": \"Downloading\",\n  \"@Downloading\": {},\n  \"Restore_Missing_Songs\": \"Restore Missing Songs\",\n  \"@Restore_Missing_Songs\": {},\n  \"Delete_All_Songs\": \"Delete All Songs\",\n  \"@Delete_All_Songs\": {},\n  \"Download_Started\": \"Download started...\",\n  \"@Download_Started\": {},\n  \"Restoring_Missing_Songs\": \"Restoring Missing Songs...\",\n  \"@Restoring_Missing_Songs\": {},\n  \"Deleting_Songs\": \"Deleting Songs...\",\n  \"@Deleting_Songs\": {},\n  \"In_Progress\": \"In Progress\",\n  \"@In_Progress\": {},\n  \"Queued\": \"Queued\",\n  \"@Queued\": {},\n  \"Queued_Count\": \"Queued ({count})\",\n  \"@Queued_Count\": {},\n  \"File_Not_Found\": \"File not found\",\n  \"@File_Not_Found\": {},\n  \"Play_All\": \"Play All\",\n  \"@Play_All\": {},\n  \"Rotate_Device\": \"Rotate your device to type.\",\n  \"@Rotate_Device\": {},\n  \"View_Equalizer\": \"Play a song to see the equalizer.\",\n  \"@View_Equalizer\": {},\n  \"No_Offline_Songs\": \"No offline songs available\",\n  \"@No_Offline_Songs\": {},\n  \"Edit\": \"Edit\",\n  \"@Edit\": {},\n  \"Edit_Playlist\": \"Edit Playlist\",\n  \"@Edit_Playlist\": {},\n  \"Select_Playlist_Icon\": \"Select Playlist Icon\",\n  \"@Select_Playlist_Icon\": {},\n  \"Top_Results\": \"Top Results\",\n  \"@Top_Results\": {},\n  \"Other_Results\": \"Other Results\",\n  \"@Other_Results\": {}\n}"
  },
  {
    "path": "lib/l10n/intl_es.arb",
    "content": "{\n    \"@@locale\": \"es\",\n    \"Gyawun\": \"Gyawun\",\n    \"Next_Up\": \"Siguiente\",\n    \"@Next_Up\": {},\n    \"Shuffle\": \"Aleatorio\",\n    \"@Shuffle\": {},\n    \"@Gyawun\": {},\n    \"Home\": \"Inicio\",\n    \"@Home\": {},\n    \"Saved\": \"Guardado\",\n    \"@Saved\": {},\n    \"YTMusic\": \"YTMusic\",\n    \"@YTMusic\": {},\n    \"Settings\": \"Configuraciones\",\n    \"@Settings\": {},\n    \"Search_Gyawun\": \"Buscar Gyawun\",\n    \"@Search_Gyawun\": {},\n    \"Favourites\": \"Favoritos\",\n    \"@Favourites\": {},\n    \"Downloads\": \"Descargas\",\n    \"@Downloads\": {},\n    \"History\": \"Historial\",\n    \"@History\": {},\n    \"nSongs\": \"{count, plural, =0{No hay canciones} =1{1 canción} other{{count} canciones}}\",\n    \"@nSongs\": {\n        \"description\": \"Número de canciones\",\n        \"placeholders\": {\n            \"count\": {\n                \"type\": \"num\",\n                \"format\": \"compact\"\n            }\n        }\n    },\n    \"Songs\": \"Canciones\",\n    \"@Songs\": {},\n    \"Albums\": \"Álbumes\",\n    \"@Albums\": {},\n    \"Playlists\": \"Listas de reproducción\",\n    \"@Playlists\": {},\n    \"Artists\": \"Artistas\",\n    \"@Artists\": {},\n    \"Subscriptions\": \"Suscripciones\",\n    \"@Subscriptions\": {},\n    \"Search_Settings\": \"Configuración de búsqueda\",\n    \"@Search_Settings\": {},\n    \"Battery_Optimisation_title\": \"Optimización de batería detectada\",\n    \"@Battery_Optimisation_title\": {},\n    \"Battery_Optimisation_message\": \"Haz clic aquí para desactivar la optimización de batería para que Gyawun funcione correctamente\",\n    \"@Battery_Optimisation_message\": {},\n    \"Donate\": \"Donar\",\n    \"@Donate\": {},\n    \"Donate_Message\": \"Apoya el desarrollo de Gyawun\",\n    \"@Donate_Message\": {},\n    \"Payment_Methods\": \"Métodos de pago\",\n    \"@Payment_Methods\": {},\n    \"Pay_With_UPI\": \"Pagar con UPI\",\n    \"@Pay_With_UPI\": {},\n    \"Support_Me_On_Kofi\": \"Apóyame en Ko-fi\",\n    \"@Support_Me_On_Kofi\": {},\n    \"Buy_Me_A_Coffee\": \"Cómprame un café\",\n    \"@Buy_Me_A_Coffee\": {},\n    \"Google_Account\": \"Cuenta de Google\",\n    \"@Google_Account\": {},\n    \"Appearence\": \"Apariencia\",\n    \"Theme_Mode\": \"Modo de tema\",\n    \"@Theme_Mode\": {},\n    \"Window_Effect\": \"Efecto de ventana\",\n    \"@Window_Effect\": {},\n    \"Dynamic_Colors\": \"Colores dinámicos\",\n    \"@Dynamic_Colors\": {},\n    \"@Appearence\": {},\n    \"Content\": \"Contenido\",\n    \"@Content\": {},\n    \"Country\": \"País\",\n    \"@Country\": {},\n    \"Language\": \"Idioma\",\n    \"@Language\": {},\n    \"Personalised_Content\": \"Contenido personalizado\",\n    \"@Personalised_Content\": {},\n    \"Enter_Visitor_Id\": \"Introducir ID de visitante\",\n    \"@Enter_Visitor_Id\": {},\n    \"Visitor_Id\": \"ID de visitante\",\n    \"@Visitor_Id\": {},\n    \"Reset_Visitor_Id\": \"Restablecer ID de visitante\",\n    \"@Reset_Visitor_Id\": {},\n    \"Audio_And_Playback\": \"Audio y reproducción\",\n    \"@Audio_And_Playback\": {},\n    \"Loudness_And_Equalizer\": \"Volumen y ecualizador\",\n    \"@Loudness_And_Equalizer\": {},\n    \"Loudness_Enhancer\": \"Mejorador de volumen\",\n    \"@Loudness_Enhancer\": {},\n    \"Enable_Equalizer\": \"Activar ecualizador\",\n    \"@Enable_Equalizer\": {},\n    \"Streaming_Quality\": \"Calidad de transmisión\",\n    \"@Streaming_Quality\": {},\n    \"DOwnload_Quality\": \"Calidad de descarga\",\n    \"@DOwnload_Quality\": {},\n    \"Skip_Silence\": \"Saltar silencio\",\n    \"@Skip_Silence\": {},\n    \"Enable_Playback_History\": \"Activar historial de reproducción\",\n    \"@Enable_Playback_History\": {},\n    \"Delete_Playback_History\": \"Eliminar historial de reproducción\",\n    \"@Delete_Playback_History\": {},\n    \"Delete_Playback_History_Confirm_Message\": \"¿Estás seguro de que quieres eliminar el historial de reproducción?\",\n    \"@Delete_Playback_History_Confirm_Message\": {},\n    \"Playback_History_Deleted\": \"Historial de reproducción eliminado\",\n    \"@Playback_History_Deleted\": {},\n    \"Enable_Search_History\": \"Activar historial de búsqueda\",\n    \"@Enable_Search_History\": {},\n    \"Delete_Search_History\": \"Eliminar historial de búsqueda\",\n    \"@Delete_Search_History\": {},\n    \"Delete_Search_History_Confirm_Message\": \"¿Estás seguro de que quieres eliminar el historial de búsqueda?\",\n    \"@Delete_Search_History_Confirm_Message\": {},\n    \"Search_History_Deleted\": \"Historial de búsqueda eliminado\",\n    \"@Search_History_Deleted\": {},\n    \"Backup_And_Restore\": \"Copia de seguridad y restauración\",\n    \"@Backup_And_Restore\": {},\n    \"Backup\": \"Copia de seguridad\",\n    \"@Backup\": {},\n    \"Restore\": \"Restaurar\",\n    \"@Restore\": {},\n    \"Select_Backup\": \"Seleccionar copia de seguridad\",\n    \"@Select_Backup\": {},\n    \"About\": \"Acerca de\",\n    \"@About\": {},\n    \"Name\": \"Nombre\",\n    \"@Name\": {},\n    \"Version\": \"Versión\",\n    \"@Version\": {},\n    \"Developer\": \"Desarrollador\",\n    \"@Developer\": {},\n    \"Sheikh_Haziq\": \"Sheikh Haziq\",\n    \"@Sheikh_Haziq\": {},\n    \"Organisation\": \"Organización\",\n    \"@Organisation\": {},\n    \"Jhelum_Corp\": \"Jhelum Corp\",\n    \"@Jhelum_Corp\": {},\n    \"Telegram\": \"Telegram\",\n    \"@Telegram\": {},\n    \"Contributors\": \"Colaboradores\",\n    \"@Contributors\": {},\n    \"Source_Code\": \"Código fuente\",\n    \"@Source_Code\": {},\n    \"Bug_Report\": \"Reporte de errores\",\n    \"@Bug_Report\": {},\n    \"Feature_Request\": \"Solicitud de características\",\n    \"@Feature_Request\": {},\n    \"Made_In_Kashmir\": \"Hecho en Cachemira\",\n    \"@Made_In_Kashmir\": {},\n    \"Check_For_Update\": \"Buscar actualizaciones\",\n    \"@Check_For_Update\": {},\n    \"Progress\": \"Progreso\",\n    \"@Progress\": {},\n    \"Play_Next\": \"Reproducir siguiente\",\n    \"@Play_Next\": {},\n    \"Add_To_Queue\": \"Añadir a la cola\",\n    \"@Add_To_Queue\": {},\n    \"Add_To_Favourites\": \"Añadir a favoritos\",\n    \"@Add_To_Favourites\": {},\n    \"Remove_From_Favourites\": \"Eliminar de favoritos\",\n    \"@Remove_From_Favourites\": {},\n    \"Download\": \"Descargar\",\n    \"@Download\": {},\n    \"Add_To_Playlist\": \"Añadir a la lista de reproducción\",\n    \"@Add_To_Playlist\": {},\n    \"Start_Radio\": \"Iniciar radio\",\n    \"@Start_Radio\": {},\n    \"Album\": \"Álbum\",\n    \"@Album\": {},\n    \"Rename\": \"Renombrar\",\n    \"@Rename\": {},\n    \"Add_To_Library\": \"Añadir a la biblioteca\",\n    \"@Add_To_Library\": {},\n    \"Remove_From_Library\": \"Eliminar de la biblioteca\",\n    \"@Remove_From_Library\": {},\n    \"Delete_Item_Message\": \"¿Estás seguro de que quieres eliminar este elemento?\",\n    \"@Delete_Item_Message\": {},\n    \"Equalizer\": \"Ecualizador\",\n    \"@Equalizer\": {},\n    \"Sleep_Timer\": \"Temporizador de apagado\",\n    \"@Sleep_Timer\": {},\n    \"Create_Playlist\": \"Crear lista de reproducción\",\n    \"@Create_Playlist\": {},\n    \"Playlist_Name\": \"Nombre de la lista\",\n    \"@Playlist_Name\": {},\n    \"Create\": \"Crear\",\n    \"@Create\": {},\n    \"Import_Playlist\": \"Importar lista de reproducción\",\n    \"@Import_Playlist\": {},\n    \"Import\": \"Importar\",\n    \"@Import\": {},\n    \"Rename_Playlist\": \"Renombrar lista de reproducción\",\n    \"@Rename_Playlist\": {},\n    \"Done\": \"Hecho\",\n    \"@Done\": {},\n    \"Cancel\": \"Cancelar\",\n    \"@Cancel\": {},\n    \"Confirm\": \"Confirmar\",\n    \"@Confirm\": {},\n    \"Yes\": \"Sí\",\n    \"@Yes\": {},\n    \"No\": \"No\",\n    \"@No\": {},\n    \"Show_More\": \"Mostrar más\",\n    \"@Show_More\": {},\n    \"Show_Less\": \"Mostrar menos\",\n    \"@Show_Less\": {},\n    \"Remove\": \"Eliminar\",\n    \"@Remove\": {},\n    \"High\": \"Alto\",\n    \"@High\": {},\n    \"Low\": \"Bajo\",\n    \"@Low\": {},\n    \"Songs_Will_Start_Playing_Soon\": \"Las canciones comenzarán a reproducirse pronto.\",\n    \"@Songs_Will_Start_Playing_Soon\": {},\n    \"Remove_Message\": \"¿Estás seguro de que quieres eliminarlo?\",\n    \"@Remove_Message\": {},\n    \"Remove_From_YTMusic_Message\": \"¿Estás seguro de que quieres eliminarlo de YTMusic?\",\n    \"@Remove_From_YTMusic_Message\": {},\n    \"Remove_All_History_Message\": \"¿Estás seguro de que quieres borrar todo el historial?\",\n    \"@Remove_All_History_Message\": {},\n    \"Copied_To_Clipboard\": \"Copiado al portapapeles\",\n    \"@Copied_To_Clipboard\": {}\n}"
  },
  {
    "path": "lib/l10n/intl_fr.arb",
    "content": "{\n  \"@@locale\": \"fr\",\n  \"Gyawun\": \"Gyawun\",\n  \"Next_Up\": \"Suivant\",\n  \"@Next_Up\": {},\n  \"Shuffle\": \"Aléatoire\",\n  \"@Shuffle\": {},\n  \"@Gyawun\": {},\n  \"Home\": \"Accueil\",\n  \"@Home\": {},\n  \"Saved\": \"Enregistré\",\n  \"@Saved\": {},\n  \"YTMusic\": \"YouTube Music\",\n  \"@YTMusic\": {},\n  \"Settings\": \"Paramètres\",\n  \"@Settings\": {},\n  \"Search_Gyawun\": \"Rechercher sur Gyawun\",\n  \"@Search_Gyawun\": {},\n  \"Favourites\": \"Favoris\",\n  \"@Favourites\": {},\n  \"Downloads\": \"Téléchargements\",\n  \"@Downloads\": {},\n  \"History\": \"Historique\",\n  \"@History\": {},\n  \"nSongs\": \"{count, plural, =0{Pas de Titres} =1{1 Titre} other{{count} Titres}}\",\n  \"@nSongs\": {\n    \"description\": \"Nombre de titres\",\n    \"placeholders\": {\n      \"count\": {\n        \"type\": \"num\",\n        \"format\": \"compact\"\n      }\n    }\n  },\n  \"Songs\": \"Titres\",\n  \"@Songs\": {},\n  \"Albums\": \"Albums\",\n  \"@Albums\": {},\n  \"Playlists\": \"Playlists\",\n  \"@Playlists\": {},\n  \"Artists\": \"Artistes\",\n  \"@Artists\": {},\n  \"Subscriptions\": \"Abonnements\",\n  \"@Subscriptions\": {},\n  \"Search_Settings\": \"Paramètres de recherche\",\n  \"@Search_Settings\": {},\n  \"Battery_Optimisation_title\": \"Optimisation de batterie détectée\",\n  \"@Battery_Optimisation_title\": {},\n  \"Battery_Optimisation_message\": \"Cliquez ici pour désactiver l'optimisation de la batterie afin que Gyawun fonctionne correctement.\",\n  \"@Battery_Optimisation_message\": {},\n  \"Donate\": \"Faire un don\",\n  \"@Donate\": {},\n  \"Donate_Message\": \"Soutenez le développement de Gyawun\",\n  \"@Donate_Message\": {},\n  \"Payment_Methods\": \"Modes de Paiement\",\n  \"@Payment_Methods\": {},\n  \"Pay_With_UPI\": \"Payer avec UPI\",\n  \"@Pay_With_UPI\": {},\n  \"Support_Me_On_Kofi\": \"Soutenez-moi sur Ko-fi\",\n  \"@Support_Me_On_Kofi\": {},\n  \"Buy_Me_A_Coffee\": \"Offrez-moi un café\",\n  \"@Buy_Me_A_Coffee\": {},\n  \"Google_Account\": \"Compte Google\",\n  \"@Google_Account\": {},\n  \"Appearence\": \"Apparence\",\n  \"Theme_Mode\": \"Thème\",\n  \"@Theme_Mode\": {},\n  \"Window_Effect\": \"Effet fenêtre\",\n  \"@Window_Effect\": {},\n  \"Dynamic_Colors\": \"Couleurs Dynamiques\",\n  \"@Dynamic_Colors\": {},\n  \"@Appearence\": {},\n  \"Content\": \"Contenu\",\n  \"@Content\": {},\n  \"Country\": \"Pays\",\n  \"@Country\": {},\n  \"Language\": \"Langue\",\n  \"@Language\": {},\n  \"Translate_Lyrics\": \"Traduire les paroles\",\n  \"@Translate_Lyrics\": {},\n  \"Autofetch_Songs\": \"Lecture automatique de titres similaires\",\n  \"@Autofetch_Songs\": {},\n  \"Personalised_Content\": \"Contenu Personnalisé\",\n  \"@Personalised_Content\": {},\n  \"Enter_Visitor_Id\": \"Saisir l'identifiant du visiteur\",\n  \"@Enter_Visitor_Id\": {},\n  \"Visitor_Id\": \"Identifiant du Visiteur\",\n  \"@Visitor_Id\": {},\n  \"Reset_Visitor_Id\": \"Réinitialiser l'identifiant du visiteur\",\n  \"@Reset_Visitor_Id\": {},\n  \"Audio_And_Playback\": \"Audio et Lecture\",\n  \"@Audio_And_Playback\": {},\n  \"Loudness_And_Equalizer\": \"Volume et Égaliseur\",\n  \"@Loudness_And_Equalizer\": {},\n  \"Loudness_Enhancer\": \"Amplificateur de Volume\",\n  \"@Loudness_Enhancer\": {},\n  \"Enable_Equalizer\": \"Activer l'égaliseur\",\n  \"@Enable_Equalizer\": {},\n  \"Streaming_Quality\": \"Qualité du Streaming\",\n  \"@Streaming_Quality\": {},\n  \"DOwnload_Quality\": \"Qualité du Téléchargement\",\n  \"@DOwnload_Quality\": {},\n  \"App_Folder\": \"Dossier Application\",\n  \"@App_Folder\": {},\n  \"Skip_Silence\": \"Ignorer le Silence\",\n  \"@Skip_Silence\": {},\n  \"Enable_Playback_History\": \"Activer l'historique de lecture\",\n  \"@Enable_Playback_History\": {},\n  \"Delete_Playback_History\": \"Supprimer l'historique de lecture\",\n  \"@Delete_Playback_History\": {},\n  \"Delete_Playback_History_Confirm_Message\": \"Êtes-vous sûr de vouloir supprimer l'historique de lecture ?\",\n  \"@Delete_Playback_History_Confirm_Message\": {},\n  \"Playback_History_Deleted\": \"Historique de lecture supprimé\",\n  \"@Playback_History_Deleted\": {},\n  \"Enable_Search_History\": \"Activer l'historique de recherche\",\n  \"@Enable_Search_History\": {},\n  \"Delete_Search_History\": \"Supprimer l'historique de recherche\",\n  \"@Delete_Search_History\": {},\n  \"Delete_Search_History_Confirm_Message\": \"Êtes-vous sûr de vouloir supprimer l'historique de recherche ?\",\n  \"@Delete_Search_History_Confirm_Message\": {},\n  \"Search_History_Deleted\": \"Historique de recherche supprimé\",\n  \"@Search_History_Deleted\": {},\n  \"Backup_And_Restore\": \"Sauvegarde et Restauration\",\n  \"@Backup_And_Restore\": {},\n  \"Backup\": \"Sauvegarde\",\n  \"@Backup\": {},\n  \"Restore\": \"Restauration\",\n  \"@Restore\": {},\n  \"Share\": \"Partager\",\n  \"@Share\": {},\n  \"Save\": \"Enregistrer\",\n  \"@Save\": {},\n  \"Backup_Success\": \"Sauvegarde effectuée avec succès à\",\n  \"@Backup_Success\": {},\n  \"Backup_Failed\": \"Échec de la sauvegarde des données\",\n  \"@Backup_Failed\": {},\n  \"Restore_Success\": \"Données restaurées avec succès\",\n  \"@Restore_Success\": {},\n  \"Restore_Failed\": \"Échec de la restauration des données\",\n  \"@Restore_Failed\": {},\n  \"Select_Backup\": \"Sélectionnez  la sauvegarde\",\n  \"@Select_Backup\": {},\n  \"About\": \"À propos\",\n  \"@About\": {},\n  \"Name\": \"Nom\",\n  \"@Name\": {},\n  \"Version\": \"Version\",\n  \"@Version\": {},\n  \"Developer\": \"Développeur\",\n  \"@Developer\": {},\n  \"Sheikh_Haziq\": \"Sheikh Haziq\",\n  \"@Sheikh_Haziq\": {},\n  \"Organisation\": \"Organisation\",\n  \"@Organisation\": {},\n  \"Jhelum_Corp\": \"Jhelum Corp\",\n  \"@Jhelum_Corp\": {},\n  \"Telegram\": \"Telegram\",\n  \"@Telegram\": {},\n  \"Contributors\": \"Contributeurs\",\n  \"@Contributors\": {},\n  \"Source_Code\": \"Code Source\",\n  \"@Source_Code\": {},\n  \"Bug_Report\": \"Rapport de bug\",\n  \"@Bug_Report\": {},\n  \"Feature_Request\": \"Demande de fonctionnalité\",\n  \"@Feature_Request\": {},\n  \"Made_In_Kashmir\": \"Fabriqué au Cachemire\",\n  \"@Made_In_Kashmir\": {},\n  \"Check_For_Update\": \"Vérifier les mises à jour\",\n  \"@Check_For_Update\": {},\n  \"Progress\": \"Progrès\",\n  \"@Progress\": {},\n  \"Play_Next\": \"Lire ensuite\",\n  \"@Play_Next\": {},\n  \"Add_To_Queue\": \"Ajouter à la file d'attente\",\n  \"@Add_To_Queue\": {},\n  \"Add_To_Favourites\": \"Ajouter aux favoris\",\n  \"@Add_To_Favourites\": {},\n  \"Remove_From_Favourites\": \"Supprimer des favoris\",\n  \"@Remove_From_Favourites\": {},\n  \"Download\": \"Télécharger\",\n  \"@Download\": {},\n  \"Add_To_Playlist\": \"Ajouter à une playlist\",\n  \"@Add_To_Playlist\": {},\n  \"Start_Radio\": \"Démarrer la radio\",\n  \"@Start_Radio\": {},\n  \"Album\": \"Album\",\n  \"@Album\": {},\n  \"Rename\": \"Renommer\",\n  \"@Rename\": {},\n  \"Add_To_Library\": \"Ajouter à la bibliothèque\",\n  \"@Add_To_Library\": {},\n  \"Remove_From_Library\": \"Supprimer de la bibliothèque\",\n  \"@Remove_From_Library\": {},\n  \"Delete_Item_Message\": \"Êtes-vous sûr de vouloir supprimer cet élément ?\",\n  \"@Delete_Item_Message\": {},\n  \"Equalizer\": \"Égaliseur\",\n  \"@Equalizer\": {},\n  \"Sleep_Timer\": \"Minuterie de Sommeil\",\n  \"@Sleep_Timer\": {},\n  \"Create_Playlist\": \"Créer une playlist\",\n  \"@Create_Playlist\": {},\n  \"Playlist_Name\": \"Nom de la playlist\",\n  \"@Playlist_Name\": {},\n  \"Create\": \"Créer\",\n  \"@Create\": {},\n  \"Import_Playlist\": \"Importer une playlist\",\n  \"@Import_Playlist\": {},\n  \"Import\": \"Importer\",\n  \"@Import\": {},\n  \"Rename_Playlist\": \"Renommer la playlist\",\n  \"@Rename_Playlist\": {},\n  \"Done\": \"Terminé\",\n  \"@Done\": {},\n  \"Cancel\": \"Annuler\",\n  \"@Cancel\": {},\n  \"Confirm\": \"Confirmer\",\n  \"@Confirm\": {},\n  \"Yes\": \"Oui\",\n  \"@Yes\": {},\n  \"No\": \"Non\",\n  \"@No\": {},\n  \"Show_More\": \"Afficher plus\",\n  \"@Show_More\": {},\n  \"Show_Less\": \"Afficher moins\",\n  \"@Show_Less\": {},\n  \"Remove\": \"Supprimer\",\n  \"@Remove\": {},\n  \"High\": \"Haute\",\n  \"@High\": {},\n  \"Low\": \"Basse\",\n  \"@Low\": {},\n  \"Songs_Will_Start_Playing_Soon\": \"Les titres commenceront bientôt à être diffusés.\",\n  \"@Songs_Will_Start_Playing_Soon\": {},\n  \"Remove_Message\": \"Êtes-vous sûr de vouloir le supprimer ?\",\n  \"@Remove_Message\": {},\n  \"Remove_From_YTMusic_Message\": \"Êtes-vous sûr de vouloir le supprimer de YouTube Music ?\",\n  \"@Remove_From_YTMusic_Message\": {},\n  \"Remove_All_History_Message\": \"Êtes-vous sûr de vouloir effacer tout l'historique ?\",\n  \"@Remove_All_History_Message\": {},\n  \"Copied_To_Clipboard\": \"Copié dans le presse-papiers\",\n  \"@Copied_To_Clipboard\": {},\n  \"No_Internet_Connection\": \"Aucune connexion Internet\",\n  \"@No_Internet_Connection\": {},\n  \"Go_To_Downloads\": \"Accéder aux téléchargements\",\n  \"@Go_To_Downloads\": {},\n  \"Retry\": \"Réessayer\",\n  \"@Retry\": {},\n  \"Playlist_Not_Available\": \"Playlist indisponible\",\n  \"@Playlist_Not_Available\": {},\n  \"Confirm_Delete_All_Message\": \"Êtes-vous sûr de vouloir les supprimer ?\",\n  \"@Confirm_Delete_All_Message\": {},\n  \"Downloading\": \"Téléchargement\",\n  \"@Downloading\": {},\n  \"Restore_Missing_Songs\": \"Restaurer les titres manquants\",\n  \"@Restore_Missing_Songs\": {},\n  \"Delete_All_Songs\": \"Supprimer tous les titres\",\n  \"@Delete_All_Songs\": {},\n  \"Download_Started\": \"Téléchargement en cours...\",\n  \"@Download_Started\": {},\n  \"Restoring_Missing_Songs\": \"Restauration des titres manquants...\",\n  \"@Restoring_Missing_Songs\": {},\n  \"Deleting_Songs\": \"Suppression de titres...\",\n  \"@Deleting_Songs\": {},\n  \"In_Progress\": \"En cours\",\n  \"@In_Progress\": {},\n  \"Queued\": \"En attente\",\n  \"@Queued\": {},\n  \"QueuedCount\": \"En attente ({count})\",\n  \"@QueuedCount\": {},\n  \"FileNotFound\": \"Fichier introuvable\",\n  \"@FileNotFound\": {}\n}\n"
  },
  {
    "path": "lib/l10n/intl_hi.arb",
    "content": "{\n    \"@@locale\": \"hi\",\n    \"Gyawun\": \"ग्यावुन\",\n    \"@Gyawun\": {},\n    \"Next_Up\": \"अगला\",\n    \"@Next_Up\": {},\n    \"Shuffle\": \"शफल\",\n    \"@Shuffle\": {},\n    \"Home\": \"होम\",\n    \"@Home\": {},\n    \"Saved\": \"सहेजे गए\",\n    \"@Saved\": {},\n    \"YTMusic\": \"YT संगीत\",\n    \"@YTMusic\": {},\n    \"Settings\": \"सेटिंग्स\",\n    \"@Settings\": {},\n    \"Search_Gyawun\": \"ग्यावुन खोजें\",\n    \"@Search_Gyawun\": {},\n    \"Favourites\": \"पसंदीदा\",\n    \"@Favourites\": {},\n    \"Downloads\": \"डाउनलोड\",\n    \"@Downloads\": {},\n    \"History\": \"इतिहास\",\n    \"@History\": {},\n    \"nSongs\": \"{count, plural, =0{कोई गाने नहीं} =1{1 गाना} other{{count} गाने}}\",\n    \"@nSongs\": {\n        \"description\": \"गानों की संख्या\",\n        \"placeholders\": {\n            \"count\": {\n                \"type\": \"num\",\n                \"format\": \"compact\"\n            }\n        }\n    },\n    \"Songs\": \"गाने\",\n    \"@Songs\": {},\n    \"Albums\": \"एल्बम\",\n    \"@Albums\": {},\n    \"Playlists\": \"प्लेलिस्ट\",\n    \"@Playlists\": {},\n    \"Artists\": \"कलाकार\",\n    \"@Artists\": {},\n    \"Subscriptions\": \"सब्सक्रिप्शन\",\n    \"@Subscriptions\": {},\n    \"Search_Settings\": \"खोज सेटिंग्स\",\n    \"@Search_Settings\": {},\n    \"Battery_Optimisation_title\": \"बैटरी ऑप्टिमाइजेशन पता चला\",\n    \"@Battery_Optimisation_title\": {},\n    \"Battery_Optimisation_message\": \"ग्यावुन को ठीक से काम करने के लिए बैटरी ऑप्टिमाइजेशन को अक्षम करने के लिए यहां क्लिक करें\",\n    \"@Battery_Optimisation_message\": {},\n    \"Donate\": \"दान करें\",\n    \"@Donate\": {},\n    \"Donate_Message\": \"ग्यावुन के विकास का समर्थन करें\",\n    \"@Donate_Message\": {},\n    \"Payment_Methods\": \"भुगतान के तरीके\",\n    \"@Payment_Methods\": {},\n    \"Pay_With_UPI\": \"UPI के साथ भुगतान करें\",\n    \"@Pay_With_UPI\": {},\n    \"Support_Me_On_Kofi\": \"को-फ़ी पर मुझे समर्थन दें\",\n    \"@Support_Me_On_Kofi\": {},\n    \"Buy_Me_A_Coffee\": \"मुझे एक कॉफी खरीदें\",\n    \"@Buy_Me_A_Coffee\": {},\n    \"Google_Account\": \"गूगल अकाउंट\",\n    \"@Google_Account\": {},\n    \"Appearence\": \"दिखावट\",\n    \"Theme_Mode\": \"थीम मोड\",\n    \"@Theme_Mode\": {},\n    \"Window_Effect\": \"विंडो प्रभाव\",\n    \"@Window_Effect\": {},\n    \"Dynamic_Colors\": \"डायनामिक रंग\",\n    \"@Dynamic_Colors\": {},\n    \"@Appearence\": {},\n    \"Content\": \"सामग्री\",\n    \"@Content\": {},\n    \"Country\": \"देश\",\n    \"@Country\": {},\n    \"Language\": \"भाषा\",\n    \"@Language\": {},\n    \"Personalised_Content\": \"व्यक्तिगत सामग्री\",\n    \"@Personalised_Content\": {},\n    \"Enter_Visitor_Id\": \"विज़िटर आईडी दर्ज करें\",\n    \"@Enter_Visitor_Id\": {},\n    \"Visitor_Id\": \"विज़िटर आईडी\",\n    \"@Visitor_Id\": {},\n    \"Reset_Visitor_Id\": \"विज़िटर आईडी रीसेट करें\",\n    \"@Reset_Visitor_Id\": {},\n    \"Audio_And_Playback\": \"ऑडियो और प्लेबैक\",\n    \"@Audio_And_Playback\": {},\n    \"Loudness_And_Equalizer\": \"लाउडनेस और इक्वलाइज़र\",\n    \"@Loudness_And_Equalizer\": {},\n    \"Loudness_Enhancer\": \"लाउडनेस एन्हांसर\",\n    \"@Loudness_Enhancer\": {},\n    \"Enable_Equalizer\": \"इक्वलाइज़र सक्षम करें\",\n    \"@Enable_Equalizer\": {},\n    \"Streaming_Quality\": \"स्ट्रीमिंग गुणवत्ता\",\n    \"@Streaming_Quality\": {},\n    \"DOwnload_Quality\": \"डाउनलोड गुणवत्ता\",\n    \"@DOwnload_Quality\": {},\n    \"Skip_Silence\": \"चुप्पी छोड़ें\",\n    \"@Skip_Silence\": {},\n    \"Enable_Playback_History\": \"प्लेबैक इतिहास सक्षम करें\",\n    \"@Enable_Playback_History\": {},\n    \"Delete_Playback_History\": \"प्लेबैक इतिहास हटाएं\",\n    \"@Delete_Playback_History\": {},\n    \"Delete_Playback_History_Confirm_Message\": \"क्या आप वाकई प्लेबैक इतिहास हटाना चाहते हैं।\",\n    \"@Delete_Playback_History_Confirm_Message\": {},\n    \"Playback_History_Deleted\": \"प्लेबैक इतिहास हटाया गया\",\n    \"@Playback_History_Deleted\": {},\n    \"Enable_Search_History\": \"खोज इतिहास सक्षम करें\",\n    \"@Enable_Search_History\": {},\n    \"Delete_Search_History\": \"खोज इतिहास हटाएं\",\n    \"@Delete_Search_History\": {},\n    \"Delete_Search_History_Confirm_Message\": \"क्या आप वाकई खोज इतिहास हटाना चाहते हैं।\",\n    \"@Delete_Search_History_Confirm_Message\": {},\n    \"Search_History_Deleted\": \"खोज इतिहास हटाया गया\",\n    \"@Search_History_Deleted\": {},\n    \"Backup_And_Restore\": \"बैकअप और पुनर्स्थापना\",\n    \"@Backup_And_Restore\": {},\n    \"Backup\": \"बैकअप\",\n    \"@Backup\": {},\n    \"Restore\": \"पुनर्स्थापित\",\n    \"@Restore\": {},\n    \"Select_Backup\": \"बैकअप चुनें\",\n    \"@Select_Backup\": {},\n    \"About\": \"के बारे में\",\n    \"@About\": {},\n    \"Name\": \"नाम\",\n    \"@Name\": {},\n    \"Version\": \"संस्करण\",\n    \"@Version\": {},\n    \"Developer\": \"डेवलपर\",\n    \"@Developer\": {},\n    \"Sheikh_Haziq\": \"शेख हाजिक\",\n    \"@Sheikh_Haziq\": {},\n    \"Organisation\": \"संगठन\",\n    \"@Organisation\": {},\n    \"Jhelum_Corp\": \"झेलम कॉर्प\",\n    \"@Jhelum_Corp\": {},\n    \"Telegram\": \"टेलीग्राम\",\n    \"@Telegram\": {},\n    \"Contributors\": \"योगदानकर्ता\",\n    \"@Contributors\": {},\n    \"Source_Code\": \"सोर्स कोड\",\n    \"@Source_Code\": {},\n    \"Bug_Report\": \"बग रिपोर्ट\",\n    \"@Bug_Report\": {},\n    \"Feature_Request\": \"फीचर अनुरोध\",\n    \"@Feature_Request\": {},\n    \"Made_In_Kashmir\": \"कश्मीर में बना\",\n    \"@Made_In_Kashmir\": {},\n    \"Check_For_Update\": \"अपडेट के लिए जाँचें\",\n    \"@Check_For_Update\": {},\n    \"Progress\": \"प्रगति\",\n    \"@Progress\": {},\n    \"Play_Next\": \"अगला चलाएं\",\n    \"@Play_Next\": {},\n    \"Add_To_Queue\": \"कतार में जोड़ें\",\n    \"@Add_To_Queue\": {},\n    \"Add_To_Favourites\": \"पसंदीदा में जोड़ें\",\n    \"@Add_To_Favourites\": {},\n    \"Remove_From_Favourites\": \"पसंदीदा से हटाएं\",\n    \"@Remove_From_Favourites\": {},\n    \"Download\": \"डाउनलोड\",\n    \"@Download\": {},\n    \"Add_To_Playlist\": \"प्लेलिस्ट में जोड़ें\",\n    \"@Add_To_Playlist\": {},\n    \"Start_Radio\": \"रेडियो शुरू करें\",\n    \"@Start_Radio\": {},\n    \"Album\": \"एल्बम\",\n    \"@Album\": {},\n    \"Rename\": \"नाम बदलें\",\n    \"@Rename\": {},\n    \"Add_To_Library\": \"लाइब्रेरी में जोड़ें\",\n    \"@Add_To_Library\": {},\n    \"Remove_From_Library\": \"लाइब्रेरी से हटाएं\",\n    \"@Remove_From_Library\": {},\n    \"Delete_Item_Message\": \"क्या आप वाकई इस आइटम को हटाना चाहते हैं?\",\n    \"@Delete_Item_Message\": {},\n    \"Equalizer\": \"इक्वलाइज़र\",\n    \"@Equalizer\": {},\n    \"Sleep_Timer\": \"स्लीप टाइमर\",\n    \"@Sleep_Timer\": {},\n    \"Create_Playlist\": \"प्लेलिस्ट बनाएं\",\n    \"@Create_Playlist\": {},\n    \"Playlist_Name\": \"प्लेलिस्ट का नाम\",\n    \"@Playlist_Name\": {},\n    \"Create\": \"बनाएं\",\n    \"@Create\": {},\n    \"Import_Playlist\": \"प्लेलिस्ट आयात करें\",\n    \"@Import_Playlist\": {},\n    \"Import\": \"आयात करें\",\n    \"@Import\": {},\n    \"Rename_Playlist\": \"प्लेलिस्ट का नाम बदलें\",\n    \"@Rename_Playlist\": {},\n    \"Done\": \"हो गया\",\n    \"@Done\": {},\n    \"Cancel\": \"रद्द करें\",\n    \"@Cancel\": {},\n    \"Confirm\": \"पुष्टि करें\",\n    \"@Confirm\": {},\n    \"Yes\": \"हाँ\",\n    \"@Yes\": {},\n    \"No\": \"नहीं\",\n    \"@No\": {},\n    \"Show_More\": \"और दिखाएं\",\n    \"@Show_More\": {},\n    \"Show_Less\": \"कम दिखाएं\",\n    \"@Show_Less\": {},\n    \"Remove\": \"हटाएं\",\n    \"@Remove\": {},\n    \"High\": \"उच्च\",\n    \"@High\": {},\n    \"Low\": \"निम्न\",\n    \"@Low\": {},\n    \"Songs_Will_Start_Playing_Soon\": \"गाने जल्द ही बजना शुरू हो जाएंगे।\",\n    \"@Songs_Will_Start_Playing_Soon\": {},\n    \"Remove_Message\": \"क्या आप वाकई इसे हटाना चाहते हैं?\",\n    \"@Remove_Message\": {},\n    \"Remove_From_YTMusic_Message\": \"क्या आप वाकई इसे YT संगीत से हटाना चाहते हैं?\",\n    \"@Remove_From_YTMusic_Message\": {},\n    \"Remove_All_History_Message\": \"क्या आप वाकई सभी इतिहास साफ़ करना चाहते हैं?\",\n    \"@Remove_All_History_Message\": {},\n    \"Copied_To_Clipboard\": \"क्लिपबोर्ड पर कॉपी किया गया\",\n    \"@Copied_To_Clipboard\": {}\n}"
  },
  {
    "path": "lib/l10n/intl_it.arb",
    "content": "{\n  \"@@locale\": \"it\",\n  \"Gyawun\": \"Gyawun\",\n  \"@Gyawun\": {},\n  \"Next_Up\": \"Prossimo\",\n  \"@Next_Up\": {},\n  \"Shuffle\": \"Casuale\",\n  \"@Shuffle\": {},\n  \"Home\": \"Home\",\n  \"@Home\": {},\n  \"Saved\": \"Salvati\",\n  \"@Saved\": {},\n  \"YTMusic\": \"YTMusic\",\n  \"@YTMusic\": {},\n  \"Settings\": \"Impostazioni\",\n  \"@Settings\": {},\n  \"Search_Gyawun\": \"Cerca in Gyawun\",\n  \"@Search_Gyawun\": {},\n  \"Favourites\": \"Preferiti\",\n  \"@Favourites\": {},\n  \"Downloads\": \"Download\",\n  \"@Downloads\": {},\n  \"History\": \"Cronologia\",\n  \"@History\": {},\n  \"nSongs\": \"{count, plural, =0{Nessun brano} =1{1 brano} other{{count} brani}}\",\n  \"@nSongs\": {\n    \"description\": \"Numero di brani\",\n    \"placeholders\": {\n      \"count\": {\n        \"type\": \"num\",\n        \"format\": \"compact\"\n      }\n    }\n  },\n  \"Songs\": \"Brani\",\n  \"@Songs\": {},\n  \"Albums\": \"Album\",\n  \"@Albums\": {},\n  \"Playlists\": \"Playlist\",\n  \"@Playlists\": {},\n  \"Artists\": \"Artisti\",\n  \"@Artists\": {},\n  \"Subscriptions\": \"Iscrizioni\",\n  \"@Subscriptions\": {},\n  \"Search_Settings\": \"Impostazioni di ricerca\",\n  \"@Search_Settings\": {},\n  \"Battery_Optimisation_title\": \"Ottimizzazione batteria rilevata\",\n  \"@Battery_Optimisation_title\": {},\n  \"Battery_Optimisation_message\": \"Clicca qui per disattivare l’ottimizzazione della batteria e permettere a Gyawun di funzionare correttamente\",\n  \"@Battery_Optimisation_message\": {},\n  \"Donate\": \"Dona\",\n  \"@Donate\": {},\n  \"Donate_Message\": \"Supporta lo sviluppo di Gyawun\",\n  \"@Donate_Message\": {},\n  \"Payment_Methods\": \"Metodi di pagamento\",\n  \"@Payment_Methods\": {},\n  \"Pay_With_UPI\": \"Paga con UPI\",\n  \"@Pay_With_UPI\": {},\n  \"Support_Me_On_Kofi\": \"Supportami su Ko-fi\",\n  \"@Support_Me_On_Kofi\": {},\n  \"Buy_Me_A_Coffee\": \"Offrimi un caffè\",\n  \"@Buy_Me_A_Coffee\": {},\n  \"Google_Account\": \"Account Google\",\n  \"@Google_Account\": {},\n  \"Appearence\": \"Aspetto\",\n  \"@Appearence\": {},\n  \"Theme_Mode\": \"Tema\",\n  \"@Theme_Mode\": {},\n  \"Window_Effect\": \"Effetto finestra\",\n  \"@Window_Effect\": {},\n  \"Dynamic_Colors\": \"Colori dinamici\",\n  \"@Dynamic_Colors\": {},\n  \"Content\": \"Contenuti\",\n  \"@Content\": {},\n  \"Country\": \"Paese\",\n  \"@Country\": {},\n  \"Language\": \"Lingua\",\n  \"@Language\": {},\n  \"Translate_Lyrics\": \"Traduci testi\",\n  \"@Translate_Lyrics\": {},\n  \"Autofetch_Songs\": \"Riproduci automaticamente brani simili\",\n  \"@Autofetch_Songs\": {},\n  \"Personalised_Content\": \"Contenuti personalizzati\",\n  \"@Personalised_Content\": {},\n  \"Enter_Visitor_Id\": \"Inserisci Visitor ID\",\n  \"@Enter_Visitor_Id\": {},\n  \"Visitor_Id\": \"Visitor ID\",\n  \"@Visitor_Id\": {},\n  \"Reset_Visitor_Id\": \"Reimposta Visitor ID\",\n  \"@Reset_Visitor_Id\": {},\n  \"Audio_And_Playback\": \"Audio e riproduzione\",\n  \"@Audio_And_Playback\": {},\n  \"Loudness_And_Equalizer\": \"Volume ed equalizzatore\",\n  \"@Loudness_And_Equalizer\": {},\n  \"Loudness_Enhancer\": \"Amplificatore volume\",\n  \"@Loudness_Enhancer\": {},\n  \"Enable_Equalizer\": \"Abilita equalizzatore\",\n  \"@Enable_Equalizer\": {},\n  \"Streaming_Quality\": \"Qualità streaming\",\n  \"@Streaming_Quality\": {},\n  \"DOwnload_Quality\": \"Qualità download\",\n  \"@DOwnload_Quality\": {},\n  \"App_Folder\": \"Cartella App\",\n  \"@App_Folder\": {},\n  \"Skip_Silence\": \"Salta silenzi\",\n  \"@Skip_Silence\": {},\n  \"Enable_Playback_History\": \"Abilita cronologia di riproduzione\",\n  \"@Enable_Playback_History\": {},\n  \"Delete_Playback_History\": \"Elimina cronologia di riproduzione\",\n  \"@Delete_Playback_History\": {},\n  \"Delete_Playback_History_Confirm_Message\": \"Sei sicuro di voler eliminare la cronologia di riproduzione?\",\n  \"@Delete_Playback_History_Confirm_Message\": {},\n  \"Playback_History_Deleted\": \"Cronologia di riproduzione eliminata\",\n  \"@Playback_History_Deleted\": {},\n  \"Enable_Search_History\": \"Abilita cronologia di ricerca\",\n  \"@Enable_Search_History\": {},\n  \"Delete_Search_History\": \"Elimina cronologia di ricerca\",\n  \"@Delete_Search_History\": {},\n  \"Delete_Search_History_Confirm_Message\": \"Sei sicuro di voler eliminare la cronologia di ricerca?\",\n  \"@Delete_Search_History_Confirm_Message\": {},\n  \"Search_History_Deleted\": \"Cronologia di ricerca eliminata\",\n  \"@Search_History_Deleted\": {},\n  \"Backup_And_Restore\": \"Backup e ripristino\",\n  \"@Backup_And_Restore\": {},\n  \"Backup\": \"Backup\",\n  \"@Backup\": {},\n  \"Restore\": \"Ripristina\",\n  \"@Restore\": {},\n  \"Share\": \"Condividi\",\n  \"@Share\": {},\n  \"Save\": \"Salva\",\n  \"@Save\": {},\n  \"Backup_Success\": \"Salvataggio dati riuscito al percorso\",\n  \"@Backup_Success\": {},\n  \"Backup_Failed\": \"Salvataggio dati non riuscito\",\n  \"@Backup_Failed\": {},\n  \"Restore_Success\": \"Ripristino dati riuscito\",\n  \"@Restore_Success\": {},\n  \"Restore_Failed\": \"Ripristino dati non riuscito\",\n  \"@Restore_Failed\": {},\n  \"Select_Backup\": \"Seleziona backup\",\n  \"@Select_Backup\": {},\n  \"About\": \"Informazioni\",\n  \"@About\": {},\n  \"Name\": \"Nome\",\n  \"@Name\": {},\n  \"Version\": \"Versione\",\n  \"@Version\": {},\n  \"Developer\": \"Sviluppatore\",\n  \"@Developer\": {},\n  \"Sheikh_Haziq\": \"Sheikh Haziq\",\n  \"@Sheikh_Haziq\": {},\n  \"Organisation\": \"Organizzazione\",\n  \"@Organisation\": {},\n  \"Jhelum_Corp\": \"Jhelum Corp\",\n  \"@Jhelum_Corp\": {},\n  \"Telegram\": \"Telegram\",\n  \"@Telegram\": {},\n  \"Contributors\": \"Collaboratori\",\n  \"@Contributors\": {},\n  \"Source_Code\": \"Codice sorgente\",\n  \"@Source_Code\": {},\n  \"Bug_Report\": \"Segnala un bug\",\n  \"@Bug_Report\": {},\n  \"Feature_Request\": \"Richiesta funzionalità\",\n  \"@Feature_Request\": {},\n  \"Made_In_Kashmir\": \"Realizzato in Kashmir\",\n  \"@Made_In_Kashmir\": {},\n  \"Check_For_Update\": \"Verifica aggiornamenti\",\n  \"@Check_For_Update\": {},\n  \"Progress\": \"Avanzamento\",\n  \"@Progress\": {},\n  \"Play_Next\": \"Riproduci dopo\",\n  \"@Play_Next\": {},\n  \"Add_To_Queue\": \"Aggiungi alla coda\",\n  \"@Add_To_Queue\": {},\n  \"Add_To_Favourites\": \"Aggiungi ai preferiti\",\n  \"@Add_To_Favourites\": {},\n  \"Remove_From_Favourites\": \"Rimuovi dai preferiti\",\n  \"@Remove_From_Favourites\": {},\n  \"Download\": \"Download\",\n  \"@Download\": {},\n  \"Add_To_Playlist\": \"Aggiungi alla playlist\",\n  \"@Add_To_Playlist\": {},\n  \"Start_Radio\": \"Avvia radio\",\n  \"@Start_Radio\": {},\n  \"Album\": \"Album\",\n  \"@Album\": {},\n  \"Rename\": \"Rinomina\",\n  \"@Rename\": {},\n  \"Add_To_Library\": \"Aggiungi alla libreria\",\n  \"@Add_To_Library\": {},\n  \"Remove_From_Library\": \"Rimuovi dalla libreria\",\n  \"@Remove_From_Library\": {},\n  \"Delete_Item_Message\": \"Sei sicuro di voler eliminare questo elemento?\",\n  \"@Delete_Item_Message\": {},\n  \"Equalizer\": \"Equalizzatore\",\n  \"@Equalizer\": {},\n  \"Sleep_Timer\": \"Timer di spegnimento\",\n  \"@Sleep_Timer\": {},\n  \"Create_Playlist\": \"Crea playlist\",\n  \"@Create_Playlist\": {},\n  \"Playlist_Name\": \"Nome playlist\",\n  \"@Playlist_Name\": {},\n  \"Create\": \"Crea\",\n  \"@Create\": {},\n  \"Import_Playlist\": \"Importa playlist\",\n  \"@Import_Playlist\": {},\n  \"Import\": \"Importa\",\n  \"@Import\": {},\n  \"Rename_Playlist\": \"Rinomina playlist\",\n  \"@Rename_Playlist\": {},\n  \"Done\": \"Fatto\",\n  \"@Done\": {},\n  \"Cancel\": \"Annulla\",\n  \"@Cancel\": {},\n  \"Confirm\": \"Conferma\",\n  \"@Confirm\": {},\n  \"Yes\": \"Sì\",\n  \"@Yes\": {},\n  \"No\": \"No\",\n  \"@No\": {},\n  \"Show_More\": \"Mostra di più\",\n  \"@Show_More\": {},\n  \"Show_Less\": \"Mostra meno\",\n  \"@Show_Less\": {},\n  \"Remove\": \"Rimuovi\",\n  \"@Remove\": {},\n  \"High\": \"Alta\",\n  \"@High\": {},\n  \"Low\": \"Bassa\",\n  \"@Low\": {},\n  \"Songs_Will_Start_Playing_Soon\": \"La riproduzione dei brani inizierà a breve.\",\n  \"@Songs_Will_Start_Playing_Soon\": {},\n  \"Remove_Message\": \"Sei sicuro di volerlo rimuovere?\",\n  \"@Remove_Message\": {},\n  \"Remove_From_YTMusic_Message\": \"Sei sicuro di volerlo rimuovere da YTMusic?\",\n  \"@Remove_From_YTMusic_Message\": {},\n  \"Remove_All_History_Message\": \"Sei sicuro di voler cancellare tutta la cronologia?\",\n  \"@Remove_All_History_Message\": {},\n  \"Copied_To_Clipboard\": \"Copiato negli appunti\",\n  \"@Copied_To_Clipboard\": {},\n  \"No_Internet_Connection\": \"Nessuna Connessione Internet\",\n  \"@No_Internet_Connection\": {},\n  \"Go_To_Downloads\": \"Vai a Download\",\n  \"@Go_To_Downloads\": {},\n  \"Retry\": \"Riprova\",\n  \"@Retry\": {},\n  \"Playlist_Not_Available\": \"Playlist non disponibile\",\n  \"@Playlist_Not_Available\": {},\n  \"Confirm_Delete_All_Message\": \"Sei sicuro di volerli eliminare?\",\n  \"@Confirm_Delete_All_Message\": {},\n  \"Downloading\": \"In download\",\n  \"@Downloading\": {},\n  \"Restore_Missing_Songs\": \"Ripristina brani mancanti\",\n  \"@Restore_Missing_Songs\": {},\n  \"Delete_All_Songs\": \"Elimina tutti i brani\",\n  \"@Delete_All_Songs\": {},\n  \"Download_Started\": \"Download avviato...\",\n  \"@Download_Started\": {},\n  \"Restoring_Missing_Songs\": \"Ripristino brani mancanti...\",\n  \"@Restoring_Missing_Songs\": {},\n  \"Deleting_Songs\": \"Eliminazione brani...\",\n  \"@Deleting_Songs\": {},\n  \"In_Progress\": \"In corso\",\n  \"@In_Progress\": {},\n  \"Queued\": \"In coda\",\n  \"@Queued\": {},\n  \"Queued_Count\": \"In coda ({count})\",\n  \"@Queued_Count\": {},\n  \"File_Not_Found\": \"File non trovato\",\n  \"@File_Not_Found\": {},\n  \"Play_All\": \"Riproduci\",\n  \"@Play_All\": {},\n  \"Rotate_Device\": \"Ruota il dispositivo per scrivere.\",\n  \"@Rotate_Device\": {},\n  \"View_Equalizer\": \"Riproduci un brano per vedere l'equalizzatore.\",\n  \"@View_Equalizer\": {},\n  \"No_Offline_Songs\": \"Nessun brano scaricato\",\n  \"@No_Offline_Songs\": {},\n  \"Edit\": \"Modifica\",\n  \"@Edit\": {},\n  \"Edit_Playlist\": \"Modifica Playlist\",\n  \"@Edit_Playlist\": {},\n  \"Select_Playlist_Icon\": \"Seleziona Icona Playlist\",\n  \"@Select_Playlist_Icon\": {},\n  \"Top_Results\": \"Risultati Principali\",\n  \"@Top_Results\": {},\n  \"Other_Results\": \"Altri Risultati\",\n  \"@Other_Results\": {}\n}"
  },
  {
    "path": "lib/l10n/intl_tr.arb",
    "content": "{\n    \"@@locale\": \"tr\",\n    \"Gyawun\": \"Gyawun\",\n    \"Next_Up\": \"Sıradaki\",\n    \"@Next_Up\": {},\n    \"Shuffle\": \"Karıştır\",\n    \"@Shuffle\": {},\n    \"@Gyawun\": {},\n    \"Home\": \"Ana Sayfa\",\n    \"@Home\": {},\n    \"Saved\": \"Kaydedilenler\",\n    \"@Saved\": {},\n    \"YTMusic\": \"YTMüzik\",\n    \"@YTMusic\": {},\n    \"Settings\": \"Ayarlar\",\n    \"@Settings\": {},\n    \"Search_Gyawun\": \"Gyawun Ara\",\n    \"@Search_Gyawun\": {},\n    \"Favourites\": \"Favoriler\",\n    \"@Favourites\": {},\n    \"Downloads\": \"İndirilenler\",\n    \"@Downloads\": {},\n    \"History\": \"Geçmiş\",\n    \"@History\": {},\n    \"nSongs\": \"{count, plural, =0{Şarkı Yok} =1{1 Şarkı} other{{count} Şarkı}}\",\n    \"@nSongs\": {\n        \"description\": \"Şarkı sayısı\",\n        \"placeholders\": {\n            \"count\": {\n                \"type\": \"num\",\n                \"format\": \"compact\"\n            }\n        }\n    },\n    \"Songs\": \"Şarkılar\",\n    \"@Songs\": {},\n    \"Albums\": \"Albümler\",\n    \"@Albums\": {},\n    \"Playlists\": \"Çalma Listeleri\",\n    \"@Playlists\": {},\n    \"Artists\": \"Sanatçılar\",\n    \"@Artists\": {},\n    \"Subscriptions\": \"Abonelikler\",\n    \"@Subscriptions\": {},\n    \"Search_Settings\": \"Arama Ayarları\",\n    \"@Search_Settings\": {},\n    \"Battery_Optimisation_title\": \"Pil Optimizasyonu Tespit Edildi\",\n    \"@Battery_Optimisation_title\": {},\n    \"Battery_Optimisation_message\": \"Gyawun un düzgün çalışması için pil optimizasyonunu devre dışı bırakmak için buraya tıklayın\",\n    \"@Battery_Optimisation_message\": {},\n    \"Donate\": \"Bağış Yap\",\n    \"@Donate\": {},\n    \"Donate_Message\": \"Gyawun un geliştirilmesini destekleyin\",\n    \"@Donate_Message\": {},\n    \"Payment_Methods\": \"Ödeme Yöntemleri\",\n    \"@Payment_Methods\": {},\n    \"Pay_With_UPI\": \"UPI ile Öde\",\n    \"@Pay_With_UPI\": {},\n    \"Support_Me_On_Kofi\": \"Ko-fi üzerinden bana destek ol\",\n    \"@Support_Me_On_Kofi\": {},\n    \"Buy_Me_A_Coffee\": \"Bana Bir Kahve Al\",\n    \"@Buy_Me_A_Coffee\": {},\n    \"Google_Account\": \"Google Hesabı\",\n    \"@Google_Account\": {},\n    \"Appearence\": \"Görünüm\",\n    \"Theme_Mode\": \"Tema Modu\",\n    \"@Theme_Mode\": {},\n    \"Window_Effect\": \"Pencere Efekti\",\n    \"@Window_Effect\": {},\n    \"Dynamic_Colors\": \"Dinamik Renkler\",\n    \"@Dynamic_Colors\": {},\n    \"@Appearence\": {},\n    \"Content\": \"İçerik\",\n    \"@Content\": {},\n    \"Country\": \"Ülke\",\n    \"@Country\": {},\n    \"Language\": \"Dil\",\n    \"@Language\": {},\n    \"Personalised_Content\": \"Kişiselleştirilmiş İçerik\",\n    \"@Personalised_Content\": {},\n    \"Enter_Visitor_Id\": \"Ziyaretçi Kimliği Girin\",\n    \"@Enter_Visitor_Id\": {},\n    \"Visitor_Id\": \"Ziyaretçi Kimliği\",\n    \"@Visitor_Id\": {},\n    \"Reset_Visitor_Id\": \"Ziyaretçi Kimliğini Sıfırla\",\n    \"@Reset_Visitor_Id\": {},\n    \"Audio_And_Playback\": \"Ses ve Yeniden Oynatma\",\n    \"@Audio_And_Playback\": {},\n    \"Loudness_And_Equalizer\": \"Ses Yüksekliği ve Ekolayzer\",\n    \"@Loudness_And_Equalizer\": {},\n    \"Loudness_Enhancer\": \"Ses Yükseltici\",\n    \"@Loudness_Enhancer\": {},\n    \"Enable_Equalizer\": \"Ekolayzeri Etkinleştir\",\n    \"@Enable_Equalizer\": {},\n    \"Streaming_Quality\": \"Yayın Kalitesi\",\n    \"@Streaming_Quality\": {},\n    \"DOwnload_Quality\": \"İndirme Kalitesi\",\n    \"@DOwnload_Quality\": {},\n    \"Skip_Silence\": \"Sessizliği Atla\",\n    \"@Skip_Silence\": {},\n    \"Enable_Playback_History\": \"Yeniden Oynatma Geçmişini Etkinleştir\",\n    \"@Enable_Playback_History\": {},\n    \"Delete_Playback_History\": \"Yeniden Oynatma Geçmişini Sil\",\n    \"@Delete_Playback_History\": {},\n    \"Delete_Playback_History_Confirm_Message\": \"Yeniden Oynatma Geçmişini silmek istediğinizden emin misiniz.\",\n    \"@Delete_Playback_History_Confirm_Message\": {},\n    \"Playback_History_Deleted\": \"Yeniden Oynatma Geçmişi Silindi\",\n    \"@Playback_History_Deleted\": {},\n    \"Enable_Search_History\": \"Arama Geçmişini Etkinleştir\",\n    \"@Enable_Search_History\": {},\n    \"Delete_Search_History\": \"Arama Geçmişini Sil\",\n    \"@Delete_Search_History\": {},\n    \"Delete_Search_History_Confirm_Message\": \"Arama Geçmişini silmek istediğinizden emin misiniz.\",\n    \"@Delete_Search_History_Confirm_Message\": {},\n    \"Search_History_Deleted\": \"Arama Geçmişi Silindi\",\n    \"@Search_History_Deleted\": {},\n    \"Backup_And_Restore\": \"Yedekle ve Geri Yükle\",\n    \"@Backup_And_Restore\": {},\n    \"Backup\": \"Yedekle\",\n    \"@Backup\": {},\n    \"Restore\": \"Geri Yükle\",\n    \"@Restore\": {},\n    \"Select_Backup\": \"Yedeği Seç\",\n    \"@Select_Backup\": {},\n    \"About\": \"Hakkında\",\n    \"@About\": {},\n    \"Name\": \"İsim\",\n    \"@Name\": {},\n    \"Version\": \"Versiyon\",\n    \"@Version\": {},\n    \"Developer\": \"Geliştirici\",\n    \"@Developer\": {},\n    \"Sheikh_Haziq\": \"Sheikh Haziq\",\n    \"@Sheikh_Haziq\": {},\n    \"Organisation\": \"Organizasyon\",\n    \"@Organisation\": {},\n    \"Jhelum_Corp\": \"Jhelum Corp\",\n    \"@Jhelum_Corp\": {},\n    \"Telegram\": \"Telegram\",\n    \"@Telegram\": {},\n    \"Contributors\": \"Katkıda Bulunanlar\",\n    \"@Contributors\": {},\n    \"Source_Code\": \"Kaynak Kodu\",\n    \"@Source_Code\": {},\n    \"Bug_Report\": \"Hata Raporu\",\n    \"@Bug_Report\": {},\n    \"Feature_Request\": \"Özellik İsteği\",\n    \"@Feature_Request\": {},\n    \"Made_In_Kashmir\": \"Keşmir de Yapıldı\",\n    \"@Made_In_Kashmir\": {},\n    \"Check_For_Update\": \"Güncellemeleri Kontrol Et\",\n    \"@Check_For_Update\": {},\n    \"Progress\": \"İlerleme\",\n    \"@Progress\": {},\n    \"Play_Next\": \"Sonraki Oynat\",\n    \"@Play_Next\": {},\n    \"Add_To_Queue\": \"Kuyruğa Ekle\",\n    \"@Add_To_Queue\": {},\n    \"Add_To_Favourites\": \"Favorilere Ekle\",\n    \"@Add_To_Favourites\": {},\n    \"Remove_From_Favourites\": \"Favorilerden Çıkar\",\n    \"@Remove_From_Favourites\": {},\n    \"Download\": \"İndir\",\n    \"@Download\": {},\n    \"Add_To_Playlist\": \"Çalma Listesine Ekle\",\n    \"@Add_To_Playlist\": {},\n    \"Start_Radio\": \"Radyo Başlat\",\n    \"@Start_Radio\": {},\n    \"Album\": \"Albüm\",\n    \"@Album\": {},\n    \"Rename\": \"Yeniden Adlandır\",\n    \"@Rename\": {},\n    \"Add_To_Library\": \"Kütüphaneye Ekle\",\n    \"@Add_To_Library\": {},\n    \"Remove_From_Library\": \"Kütüphaneden Çıkar\",\n    \"@Remove_From_Library\": {},\n    \"Delete_Item_Message\": \"Bu öğeyi silmek istediğinizden emin misiniz?\",\n    \"@Delete_Item_Message\": {},\n    \"Equalizer\": \"Ekolayzer\",\n    \"@Equalizer\": {},\n    \"Sleep_Timer\": \"Uyku Zamanlayıcı\",\n    \"@Sleep_Timer\": {},\n    \"Create_Playlist\": \"Çalma Listesi Oluştur\",\n    \"@Create_Playlist\": {},\n    \"Playlist_Name\": \"Çalma Listesi Adı\",\n    \"@Playlist_Name\": {},\n    \"Create\": \"Oluştur\",\n    \"@Create\": {},\n    \"Import_Playlist\": \"Çalma Listesi İçe Aktar\",\n    \"@Import_Playlist\": {},\n    \"Import\": \"İçe Aktar\",\n    \"@Import\": {},\n    \"Rename_Playlist\": \"Çalma Listesini Yeniden Adlandır\",\n    \"@Rename_Playlist\": {},\n    \"Done\": \"Tamam\",\n    \"@Done\": {},\n    \"Cancel\": \"İptal\",\n    \"@Cancel\": {},\n    \"Confirm\": \"Onayla\",\n    \"@Confirm\": {},\n    \"Yes\": \"Evet\",\n    \"@Yes\": {},\n    \"No\": \"Hayır\",\n    \"@No\": {},\n    \"Show_More\": \"Daha Fazla Göster\",\n    \"@Show_More\": {},\n    \"Show_Less\": \"Daha Az Göster\",\n    \"@Show_Less\": {},\n    \"Remove\": \"Kaldır\",\n    \"@Remove\": {},\n    \"High\": \"Yüksek\",\n    \"@High\": {},\n    \"Low\": \"Düşük\",\n    \"@Low\": {},\n    \"Songs_Will_Start_Playing_Soon\": \"Şarkılar yakında çalmaya başlayacak.\",\n    \"@Songs_Will_Start_Playing_Soon\": {},\n    \"Remove_Message\": \"Kaldırmak istediğinizden emin misiniz?\",\n    \"@Remove_Message\": {},\n    \"Remove_From_YTMusic_Message\": \"YTMüzikten kaldırmak istediğinizden emin misiniz?\",\n    \"@Remove_From_YTMusic_Message\": {},\n    \"Remove_All_History_Message\": \"Tüm geçmişi temizlemek istediğinizden emin misiniz?\",\n    \"@Remove_All_History_Message\": {},\n    \"Copied_To_Clipboard\": \"Panoya Kopyalandı\",\n    \"@Copied_To_Clipboard\": {}\n}"
  },
  {
    "path": "lib/l10n/intl_ur.arb",
    "content": "{\n    \"@@locale\": \"ur\",\n    \"Gyawun\": \"گیاون\",\n    \"Next_Up\": \"اگلا\",\n    \"@Next_Up\": {},\n    \"Shuffle\": \"بے ترتیب\",\n    \"@Shuffle\": {},\n    \"@Gyawun\": {},\n    \"Home\": \"ہوم\",\n    \"@Home\": {},\n    \"Saved\": \"محفوظ کردہ\",\n    \"@Saved\": {},\n    \"YTMusic\": \"وائی ٹی میوزک\",\n    \"@YTMusic\": {},\n    \"Settings\": \"ترتیبات\",\n    \"@Settings\": {},\n    \"Search_Gyawun\": \"گیاون تلاش کریں\",\n    \"@Search_Gyawun\": {},\n    \"Favourites\": \"پسندیدہ\",\n    \"@Favourites\": {},\n    \"Downloads\": \"ڈاؤن لوڈ\",\n    \"@Downloads\": {},\n    \"History\": \"تاریخ\",\n    \"@History\": {},\n    \"nSongs\": \"{count, plural, =0{کوئی گانے نہیں} =1{1 گانا} other{{count} گانے}}\",\n    \"@nSongs\": {\n        \"description\": \"گانوں کی تعداد\",\n        \"placeholders\": {\n            \"count\": {\n                \"type\": \"num\",\n                \"format\": \"compact\"\n            }\n        }\n    },\n    \"Songs\": \"گانے\",\n    \"@Songs\": {},\n    \"Albums\": \"البمز\",\n    \"@Albums\": {},\n    \"Playlists\": \"پلے لسٹس\",\n    \"@Playlists\": {},\n    \"Artists\": \"فنکار\",\n    \"@Artists\": {},\n    \"Subscriptions\": \"سبسکرپشنز\",\n    \"@Subscriptions\": {},\n    \"Search_Settings\": \"تلاش کی ترتیبات\",\n    \"@Search_Settings\": {},\n    \"Battery_Optimisation_title\": \"بیٹری کی اصلاح کا پتہ چلا\",\n    \"@Battery_Optimisation_title\": {},\n    \"Battery_Optimisation_message\": \"گیاون کو درست طریقے سے کام کرنے کے لئے بیٹری کی اصلاح کو غیر فعال کرنے کے لئے یہاں کلک کریں\",\n    \"@Battery_Optimisation_message\": {},\n    \"Donate\": \"عطیہ\",\n    \"@Donate\": {},\n    \"Donate_Message\": \"گیاون کی ترقی کی حمایت کریں\",\n    \"@Donate_Message\": {},\n    \"Payment_Methods\": \"ادائیگی کے طریقے\",\n    \"@Payment_Methods\": {},\n    \"Pay_With_UPI\": \"یو پی آئی کے ساتھ ادائیگی\",\n    \"@Pay_With_UPI\": {},\n    \"Support_Me_On_Kofi\": \"کوفی پر مجھے سپورٹ کریں\",\n    \"@Support_Me_On_Kofi\": {},\n    \"Buy_Me_A_Coffee\": \"مجھے ایک کافی خریدیں\",\n    \"@Buy_Me_A_Coffee\": {},\n    \"Google_Account\": \"گوگل اکاؤنٹ\",\n    \"@Google_Account\": {},\n    \"Appearence\": \"ظاہری شکل\",\n    \"Theme_Mode\": \"تھیم موڈ\",\n    \"@Theme_Mode\": {},\n    \"Window_Effect\": \"ونڈو ایفیکٹ\",\n    \"@Window_Effect\": {},\n    \"Dynamic_Colors\": \"ڈائنامک رنگ\",\n    \"@Dynamic_Colors\": {},\n    \"@Appearence\": {},\n    \"Content\": \"مواد\",\n    \"@Content\": {},\n    \"Country\": \"ملک\",\n    \"@Country\": {},\n    \"Language\": \"زبان\",\n    \"@Language\": {},\n    \"Personalised_Content\": \"ذاتی بنایا گیا مواد\",\n    \"@Personalised_Content\": {},\n    \"Enter_Visitor_Id\": \"وزیٹر آئی ڈی درج کریں\",\n    \"@Enter_Visitor_Id\": {},\n    \"Visitor_Id\": \"وزیٹر آئی ڈی\",\n    \"@Visitor_Id\": {},\n    \"Reset_Visitor_Id\": \"وزیٹر آئی ڈی دوبارہ ترتیب دیں\",\n    \"@Reset_Visitor_Id\": {},\n    \"Audio_And_Playback\": \"آڈیو اور پلے بیک\",\n    \"@Audio_And_Playback\": {},\n    \"Loudness_And_Equalizer\": \"لاؤڈنس اور ایکوالائزر\",\n    \"@Loudness_And_Equalizer\": {},\n    \"Loudness_Enhancer\": \"لاؤڈنس انہینسر\",\n    \"@Loudness_Enhancer\": {},\n    \"Enable_Equalizer\": \"ایکوالائزر کو فعال کریں\",\n    \"@Enable_Equalizer\": {},\n    \"Streaming_Quality\": \"سٹریمنگ کوالٹی\",\n    \"@Streaming_Quality\": {},\n    \"DOwnload_Quality\": \"ڈاؤن لوڈ کوالٹی\",\n    \"@DOwnload_Quality\": {},\n    \"Skip_Silence\": \"خاموشی کو چھوڑ دیں\",\n    \"@Skip_Silence\": {},\n    \"Enable_Playback_History\": \"پلے بیک ہسٹری کو فعال کریں\",\n    \"@Enable_Playback_History\": {},\n    \"Delete_Playback_History\": \"پلے بیک ہسٹری کو حذف کریں\",\n    \"@Delete_Playback_History\": {},\n    \"Delete_Playback_History_Confirm_Message\": \"کیا آپ واقعی پلے بیک ہسٹری کو حذف کرنا چاہتے ہیں؟\",\n    \"@Delete_Playback_History_Confirm_Message\": {},\n    \"Playback_History_Deleted\": \"پلے بیک ہسٹری حذف کر دی گئی\",\n    \"@Playback_History_Deleted\": {},\n    \"Enable_Search_History\": \"تلاش کی ہسٹری کو فعال کریں\",\n    \"@Enable_Search_History\": {},\n    \"Delete_Search_History\": \"تلاش کی ہسٹری کو حذف کریں\",\n    \"@Delete_Search_History\": {},\n    \"Delete_Search_History_Confirm_Message\": \"کیا آپ واقعی تلاش کی ہسٹری کو حذف کرنا چاہتے ہیں؟\",\n    \"@Delete_Search_History_Confirm_Message\": {},\n    \"Search_History_Deleted\": \"تلاش کی ہسٹری حذف کر دی گئی\",\n    \"@Search_History_Deleted\": {},\n    \"Backup_And_Restore\": \"بیک اپ اور بحالی\",\n    \"@Backup_And_Restore\": {},\n    \"Backup\": \"بیک اپ\",\n    \"@Backup\": {},\n    \"Restore\": \"بحال کریں\",\n    \"@Restore\": {},\n    \"Select_Backup\": \"بیک اپ منتخب کریں\",\n    \"@Select_Backup\": {},\n    \"About\": \"کے بارے میں\",\n    \"@About\": {},\n    \"Name\": \"نام\",\n    \"@Name\": {},\n    \"Version\": \"ورژن\",\n    \"@Version\": {},\n    \"Developer\": \"ڈویلپر\",\n    \"@Developer\": {},\n    \"Sheikh_Haziq\": \"شیخ ہازق\",\n    \"@Sheikh_Haziq\": {},\n    \"Organisation\": \"تنظیم\",\n    \"@Organisation\": {},\n    \"Jhelum_Corp\": \"جہلم کارپ\",\n    \"@Jhelum_Corp\": {},\n    \"Telegram\": \"ٹیلیگرام\",\n    \"@Telegram\": {},\n    \"Contributors\": \"شراکت دار\",\n    \"@Contributors\": {},\n    \"Source_Code\": \"سورس کوڈ\",\n    \"@Source_Code\": {},\n    \"Bug_Report\": \"بگ رپورٹ\",\n    \"@Bug_Report\": {},\n    \"Feature_Request\": \"خصوصیت کی درخواست\",\n    \"@Feature_Request\": {},\n    \"Made_In_Kashmir\": \"کشمیر میں بنایا گیا\",\n    \"@Made_In_Kashmir\": {},\n    \"Check_For_Update\": \"اپ ڈیٹ کے لئے چیک کریں\",\n    \"@Check_For_Update\": {},\n    \"Progress\": \"پیش رفت\",\n    \"@Progress\": {},\n    \"Play_Next\": \"اگلا چلائیں\",\n    \"@Play_Next\": {},\n    \"Add_To_Queue\": \"قطار میں شامل کریں\",\n    \"@Add_To_Queue\": {},\n    \"Add_To_Favourites\": \"پسندیدہ میں شامل کریں\",\n    \"@Add_To_Favourites\": {},\n    \"Remove_From_Favourites\": \"پسندیدہ سے ہٹائیں\",\n    \"@Remove_From_Favourites\": {},\n    \"Download\": \"ڈاؤن لوڈ\",\n    \"@Download\": {},\n    \"Add_To_Playlist\": \"پلے لسٹ میں شامل کریں\",\n    \"@Add_To_Playlist\": {},\n    \"Start_Radio\": \"ریڈیو شروع کریں\",\n    \"@Start_Radio\": {},\n    \"Album\": \"البم\",\n    \"@Album\": {},\n    \"Rename\": \"نام تبدیل کریں\",\n    \"@Rename\": {},\n    \"Add_To_Library\": \"لائبریری میں شامل کریں\",\n    \"@Add_To_Library\": {},\n    \"Remove_From_Library\": \"لائبریری سے ہٹائیں\",\n    \"@Remove_From_Library\": {},\n    \"Delete_Item_Message\": \"کیا آپ واقعی اس آئٹم کو حذف کرنا چاہتے ہیں؟\",\n    \"@Delete_Item_Message\": {},\n    \"Equalizer\": \"ایکولائزر\",\n    \"@Equalizer\": {},\n    \"Sleep_Timer\": \"سلیپ ٹائمر\",\n    \"@Sleep_Timer\": {},\n    \"Create_Playlist\": \"پلے لسٹ بنائیں\",\n    \"@Create_Playlist\": {},\n    \"Playlist_Name\": \"پلے لسٹ کا نام\",\n    \"@Playlist_Name\": {},\n    \"Create\": \"بنائیں\",\n    \"@Create\": {},\n    \"Import_Playlist\": \"پلے لسٹ درآمد کریں\",\n    \"@Import_Playlist\": {},\n    \"Import\": \"درآمد\",\n    \"@Import\": {},\n    \"Rename_Playlist\": \"پلے لسٹ کا نام تبدیل کریں\",\n    \"@Rename_Playlist\": {},\n    \"Done\": \"ہو گیا\",\n    \"@Done\": {},\n    \"Cancel\": \"منسوخ کریں\",\n    \"@Cancel\": {},\n    \"Confirm\": \"تصدیق کریں\",\n    \"@Confirm\": {},\n    \"Yes\": \"ہاں\",\n    \"@Yes\": {},\n    \"No\": \"نہیں\",\n    \"@No\": {},\n    \"Show_More\": \"مزید دکھائیں\",\n    \"@Show_More\": {},\n    \"Show_Less\": \"کم دکھائیں\",\n    \"@Show_Less\": {},\n    \"Remove\": \"ہٹائیں\",\n    \"@Remove\": {},\n    \"High\": \"زیادہ\",\n    \"@High\": {},\n    \"Low\": \"کم\",\n    \"@Low\": {},\n    \"Songs_Will_Start_Playing_Soon\": \"گانے جلد ہی چلنا شروع ہوجائیں گے۔\",\n    \"@Songs_Will_Start_Playing_Soon\": {},\n    \"Remove_Message\": \"کیا آپ واقعی اسے ہٹانا چاہتے ہیں؟\",\n    \"@Remove_Message\": {},\n    \"Remove_From_YTMusic_Message\": \"کیا آپ واقعی اسے YTMusic سے ہٹانا چاہتے ہیں؟\",\n    \"@Remove_From_YTMusic_Message\": {},\n    \"Remove_All_History_Message\": \"کیا آپ واقعی تمام تاریخ کو صاف کرنا چاہتے ہیں؟\",\n    \"@Remove_All_History_Message\": {},\n    \"Copied_To_Clipboard\": \"کلپ بورڈ پر کاپی کر دیا گیا\",\n    \"@Copied_To_Clipboard\": {}\n}"
  },
  {
    "path": "lib/main.dart",
    "content": "import 'dart:io';\n\nimport 'package:dynamic_color/dynamic_color.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_localizations/flutter_localizations.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/themes/theme.dart';\nimport 'package:hive_flutter/hive_flutter.dart';\nimport 'package:just_audio_background/just_audio_background.dart';\nimport 'package:just_audio_media_kit/just_audio_media_kit.dart';\nimport 'package:m3e_collection/m3e_collection.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:provider/provider.dart';\nimport 'package:yt_music/client.dart';\nimport 'package:yt_music/modals/yt_config.dart';\nimport 'package:yt_music/ytmusic.dart';\n\nimport 'generated/l10n.dart';\nimport 'services/download_manager.dart';\nimport 'services/favourites_manager.dart';\nimport 'services/file_storage.dart';\nimport 'services/history_manager.dart';\nimport 'services/library.dart';\nimport 'services/lyrics.dart';\nimport 'services/media_player.dart';\nimport 'services/settings_manager.dart';\nimport 'utils/router.dart';\n\nvoid main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n  if (Platform.isAndroid) {\n    await JustAudioBackground.init(\n      androidNotificationChannelId: 'com.jhelum.gyawun.audio',\n      androidNotificationChannelName: 'Audio playback',\n      androidNotificationOngoing: true,\n      // androidStopForegroundOnPause: false,\n    );\n  }\n\n  if (Platform.isWindows || Platform.isLinux) {\n    JustAudioMediaKit.ensureInitialized();\n    JustAudioMediaKit.bufferSize = 8 * 1024 * 1024;\n    JustAudioMediaKit.title = 'Gyawun Music';\n    JustAudioMediaKit.prefetchPlaylist = true;\n    JustAudioMediaKit.pitch = true;\n  }\n  await initialiseHive();\n  await SystemChrome.setEnabledSystemUIMode(\n    SystemUiMode.edgeToEdge,\n    overlays: [SystemUiOverlay.top],\n  );\n\n  await SystemChrome.setPreferredOrientations([\n    DeviceOrientation.portraitUp,\n    DeviceOrientation.portraitDown,\n    DeviceOrientation.landscapeLeft,\n    DeviceOrientation.landscapeRight,\n  ]);\n\n  SettingsManager settingsManager = await SettingsManager.create();\n  GetIt.I.registerSingleton<SettingsManager>(settingsManager);\n\n  final ytConfig = await getYtConfig(settingsManager);\n  YTMusic ytMusic = YTMusic(config: ytConfig!);\n  GetIt.I.registerSingleton<YTMusic>(ytMusic);\n\n  final GlobalKey<NavigatorState> panelKey = GlobalKey<NavigatorState>();\n  GetIt.I.registerSingleton(panelKey);\n\n  FileStorage fileStorage = await FileStorage.create();\n  GetIt.I.registerSingleton<FileStorage>(fileStorage);\n\n  MediaPlayer mediaPlayer = MediaPlayer();\n  GetIt.I.registerSingleton<MediaPlayer>(mediaPlayer);\n\n  LibraryService libraryService = await LibraryService.create();\n  GetIt.I.registerSingleton<LibraryService>(libraryService);\n\n  DownloadManager downloadManager = await DownloadManager.create();\n  GetIt.I.registerSingleton<DownloadManager>(downloadManager);\n\n  HistoryManager historyManager = await HistoryManager.create();\n  GetIt.I.registerSingleton<HistoryManager>(historyManager);\n\n  GetIt.I.registerSingleton<Lyrics>(Lyrics());\n\n  FavouritesManager favouritesManager = await FavouritesManager.create();\n  GetIt.I.registerSingleton<FavouritesManager>(favouritesManager);\n\n  runApp(\n    MultiProvider(\n      providers: [\n        ChangeNotifierProvider(create: (_) => settingsManager),\n        ChangeNotifierProvider(create: (_) => mediaPlayer),\n        ChangeNotifierProvider(create: (_) => libraryService),\n      ],\n      child: const Gyawun(),\n    ),\n  );\n}\n\nclass Gyawun extends StatelessWidget {\n  const Gyawun({super.key});\n  @override\n  Widget build(BuildContext context) {\n    final settings = context.select(\n      (SettingsManager s) => (\n        language: s.language['value']!,\n        themeMode: s.themeMode,\n        dynamicColors: s.dynamicColors,\n        accentColor: s.accentColor,\n        amoledBlack: s.amoledBlack,\n      ),\n    );\n    return DynamicColorBuilder(\n      builder: (lightScheme, darkScheme) {\n        final primaryColor =\n            (settings.dynamicColors && darkScheme != null\n                ? darkScheme.primary\n                : settings.accentColor) ??\n            Colors.red;\n        final isPureBlack = settings.amoledBlack;\n        return Shortcuts(\n          shortcuts: <LogicalKeySet, Intent>{\n            LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),\n          },\n          child: MaterialApp.router(\n            title: 'Gyawun Music',\n            routerConfig: router,\n            locale: Locale(settings.language),\n            localizationsDelegates: const [\n              S.delegate,\n              GlobalMaterialLocalizations.delegate,\n              GlobalCupertinoLocalizations.delegate,\n              GlobalWidgetsLocalizations.delegate,\n            ],\n            supportedLocales: S.delegate.supportedLocales,\n            debugShowCheckedModeBanner: false,\n            themeMode: settings.themeMode,\n            theme: ColorScheme.fromSeed(\n              seedColor: primaryColor,\n            ).toM3EThemeData(base: AppTheme.light(primary: primaryColor)),\n            darkTheme:\n                ColorScheme.fromSeed(\n                  brightness: Brightness.dark,\n                  surface: isPureBlack ? Colors.black : null,\n                  seedColor: primaryColor,\n                ).toM3EThemeData(\n                  base: AppTheme.dark(\n                    primary: primaryColor,\n                    isPureBlack: isPureBlack,\n                  ),\n                ),\n          ),\n        );\n      },\n    );\n  }\n}\n\nFuture<void> initialiseHive() async {\n  String? applicationDataDirectoryPath;\n  if (Platform.isWindows || Platform.isLinux) {\n    applicationDataDirectoryPath =\n        \"${(await getApplicationSupportDirectory()).path}/database\";\n  }\n  await Hive.initFlutter(applicationDataDirectoryPath);\n}\n\nFuture<YTConfig?>? getYtConfig(SettingsManager settingsManager) async {\n  final visitorId = settingsManager.visitorId;\n  final apiKey = settingsManager.apiKey;\n  final clientName = settingsManager.clientName;\n  final clientVersion = settingsManager.clientVersion;\n  if (visitorId == null ||\n      apiKey == null ||\n      clientName == null ||\n      clientVersion == null) {\n    final config = await YTClient.getConfig();\n    settingsManager.visitorId = visitorId ?? config?.visitorData;\n    settingsManager.apiKey = apiKey ?? config?.apiKey;\n    settingsManager.clientName = clientName ?? config?.clientName;\n    settingsManager.clientVersion = clientVersion ?? config?.clientVersion;\n    return config;\n  } else {\n    return YTConfig(\n      visitorData: visitorId,\n      language: settingsManager.language['value']!,\n      location: settingsManager.location['value']!,\n      apiKey: apiKey,\n      clientName: clientName,\n      clientVersion: clientVersion,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/browse/browse_page.dart",
    "content": "import 'package:cached_network_image/cached_network_image.dart';\nimport 'package:expandable_text/expandable_text.dart';\nimport 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/core/widgets/internet_guard.dart';\nimport 'package:gyawun/core/utils/service_locator.dart';\nimport 'package:gyawun/screens/browse/cubit/browse_cubit.dart';\nimport 'package:gyawun/core/widgets/section_item.dart';\nimport 'package:loading_indicator_m3e/loading_indicator_m3e.dart';\n\nimport '../../generated/l10n.dart';\nimport '../../services/bottom_message.dart';\nimport '../../services/library.dart';\nimport '../../services/media_player.dart';\nimport '../../utils/bottom_modals.dart';\nimport '../../utils/enhanced_image.dart';\nimport '../../utils/extensions.dart';\n\nclass BrowsePage extends StatelessWidget {\n  final Map<String, dynamic> endpoint;\n  final bool isMore;\n  const BrowsePage({super.key, required this.endpoint, this.isMore = false});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => BrowseCubit(sl(), endpoint: endpoint)..fetch(),\n      child: _BrowsePage(endpoint: endpoint, isMore: isMore),\n    );\n  }\n}\n\nclass _BrowsePage extends StatefulWidget {\n  const _BrowsePage({required this.endpoint, this.isMore = false});\n  final Map<String, dynamic> endpoint;\n  final bool isMore;\n\n  @override\n  State<_BrowsePage> createState() => _BrowsePageState();\n}\n\nclass _BrowsePageState extends State<_BrowsePage> {\n  late ScrollController _scrollController;\n\n  String? continuation;\n  @override\n  void initState() {\n    super.initState();\n    _scrollController = ScrollController();\n    _scrollController.addListener(_scrollListener);\n  }\n\n  // @override\n  // void didUpdateWidget(covariant _BrowsePage oldWidget) {\n  //   super.didUpdateWidget(oldWidget);\n  //   if (oldWidget.endpoint['browseId'] != widget.endpoint['browseId']) {\n  //     fetchData();\n  //   }\n  // }\n\n  Future<void> _scrollListener() async {\n    if (_scrollController.position.pixels ==\n        _scrollController.position.maxScrollExtent) {\n      await context.read<BrowseCubit>().fetchNext();\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return InternetGuard(\n      onConnectivityRestored: context.read<BrowseCubit>().fetch,\n      child: BlocBuilder<BrowseCubit, BrowseState>(\n        builder: (context, state) {\n          switch (state) {\n            case BrowseLoading():\n              return Scaffold(\n                backgroundColor: Theme.of(context).scaffoldBackgroundColor,\n                body: const Center(child: LoadingIndicatorM3E()),\n              );\n            case BrowseError():\n              return Center(child: Text(state.message ?? ''));\n            case BrowseSuccess():\n              final isAddedToLibrary =\n                  (state.header.containsKey('playlistId') &&\n                  context.watch<LibraryService>().getPlaylist(\n                        state.header['playlistId'],\n                      ) !=\n                      null);\n              return Scaffold(\n                appBar: AppBar(\n                  title: state.header['title'] != null\n                      ? Text(state.header['title'])\n                      : null,\n                  actionsPadding: .only(right: 8),\n                  actions: [\n                    if (state.header['privacy'] != 'PRIVATE' &&\n                        state.header.containsKey('playlistId') &&\n                        state.header['playlistId'] != 'LM')\n                      IconButton(\n                        icon: Icon(\n                          isAddedToLibrary\n                              ? Icons.bookmark_added\n                              : Icons.bookmark_add_outlined,\n                        ),\n                        onPressed: () {\n                          context\n                              .read<LibraryService>()\n                              .addToOrRemoveFromLibrary({\n                                'endpoint': widget.endpoint,\n                                ...state.header,\n                              })\n                              .then((String message) {\n                                if (context.mounted) {\n                                  BottomMessage.showText(context, message);\n                                }\n                              });\n                        },\n                      ),\n                  ],\n                ),\n                body: SingleChildScrollView(\n                  controller: _scrollController,\n                  child: Center(\n                    child: Container(\n                      padding: const EdgeInsets.only(\n                        left: 8,\n                        right: 8,\n                        bottom: 8,\n                      ),\n                      constraints: const BoxConstraints(maxWidth: 1000),\n                      child: Column(\n                        children: [\n                          if (state.header['thumbnails'] != null)\n                            HeaderWidget(\n                              header: {\n                                'endpoint': widget.endpoint,\n                                ...state.header,\n                              },\n                            ),\n                          const SizedBox(height: 8),\n                          ...state.sections.indexed.map((sec) {\n                            return SectionItem(\n                              section: sec.$2,\n                              isMore:\n                                  widget.isMore ||\n                                  state.sections.length == 1 ||\n                                  sec.$1 == 0,\n                            );\n                          }),\n                          if (!state.loadingMore && state.continuation != null)\n                            const SizedBox(height: 64),\n                          if (state.loadingMore)\n                            const Center(\n                              child: Padding(\n                                padding: EdgeInsets.all(8.0),\n                                child: ExpressiveLoadingIndicator(),\n                              ),\n                            ),\n                        ],\n                      ),\n                    ),\n                  ),\n                ),\n              );\n          }\n        },\n      ),\n    );\n  }\n}\n\nclass HeaderWidget extends StatefulWidget {\n  const HeaderWidget({super.key, required this.header});\n\n  final Map<String, dynamic> header;\n\n  @override\n  State<HeaderWidget> createState() => _HeaderWidgetState();\n}\n\nclass _HeaderWidgetState extends State<HeaderWidget> {\n  // late bool isAddedToLibrary;\n\n  @override\n  initState() {\n    super.initState();\n  }\n\n  Widget _buildImage(\n    BuildContext context,\n    List thumbnails,\n    double maxWidth, {\n    bool isRound = false,\n  }) {\n    return isRound\n        ? CircleAvatar(\n            backgroundImage: CachedNetworkImageProvider(\n              getEnhancedImage(\n                thumbnails.first['url'],\n                dp: MediaQuery.of(context).devicePixelRatio,\n                width: 250,\n              ),\n            ),\n            radius: 125,\n            backgroundColor: Theme.of(context).scaffoldBackgroundColor,\n          )\n        : ClipRRect(\n            borderRadius: BorderRadius.circular(8),\n            child: CachedNetworkImage(\n              imageUrl: getEnhancedImage(\n                thumbnails.last['url'],\n                dp: MediaQuery.of(context).devicePixelRatio,\n                width: 250,\n              ),\n              filterQuality: FilterQuality.high,\n              width: 250,\n              height: 250,\n            ),\n          );\n  }\n\n  Padding _buildContent(\n    Map header,\n    BuildContext context, {\n    bool isRow = false,\n  }) {\n    return Padding(\n      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),\n      child: Column(\n        crossAxisAlignment: isRow\n            ? CrossAxisAlignment.start\n            : CrossAxisAlignment.center,\n        mainAxisAlignment: isRow\n            ? MainAxisAlignment.start\n            : MainAxisAlignment.center,\n        children: [\n          if (header['subtitle'] != null)\n            Padding(\n              padding: const EdgeInsets.symmetric(vertical: 4),\n              child: Text(header['subtitle'] ?? '', maxLines: 2),\n            ),\n          if (header['secondSubtitle'] != null)\n            Padding(\n              padding: const EdgeInsets.symmetric(vertical: 4),\n              child: Text(header['secondSubtitle']),\n            ),\n          if (header['description'] != null)\n            Padding(\n              padding: const EdgeInsets.symmetric(vertical: 4),\n              child: ExpandableText(\n                header['description'].split('\\n')[0],\n                expandText: S.of(context).Show_More,\n                collapseText: S.of(context).Show_Less,\n                maxLines: isRow ? 3 : 2,\n                style: TextStyle(color: context.subtitleColor),\n                textAlign: TextAlign.center,\n              ),\n            ),\n          if (header['playlistId'] != null)\n            Padding(\n              padding: const EdgeInsets.symmetric(vertical: 4),\n              child: Wrap(\n                spacing: 4,\n                runSpacing: 8,\n                alignment: WrapAlignment.center,\n                runAlignment: WrapAlignment.center,\n                crossAxisAlignment: WrapCrossAlignment.center,\n                children: [\n                  if (header['videoId'] != null || header['playlistId'] != null)\n                    FilledButton.icon(\n                      style: const ButtonStyle(\n                        padding: WidgetStatePropertyAll(\n                          .symmetric(horizontal: 24, vertical: 16),\n                        ),\n                        shape: WidgetStatePropertyAll(\n                          RoundedRectangleBorder(\n                            borderRadius: .only(\n                              topLeft: .circular(24),\n                              bottomLeft: .circular(24),\n                              topRight: .circular(8),\n                              bottomRight: .circular(8),\n                            ),\n                          ),\n                        ),\n                      ),\n                      onPressed: () async {\n                        BottomMessage.showText(\n                          context,\n                          S.of(context).Songs_Will_Start_Playing_Soon,\n                        );\n                        await GetIt.I<MediaPlayer>().startPlaylistSongs(\n                          Map.from(header),\n                        );\n                      },\n                      label: const Text('Play All'),\n                      icon: const Icon(FluentIcons.play_24_filled),\n                    ),\n\n                  FilledButton(\n                    style: const ButtonStyle(\n                      padding: WidgetStatePropertyAll(\n                        .symmetric(horizontal: 8, vertical: 16),\n                      ),\n                      shape: WidgetStatePropertyAll(\n                        RoundedRectangleBorder(\n                          borderRadius: .only(\n                            topLeft: .circular(8),\n                            bottomLeft: .circular(8),\n                            topRight: .circular(24),\n                            bottomRight: .circular(24),\n                          ),\n                        ),\n                      ),\n                    ),\n                    child: const Icon(Icons.more_vert, size: 20),\n                    onPressed: () {\n                      Modals.showPlaylistBottomModal(context, header);\n                    },\n                  ),\n                ],\n              ),\n            ),\n        ],\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      width: double.maxFinite,\n      child: LayoutBuilder(\n        builder: (context, constraints) {\n          return constraints.maxWidth > 600\n              ? Row(\n                  children: [\n                    if (widget.header['thumbnails'] != null)\n                      _buildImage(\n                        context,\n                        widget.header['thumbnails'],\n                        constraints.maxWidth,\n                        isRound: widget.header['type'] == 'ARTIST',\n                      ),\n                    const SizedBox(width: 4),\n                    Expanded(\n                      child: _buildContent(widget.header, context, isRow: true),\n                    ),\n                  ],\n                )\n              : Column(\n                  children: [\n                    if (widget.header['thumbnails'] != null)\n                      _buildImage(\n                        context,\n                        widget.header['thumbnails'],\n                        constraints.maxWidth,\n                        isRound: widget.header['type'] == 'ARTIST',\n                      ),\n                    SizedBox(\n                      height: widget.header['thumbnails'] != null ? 4 : 0,\n                    ),\n                    _buildContent(widget.header, context),\n                  ],\n                );\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/browse/cubit/browse_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\nimport 'package:yt_music/ytmusic.dart';\n\npart 'browse_state.dart';\n\nclass BrowseCubit extends Cubit<BrowseState> {\n  final YTMusic _ytMusic;\n  final Map<String, dynamic> endpoint;\n  BrowseCubit(this._ytMusic, {required this.endpoint}) : super(BrowseLoading());\n  Future<void> fetch() async {\n    emit(const BrowseLoading());\n    try {\n      final feed = await _ytMusic.browse(body: endpoint, limit: 2);\n      emit(BrowseSuccess(\n        header: feed['header'] ?? {},\n        sections: feed['sections'],\n        continuation: feed['continuation'],\n        loadingMore: false,\n      ));\n    } catch (e, st) {\n      emit(BrowseError(e.toString(), st.toString()));\n    }\n  }\n\n  Future<void> fetchNext() async {\n    final current = state;\n    if (current is! BrowseSuccess) return;\n    if (current.loadingMore || current.continuation == null) return;\n    \n    emit(current.copyWith(loadingMore: true));\n    try {\n      final feed = await _ytMusic.browseContinuation(\n          additionalParams: current.continuation!);\n      emit(\n        BrowseSuccess(\n          header: current.header,\n          sections: [...current.sections, ...feed['sections']],\n          continuation: feed['continuation'],\n          loadingMore: false, \n        ),\n      );\n    } catch (e, st) {\n      emit(BrowseError(e.toString(), st.toString()));\n    }\n  }\n}\n"
  },
  {
    "path": "lib/screens/browse/cubit/browse_state.dart",
    "content": "part of 'browse_cubit.dart';\n\n@immutable\nsealed class BrowseState {\n  const BrowseState();\n}\n\nfinal class BrowseLoading extends BrowseState {\n  const BrowseLoading();\n}\n\nfinal class BrowseError extends BrowseState {\n  final String? message;\n  final String? stackTrace;\n  const BrowseError([this.message, this.stackTrace]);\n}\n\nfinal class BrowseSuccess extends BrowseState {\n  final Map<dynamic, dynamic> header;\n  final List sections;\n  final bool loadingMore;\n  final String? continuation;\n  const BrowseSuccess({\n    required this.header,\n    required this.sections,\n    required this.continuation,\n    required this.loadingMore,\n  });\n\n  BrowseSuccess copyWith({\n    Map<dynamic, dynamic>? header,\n    List? sections,\n    String? continuation,\n    bool? loadingMore,\n  }) {\n    return BrowseSuccess(\n      header: header ?? this.header,\n      sections: sections ?? this.sections,\n      continuation: continuation ?? this.continuation,\n      loadingMore: loadingMore ?? this.loadingMore,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/chip/chip_page.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/internet_guard.dart';\nimport 'package:gyawun/core/utils/service_locator.dart';\nimport 'package:gyawun/screens/chip/cubit/chip_cubit.dart';\nimport 'package:gyawun/core/widgets/section_item.dart';\nimport 'package:loading_indicator_m3e/loading_indicator_m3e.dart';\n\nclass ChipPage extends StatelessWidget {\n  const ChipPage({super.key, required this.title, required this.endpoint});\n  final String title;\n  final Map<String, dynamic> endpoint;\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (context) => ChipCubit(sl(), endpoint: endpoint)..fetch(),\n      child: _ChipPage(title: title),\n    );\n  }\n}\n\nclass _ChipPage extends StatelessWidget {\n  const _ChipPage({required this.title});\n\n  final String title;\n\n  @override\n  Widget build(BuildContext context) {\n    return InternetGuard(\n      onConnectivityRestored: context.read<ChipCubit>().fetch,\n      child: Scaffold(\n        body: NestedScrollView(\n          headerSliverBuilder: (context, innerBoxIsScrolled) {\n            return [ExpressiveAppBar(title: title, hasLeading: true)];\n          },\n          body: BlocBuilder<ChipCubit, ChipState>(\n            builder: (context, state) {\n              switch (state) {\n                case ChipLoading():\n                  return Center(child: LoadingIndicatorM3E());\n                case ChipError():\n                  return Center(child: Text(state.message ?? ''));\n                case ChipSuccess():\n                  return NotificationListener<ScrollNotification>(\n                    onNotification: (scrollInfo) {\n                      if (!state.loadingMore &&\n                          scrollInfo.metrics.pixels ==\n                              scrollInfo.metrics.maxScrollExtent) {\n                        context.read<ChipCubit>().fetchNext();\n                      }\n                      return false;\n                    },\n                    child: SafeArea(\n                      child: SingleChildScrollView(\n                        child: Column(\n                          children: [\n                            ...state.sections.map((section) {\n                              return SectionItem(section: section);\n                            }),\n                            if (state.loadingMore)\n                              const Padding(\n                                padding: EdgeInsets.all(8.0),\n                                child: ExpressiveLoadingIndicator(),\n                              ),\n                          ],\n                        ),\n                      ),\n                    ),\n                  );\n              }\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/chip/cubit/chip_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\nimport 'package:yt_music/ytmusic.dart';\n\npart 'chip_state.dart';\n\nclass ChipCubit extends Cubit<ChipState> {\n  final YTMusic _ytMusic;\n  final Map<String, dynamic> endpoint;\n  ChipCubit(this._ytMusic, {required this.endpoint}) : super(ChipLoading());\n\n  Future<void> fetch() async {\n    emit(const ChipLoading());\n    try {\n      final feed = await _ytMusic.browse(body: endpoint);\n      emit(ChipSuccess(\n        sections: feed['sections'],\n        continuation: feed['continuation'],\n        loadingMore: false,\n      ));\n    } catch (e, st) {\n      emit(ChipError(e.toString(), st.toString()));\n    }\n  }\n\n  Future<void> refresh() async {\n    try {\n      final feed = await _ytMusic.browse();\n      emit(ChipSuccess(\n        sections: feed['sections'],\n        continuation: feed['continuation'],\n        loadingMore: false,\n      ));\n    } catch (e, st) {\n      emit(ChipError(e.toString(), st.toString()));\n    }\n  }\n\n  Future<void> fetchNext() async {\n    final current = state;\n    if (current is! ChipSuccess) return;\n    if (current.loadingMore || current.continuation == null) return;\n    emit(current.copyWith(loadingMore: true));\n    try {\n      final feed = await _ytMusic.browseContinuation(\n          additionalParams: current.continuation!);\n      emit(\n        ChipSuccess(\n          sections: [...current.sections, ...feed['sections'] ?? []],\n          continuation: feed['continuation'],\n          loadingMore: false,\n        ),\n      );\n    } catch (e, st) {\n      emit(ChipError(e.toString(), st.toString()));\n    }\n  }\n}\n"
  },
  {
    "path": "lib/screens/chip/cubit/chip_state.dart",
    "content": "part of 'chip_cubit.dart';\n\n@immutable\nsealed class ChipState {\n  const ChipState();\n}\n\nfinal class ChipLoading extends ChipState {\n  const ChipLoading();\n}\n\nfinal class ChipError extends ChipState {\n  final String? message;\n  final String? stackTrace;\n  const ChipError([this.message, this.stackTrace]);\n}\n\nfinal class ChipSuccess extends ChipState {\n  final List sections;\n  final bool loadingMore;\n  final String? continuation;\n  const ChipSuccess({\n    required this.sections,\n    required this.continuation,\n    required this.loadingMore,\n  });\n\n  ChipSuccess copyWith({\n    List? sections,\n    String? continuation,\n    bool? loadingMore,\n  }) {\n    return ChipSuccess(\n      sections: sections ?? this.sections,\n      continuation: continuation ?? this.continuation,\n      loadingMore: loadingMore ?? this.loadingMore,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/home/cubit/home_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\nimport 'package:yt_music/ytmusic.dart';\n\npart 'home_state.dart';\n\nclass HomeCubit extends Cubit<HomeState> {\n  final YTMusic _ytMusic;\n  HomeCubit(this._ytMusic) : super(HomeLoading());\n\n  Future<void> fetch() async {\n    emit(const HomeLoading());\n    try {\n      final feed = await _ytMusic.browse();\n      emit(HomeSuccess(\n        chips: feed['chips'] ?? [],\n        sections: feed['sections'],\n        continuation: feed['continuation'],\n        loadingMore: false,\n      ));\n    } catch (e, st) {\n      emit(HomeError(e.toString(), st.toString()));\n    }\n  }\n\n  Future<void> refresh() async {\n    try {\n      final feed = await _ytMusic.browse();\n      emit(HomeSuccess(\n        chips: feed['chips'] ?? [],\n        sections: feed['sections'],\n        continuation: feed['continuation'],\n        loadingMore: false,\n      ));\n    } catch (e, st) {\n      emit(HomeError(e.toString(), st.toString()));\n    }\n  }\n\n  Future<void> fetchNext() async {\n    final current = state;\n    if (current is! HomeSuccess) return;\n    if (current.loadingMore || current.continuation == null) return;\n    emit(current.copyWith(loadingMore: true));\n    try {\n      final feed = await _ytMusic.browseContinuation(\n          additionalParams: current.continuation!);\n      emit(\n        HomeSuccess(\n          chips: current.chips,\n          sections: [...current.sections, ...feed['sections']],\n          continuation: feed['continuation'],\n          loadingMore: false,\n        ),\n      );\n    } catch (e, st) {\n      emit(HomeError(e.toString(), st.toString()));\n    }\n  }\n}\n"
  },
  {
    "path": "lib/screens/home/cubit/home_state.dart",
    "content": "part of 'home_cubit.dart';\n\n@immutable\nsealed class HomeState {\n  const HomeState();\n}\n\nfinal class HomeLoading extends HomeState {\n  const HomeLoading();\n}\n\nfinal class HomeError extends HomeState {\n  final String? message;\n  final String? stackTrace;\n  const HomeError([this.message, this.stackTrace]);\n}\n\nfinal class HomeSuccess extends HomeState {\n  final List chips;\n  final List sections;\n  final bool loadingMore;\n  final String? continuation;\n  const HomeSuccess({\n    required this.chips,\n    required this.sections,\n    required this.continuation,\n    required this.loadingMore,\n  });\n\n  HomeSuccess copyWith({\n    List? chips,\n    List? sections,\n    String? continuation,\n    bool? loadingMore,\n  }) {\n    return HomeSuccess(\n      chips: chips ?? this.chips,\n      sections: sections ?? this.sections,\n      continuation: continuation ?? this.continuation,\n      loadingMore: loadingMore ?? this.loadingMore,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/home/home_page.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/core/widgets/internet_guard.dart';\nimport 'package:gyawun/core/utils/service_locator.dart';\nimport 'package:gyawun/screens/home/cubit/home_cubit.dart';\nimport 'package:gyawun/core/widgets/section_item.dart';\nimport 'package:gyawun/screens/home/widgets/chips_row.dart';\nimport 'package:m3e_collection/m3e_collection.dart';\n\nimport '../../generated/l10n.dart';\nimport '../../utils/adaptive_widgets/adaptive_widgets.dart';\n\nclass HomePage extends StatelessWidget {\n  const HomePage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => HomeCubit(sl())..fetch(),\n      child: _HomePage(),\n    );\n  }\n}\n\nclass _HomePage extends StatefulWidget {\n  const _HomePage();\n\n  @override\n  State<_HomePage> createState() => _HomePageState();\n}\n\nclass _HomePageState extends State<_HomePage> {\n  late ScrollController _scrollController;\n\n  @override\n  void initState() {\n    super.initState();\n    _scrollController = ScrollController();\n    _scrollController.addListener(_scrollListener);\n  }\n\n  Future<void> _scrollListener() async {\n    if (_scrollController.position.pixels ==\n        _scrollController.position.maxScrollExtent) {\n      await context.read<HomeCubit>().fetchNext();\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return InternetGuard(\n      onConnectivityRestored: context.read<HomeCubit>().fetch,\n      child: Scaffold(\n        appBar: PreferredSize(\n          preferredSize: AppBar().preferredSize,\n          child: AppBar(\n            automaticallyImplyLeading: false,\n            title: Material(\n              color: Colors.transparent,\n              child: LayoutBuilder(\n                builder: (context, constraints) {\n                  return Row(\n                    children: [\n                      ConstrainedBox(\n                        constraints: BoxConstraints(\n                          maxWidth: constraints.maxWidth > 400\n                              ? (400)\n                              : constraints.maxWidth,\n                        ),\n                        child: AdaptiveTextField(\n                          onTap: () => context.go('/search'),\n                          readOnly: true,\n                          keyboardType: TextInputType.text,\n                          maxLines: 1,\n                          autofocus: false,\n                          textInputAction: TextInputAction.search,\n                          fillColor: Theme.of(context).colorScheme.surfaceContainer,\n                          contentPadding: const EdgeInsets.symmetric(\n                            vertical: 2,\n                            horizontal: 8,\n                          ),\n                          borderRadius: BorderRadius.circular(\n                            Platform.isWindows ? 4.0 : 35,\n                          ),\n                          hintText: S.of(context).Search_Gyawun,\n                          prefix: Icon(AdaptiveIcons.search),\n                        ),\n                      ),\n                    ],\n                  );\n                },\n              ),\n            ),\n            centerTitle: false,\n          ),\n        ),\n        body: ExpressiveRefreshIndicator(\n          onRefresh: context.read<HomeCubit>().refresh,\n          child: BlocBuilder<HomeCubit, HomeState>(\n            builder: (context, state) {\n              switch (state) {\n                case HomeLoading():\n                  return Center(child: LoadingIndicatorM3E());\n                case HomeError():\n                  return Center(child: Text(state.message ?? ''));\n                case HomeSuccess():\n                  return SingleChildScrollView(\n                    padding: const EdgeInsets.symmetric(vertical: 8),\n                    controller: _scrollController,\n                    child: SafeArea(\n                      child: Column(\n                        children: [\n                          ChipsRow(chips: state.chips),\n                          Column(\n                            children: [\n                              ...state.sections.map((section) {\n                                return SectionItem(section: section);\n                              }),\n                              if (!state.loadingMore &&\n                                  state.continuation != null)\n                                const SizedBox(height: 50),\n                              if (state.loadingMore)\n                                const Padding(\n                                  padding: EdgeInsets.all(8.0),\n                                  child: ExpressiveLoadingIndicator(),\n                                ),\n                            ],\n                          ),\n                        ],\n                      ),\n                    ),\n                  );\n              }\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/home/widgets/chips_row.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\n\nclass ChipsRow extends StatelessWidget {\n  const ChipsRow({super.key, required this.chips});\n  final List chips;\n\n  @override\n  Widget build(BuildContext context) {\n    return SingleChildScrollView(\n      padding: EdgeInsets.symmetric(horizontal: 12),\n      scrollDirection: Axis.horizontal,\n      child: Row(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: chips.map((chip) {\n          return Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 4),\n            child: FilledButton.tonal(\n              onPressed: () => context.go('/chip', extra: chip),\n              child: Text(chip['title']),\n            ),\n          );\n        }).toList(),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/cubit/library_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/download_manager.dart';\nimport 'package:gyawun/services/favourites_manager.dart';\n\nimport '../../../../services/library.dart';\nimport '../../../services/history_manager.dart';\n\npart 'library_state.dart';\n\nclass LibraryCubit extends Cubit<LibraryState> {\n  final LibraryService libraryService;\n\n  late final FavouritesManager _favouritesManager;\n  late final DownloadManager _downloadsManager;\n  late final SongHistory _songHistory;\n\n  late final VoidCallback _listener;\n\n  LibraryCubit(this.libraryService) : super(const LibraryLoading()) {\n    _favouritesManager = GetIt.I<FavouritesManager>();\n    _downloadsManager = GetIt.I<DownloadManager>();\n    _songHistory = GetIt.I<HistoryManager>().songs;\n\n    _listener = _emitCurrentState;\n\n    libraryService.addListener(_listener);\n    _favouritesManager.listenable.addListener(_listener);\n    _downloadsManager.downloadsNotifier.addListener(_listener);\n    _songHistory.listenable.addListener(_listener);\n  }\n\n  void loadLibrary() {\n    _emitCurrentState();\n  }\n\n  void _emitCurrentState() {\n    try {\n      final downloadedCount = _downloadsManager.downloads.length;\n\n      emit(\n        LibraryLoaded(\n          playlists: libraryService.playlists,\n          favourites: _favouritesManager.playlist,\n          downloadsCount: downloadedCount,\n          historyCount: _songHistory.count,\n        ),\n      );\n    } catch (e) {\n      emit(LibraryError(e.toString()));\n    }\n  }\n\n  @override\n  Future<void> close() {\n    libraryService.removeListener(_listener);\n    _favouritesManager.listenable.removeListener(_listener);\n    _downloadsManager.downloadsNotifier.removeListener(_listener);\n    _songHistory.listenable.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/cubit/library_state.dart",
    "content": "part of 'library_cubit.dart';\n\n@immutable\nsealed class LibraryState {\n  const LibraryState();\n}\n\nclass LibraryLoading extends LibraryState {\n  const LibraryLoading();\n}\n\nclass LibraryLoaded extends LibraryState {\n  final Map playlists;\n  final Map favourites;\n  final int downloadsCount;\n  final int historyCount;\n\n  const LibraryLoaded({\n    required this.playlists,\n    required this.favourites,\n    required this.downloadsCount,\n    required this.historyCount,\n  });\n}\n\nclass LibraryError extends LibraryState {\n  final String message;\n  const LibraryError(this.message);\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/cubit/downloads_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/download_manager.dart';\n\npart 'downloads_state.dart';\n\nclass DownloadsCubit extends Cubit<DownloadsState> {\n  final DownloadManager _manager = GetIt.I<DownloadManager>();\n\n  late final VoidCallback _listener;\n\n  DownloadsCubit() : super(const DownloadsLoading()) {\n    _listener = () {\n      if (!isClosed) {\n        _emitState();\n      }\n    };\n\n    _manager.playlistsNotifier.addListener(_listener);\n  }\n\n  void load() {\n    _emitState();\n  }\n\n  void _emitState() {\n    if (isClosed) return;\n\n    try {\n      emit(DownloadsLoaded(_manager.playlistsNotifier.value));\n    } catch (e) {\n      if (!isClosed) {\n        emit(DownloadsError(e.toString()));\n      }\n    }\n  }\n\n  @override\n  Future<void> close() {\n    _manager.playlistsNotifier.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/cubit/downloads_state.dart",
    "content": "part of 'downloads_cubit.dart';\n\n@immutable\nsealed class DownloadsState {\n  const DownloadsState();\n}\n\nclass DownloadsLoading extends DownloadsState {\n  const DownloadsLoading();\n}\n\nclass DownloadsLoaded extends DownloadsState {\n  final Map playlists;\n\n  const DownloadsLoaded(this.playlists);\n}\n\nclass DownloadsError extends DownloadsState {\n  final String message;\n  const DownloadsError(this.message);\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/downloading/cubit/downloading_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/download_manager.dart';\n\npart 'downloading_state.dart';\n\nclass DownloadingCubit extends Cubit<DownloadingState> {\n  final DownloadManager _manager = GetIt.I<DownloadManager>();\n\n  late final VoidCallback _listener;\n\n  DownloadingCubit() : super(const DownloadingLoading()) {\n    _listener = () {\n      if (!isClosed) {\n        _emitState();\n      }\n    };\n\n    _manager.downloadsNotifier.addListener(_listener);\n  }\n\n  void load() {\n    _emitState();\n  }\n\n  void _emitState() {\n    if (isClosed) return;\n\n    try {\n      final allSongs = _manager.downloadsNotifier.value;\n\n      final downloading = allSongs\n          .where((s) => s['status'] == 'DOWNLOADING')\n          .toList();\n\n      final queued = _manager.getDownloadQueue();\n\n      emit(DownloadingLoaded(downloading: downloading, queued: queued));\n    } catch (e) {\n      if (!isClosed) {\n        emit(DownloadingError(e.toString()));\n      }\n    }\n  }\n\n  @override\n  Future<void> close() {\n    _manager.downloadsNotifier.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/downloading/cubit/downloading_state.dart",
    "content": "part of 'downloading_cubit.dart';\n\n@immutable\nsealed class DownloadingState {\n  const DownloadingState();\n}\n\nclass DownloadingLoading extends DownloadingState {\n  const DownloadingLoading();\n}\n\nclass DownloadingLoaded extends DownloadingState {\n  final List downloading;\n  final List queued;\n\n  const DownloadingLoaded({\n    required this.downloading,\n    required this.queued,\n  });\n}\n\nclass DownloadingError extends DownloadingState {\n  final String message;\n  const DownloadingError(this.message);\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/downloading/downloading_page.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:gyawun/screens/library/downloads/downloading/widgets/downloading_section_tile.dart';\n\nimport '../../../../../generated/l10n.dart';\nimport 'cubit/downloading_cubit.dart';\nimport 'widgets/downloading_song_tile.dart';\n\nclass DownloadingPage extends StatelessWidget {\n  const DownloadingPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => DownloadingCubit()..load(),\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(S.of(context).Downloading),\n          centerTitle: true,\n        ),\n        body: BlocBuilder<DownloadingCubit, DownloadingState>(\n          builder: (context, state) {\n            return switch (state) {\n              DownloadingLoading() => const Center(\n                child: CircularProgressIndicator(),\n              ),\n              DownloadingError(:final message) => Center(child: Text(message)),\n              DownloadingLoaded(:final downloading, :final queued) =>\n                CustomScrollView(\n                  slivers: [\n                    if (downloading.isNotEmpty) ...[\n                      SliverToBoxAdapter(\n                        child: DownloadingSectionTile(\n                          title: S.of(context).In_Progress,\n                        ),\n                      ),\n                      SliverList(\n                        delegate: SliverChildBuilderDelegate(\n                          (context, index) =>\n                              DownloadingSongTile(song: downloading[index]),\n                          childCount: downloading.length,\n                        ),\n                      ),\n                    ],\n                    if (queued.isNotEmpty) ...[\n                      SliverToBoxAdapter(\n                        child: DownloadingSectionTile(\n                          title: S.of(context).Queued_Count(queued.length),\n                        ),\n                      ),\n                      SliverList(\n                        delegate: SliverChildBuilderDelegate(\n                          (context, index) =>\n                              DownloadingSongTile(song: queued[index]),\n                          childCount: queued.length,\n                        ),\n                      ),\n                    ],\n                  ],\n                ),\n            };\n          },\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/downloading/widgets/downloading_section_tile.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass DownloadingSectionTile extends StatelessWidget {\n  const DownloadingSectionTile({super.key, required this.title});\n  final String title;\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 8),\n      child: Text(\n        title,\n        style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n              fontWeight: FontWeight.bold,\n              color: Theme.of(context).colorScheme.primary,\n            ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/downloading/widgets/downloading_song_tile.dart",
    "content": "import 'package:cached_network_image/cached_network_image.dart';\nimport 'package:expandable_text/expandable_text.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/generated/l10n.dart';\nimport 'package:gyawun/services/download_manager.dart';\nimport 'package:gyawun/utils/adaptive_widgets/listtile.dart';\nimport 'package:gyawun/utils/extensions.dart';\n\nclass DownloadingSongTile extends StatelessWidget {\n  const DownloadingSongTile({required this.song, super.key});\n  final Map song;\n  @override\n  Widget build(BuildContext context) {\n    List thumbnails = song['thumbnails'];\n    double height =\n        (song['aspectRatio'] != null ? 50 / song['aspectRatio'] : 50)\n            .toDouble();\n    final notifier =\n        GetIt.I<DownloadManager>().getProgressNotifier(song['videoId']);\n    return AdaptiveListTile(\n      title: Text(song['title'] ?? \"\", maxLines: 1),\n      leading: ClipRRect(\n        borderRadius: BorderRadius.circular(3),\n        child: CachedNetworkImage(\n          imageUrl:\n              thumbnails.where((el) => el['width'] >= 50).toList().first['url'],\n          height: height,\n          width: 50,\n          fit: BoxFit.cover,\n        ),\n      ),\n      subtitle: (notifier != null)\n          ? ValueListenableBuilder(\n              valueListenable: notifier,\n              builder: (context, progress, child) => LinearProgressIndicator(\n                value: progress,\n                color: Theme.of(context).primaryColor,\n                backgroundColor: Theme.of(context)\n                    .colorScheme\n                    .onSurface\n                    .withValues(alpha: 0.3),\n              ),\n            )\n          : LinearProgressIndicator(\n              value: 0.0,\n              color: Theme.of(context).primaryColor,\n              backgroundColor: Theme.of(context)\n                  .colorScheme\n                  .onSurface\n                  .withValues(alpha: 0.3),\n            ),\n      description: song['type'] == 'EPISODE' && song['description'] != null\n          ? ExpandableText(\n              song['description'].split('\\n')?[0] ?? '',\n              expandText: S.of(context).Show_More,\n              collapseText: S.of(context).Show_Less,\n              maxLines: 3,\n              style: TextStyle(color: context.subtitleColor),\n            )\n          : null,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/downloads_page.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\nimport 'package:gyawun/services/download_manager.dart';\nimport 'package:gyawun/utils/adaptive_widgets/icons.dart';\n\nimport '../../../../generated/l10n.dart';\nimport '../../../../utils/bottom_modals.dart';\nimport '../../../../utils/playlist_thumbnail.dart';\nimport '../../../core/widgets/expressive_list_group.dart';\nimport '../../../services/favourites_manager.dart';\nimport 'cubit/downloads_cubit.dart';\n\nclass DownloadsPage extends StatelessWidget {\n  const DownloadsPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => DownloadsCubit()..load(),\n      child: Scaffold(\n        body: BlocBuilder<DownloadsCubit, DownloadsState>(\n          builder: (context, state) {\n            return switch (state) {\n              DownloadsLoading() => const Center(\n                child: CircularProgressIndicator(),\n              ),\n              DownloadsError(:final message) => Center(child: Text(message)),\n              DownloadsLoaded(:final playlists) => _DownloadsBody(\n                playlists: playlists,\n              ),\n            };\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass _DownloadsBody extends StatelessWidget {\n  const _DownloadsBody({required this.playlists});\n\n  final Map playlists;\n\n  @override\n  Widget build(BuildContext context) {\n    List<MapEntry> sortedEntries = playlists.entries.toList();\n\n    sortedEntries.sort((a, b) {\n      if (a.key == DownloadManager.songsPlaylistId) return -1;\n      if (b.key == DownloadManager.songsPlaylistId) return 1;\n      if (a.key == FavouritesManager.playlistId) return -1;\n      if (b.key == FavouritesManager.playlistId) return 1;\n      return a.value['title'].compareTo(b.value['title']);\n    });\n\n    return NestedScrollView(\n      headerSliverBuilder: (context, innerBoxIsScrolled) {\n        return [\n          ExpressiveAppBar(\n            title: S.of(context).Downloads,\n            hasLeading: true,\n            actions: [\n              IconButton(\n                onPressed: () {\n                  Modals.showDownloadBottomModal(context);\n                },\n                icon: const Icon(Icons.more_vert, size: 25),\n              ),\n            ],\n          ),\n        ];\n      },\n      body: Padding(\n        padding: const .symmetric(vertical: 4, horizontal: 16),\n        child: ExpressiveListGroup(\n          children: [\n            ...sortedEntries.map((entry) {\n              final playlist = entry.value;\n              return ExpressiveListTile(\n                title: playlist['id'] == DownloadManager.songsPlaylistId\n                    ? Text(S.of(context).Songs)\n                    : playlist['id'] == FavouritesManager.playlistId\n                    ? Text(S.of(context).Favourites)\n                    : Text(playlist['title']),\n                leading: _leading(context, playlist),\n                subtitle: Text(S.of(context).nSongs(playlist['songs'].length)),\n                trailing: const Icon(FluentIcons.chevron_right_24_filled),\n                onTap: () {\n                  context.push(\n                    '/library/downloads/download_playlist',\n                    extra: {'playlistId': playlist['id']},\n                  );\n                },\n                onLongPress: () {\n                  Modals.showDownloadDetailsBottomModal(context, playlist);\n                },\n              );\n            }),\n          ],\n        ),\n      ),\n    );\n  }\n\n  Widget _leading(BuildContext context, Map playlist) {\n    if (playlist['id'] == DownloadManager.songsPlaylistId ||\n        playlist['id'] == FavouritesManager.playlistId) {\n      return Container(\n        height: 40,\n        width: 40,\n        decoration: BoxDecoration(\n          color: Theme.of(context).colorScheme.primaryContainer,\n          borderRadius: BorderRadius.circular(8),\n        ),\n        child: Icon(\n          playlist['id'] == DownloadManager.songsPlaylistId\n              ? CupertinoIcons.music_note_list\n              : AdaptiveIcons.heart_fill,\n          color: Theme.of(context).colorScheme.onPrimaryContainer,\n        ),\n      );\n    }\n\n    if (playlist['type'] == 'ALBUM') {\n      return PlaylistThumbnail(playlist: [playlist['songs'][0]], size: 40);\n    }\n\n    return PlaylistThumbnail(playlist: playlist['songs'], size: 40);\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/playlist/cubit/download_playlist_cubit.dart",
    "content": "import 'dart:io';\n\nimport 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/download_manager.dart';\n\npart 'download_playlist_state.dart';\n\nclass DownloadPlaylistCubit extends Cubit<DownloadPlaylistState> {\n  final String playlistId;\n  final DownloadManager _manager = GetIt.I<DownloadManager>();\n\n  late final VoidCallback _listener;\n\n  DownloadPlaylistCubit(this.playlistId)\n    : super(const DownloadPlaylistLoading()) {\n    _listener = () {\n      if (!isClosed) {\n        _emitState();\n      }\n    };\n\n    _manager.playlistsNotifier.addListener(_listener);\n  }\n\n  void load() {\n    _verifyPlaylistIntegrity();\n    _emitState();\n  }\n\n  void _emitState() {\n    if (isClosed) return;\n\n    final allPlaylists = _manager.playlistsNotifier.value;\n    final playlist = allPlaylists[playlistId];\n\n    if (playlist == null || playlist['songs'] == null) {\n      emit(const DownloadPlaylistError('Playlist not available'));\n      return;\n    }\n\n    emit(\n      DownloadPlaylistLoaded(\n        playlist: playlist,\n        songs: List.from(playlist['songs']),\n      ),\n    );\n  }\n\n  Future<void> _verifyPlaylistIntegrity() async {\n    final allPlaylists = _manager.playlistsNotifier.value;\n    final playlist = allPlaylists[playlistId];\n    if (playlist == null) return;\n\n    final List songs = playlist['songs'] ?? [];\n\n    for (final song in songs) {\n      final path = song['path'];\n      if (path == null) continue;\n\n      final exists = await File(path).exists();\n      final status = song['status'];\n\n      if (!exists && status == 'DOWNLOADED') {\n        await _manager.updateStatus(song['videoId'], 'DELETED');\n      }\n    }\n  }\n\n  Future<void> removeSong(Map song) async {\n    await _manager.deleteSong(key: song['videoId'], playlistId: playlistId);\n  }\n\n  Map getCleanSong(Map song) {\n    return _manager.getCleanSong(song);\n  }\n\n  List? getDownloadedSongs(String? playlistId) {\n    return _manager.getDownloadedSongs(playlistId);\n  }\n\n  Future<void> restoreDownloads(List<Map> songs) async {\n    await _manager.restoreDownloads(songs: songs);\n  }\n\n  @override\n  Future<void> close() {\n    _manager.playlistsNotifier.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/playlist/cubit/download_playlist_state.dart",
    "content": "part of 'download_playlist_cubit.dart';\n\n@immutable\nsealed class DownloadPlaylistState {\n  const DownloadPlaylistState();\n}\n\nclass DownloadPlaylistLoading extends DownloadPlaylistState {\n  const DownloadPlaylistLoading();\n}\n\nclass DownloadPlaylistLoaded extends DownloadPlaylistState {\n  final Map playlist;\n  final List songs;\n\n  const DownloadPlaylistLoaded({\n    required this.playlist,\n    required this.songs,\n  });\n}\n\nclass DownloadPlaylistError extends DownloadPlaylistState {\n  final String message;\n  const DownloadPlaylistError(this.message);\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/playlist/download_playlist_page.dart",
    "content": "import 'dart:ui';\n\nimport 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_swipe_action_cell/core/cell.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/core/widgets/song_tile.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/themes/text_styles.dart';\nimport '../../../../../generated/l10n.dart';\nimport '../../../../../utils/bottom_modals.dart';\nimport '../../../../services/bottom_message.dart';\nimport '../../../../services/download_manager.dart';\nimport '../../../../services/favourites_manager.dart';\nimport '../../../../utils/adaptive_widgets/appbar.dart';\nimport '../../../../utils/adaptive_widgets/scaffold.dart';\nimport 'cubit/download_playlist_cubit.dart';\n\nclass DownloadPlaylistPage extends StatelessWidget {\n  const DownloadPlaylistPage({super.key, required this.playlistId});\n\n  final String playlistId;\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => DownloadPlaylistCubit(playlistId)..load(),\n      child: BlocBuilder<DownloadPlaylistCubit, DownloadPlaylistState>(\n        builder: (context, state) {\n          return switch (state) {\n            DownloadPlaylistLoading() => const Scaffold(\n              body: Center(child: CircularProgressIndicator()),\n            ),\n            DownloadPlaylistError() => AdaptiveScaffold(\n              appBar: AdaptiveAppBar(),\n              body: Center(child: Text(S.of(context).Playlist_Not_Available)),\n            ),\n            DownloadPlaylistLoaded(:final playlist, :final songs) =>\n              _PlaylistView(\n                playlist: playlist,\n                songs: songs,\n                playlistId: playlistId,\n              ),\n          };\n        },\n      ),\n    );\n  }\n}\n\nclass _PlaylistView extends StatelessWidget {\n  _PlaylistView({\n    required this.playlist,\n    required this.songs,\n    required this.playlistId,\n  });\n\n  final Map playlist;\n  final List songs;\n  final String playlistId;\n\n  final Map<String, _SongStatusConfig> statusMap = {\n    \"DELETED\": _SongStatusConfig(\n      onTap: (ctx, _) {\n        BottomMessage.showText(ctx, S.of(ctx).File_Not_Found);\n      },\n      onLongPress: (ctx, _) {\n        BottomMessage.showText(ctx, S.of(ctx).File_Not_Found);\n      },\n      icon: FluentIcons.arrow_circle_down_24_regular,\n      onIconPress: (ctx, song) {\n        ctx.read<DownloadPlaylistCubit>().restoreDownloads([song]);\n      },\n    ),\n    \"QUEUED\": _SongStatusConfig(\n      onTap: (ctx, _) {\n        BottomMessage.showText(ctx, S.of(ctx).Queued);\n      },\n      onLongPress: (ctx, _) {\n        BottomMessage.showText(ctx, S.of(ctx).Queued);\n      },\n      icon: FluentIcons.clock_24_regular,\n      onIconPress: (ctx, _) {\n        BottomMessage.showText(ctx, S.of(ctx).Queued);\n      },\n    ),\n    \"DOWNLOADING\": _SongStatusConfig(\n      onTap: (ctx, _) {\n        BottomMessage.showText(ctx, S.of(ctx).Downloading);\n      },\n      onLongPress: (ctx, _) {\n        BottomMessage.showText(ctx, S.of(ctx).Downloading);\n      },\n      icon: FluentIcons.arrow_sync_circle_24_regular,\n      onIconPress: (ctx, _) {\n        BottomMessage.showText(ctx, S.of(ctx).Downloading);\n      },\n    ),\n  };\n\n  @override\n  Widget build(BuildContext context) {\n    final downloadedSongs = context\n        .read<DownloadPlaylistCubit>()\n        .getDownloadedSongs(playlistId);\n    return Scaffold(\n      body: Center(\n        child: Container(\n          constraints: const BoxConstraints(maxWidth: 1000),\n          child: NestedScrollView(\n            headerSliverBuilder: (context, innerBoxIsScrolled) {\n              return [\n                SliverAppBar(\n                  pinned: true,\n                  expandedHeight: 120,\n                  flexibleSpace: LayoutBuilder(\n                    builder: (context, constraints) {\n                      final maxHeight = 120.0;\n                      final t = (constraints.maxHeight / (maxHeight + 30))\n                          .clamp(0.0, 1.0);\n                      final paddingLeft = lerpDouble(100, 16, t)!;\n\n                      return FlexibleSpaceBar(\n                        titlePadding: EdgeInsets.only(\n                          left: paddingLeft,\n                          bottom: 8,\n                        ),\n                        title: Column(\n                          mainAxisSize: MainAxisSize.min,\n                          crossAxisAlignment: CrossAxisAlignment.start,\n                          children: [\n                            Text(\n                              playlist.isNotEmpty &&\n                                      playlist['id'] ==\n                                          DownloadManager.songsPlaylistId\n                                  ? S.of(context).Songs\n                                  : playlist.isNotEmpty &&\n                                        playlist['id'] ==\n                                            FavouritesManager.playlistId\n                                  ? S.of(context).Favourites\n                                  : playlist.isNotEmpty\n                                  ? playlist['title']\n                                  : null,\n                              maxLines: 1,\n                              overflow: TextOverflow.ellipsis,\n                              style: textStyle(context).copyWith(fontSize: 16),\n                            ),\n                            SizedBox(height: 2),\n                            Text(\n                              S.of(context).nSongs(songs.length),\n                              maxLines: 1,\n                              overflow: TextOverflow.ellipsis,\n                              style: textStyle(context).copyWith(\n                                fontSize: 11,\n                                fontWeight: FontWeight.w600,\n                              ),\n                            ),\n                          ],\n                        ),\n                      );\n                    },\n                  ),\n                ),\n              ];\n            },\n            body: CustomScrollView(\n              slivers: [\n                SliverToBoxAdapter(\n                  child: Padding(\n                    padding: const EdgeInsets.all(16.0),\n                    child: Row(\n                      mainAxisAlignment: MainAxisAlignment.start,\n                      children: [\n                        FilledButton.icon(\n                          style: const ButtonStyle(\n                            padding: WidgetStatePropertyAll(\n                              .symmetric(horizontal: 24, vertical: 16),\n                            ),\n                            shape: WidgetStatePropertyAll(\n                              RoundedRectangleBorder(\n                                borderRadius: .only(\n                                  topRight: .circular(8),\n                                  bottomRight: .circular(8),\n                                  topLeft: .circular(24),\n                                  bottomLeft: .circular(24),\n                                ),\n                              ),\n                            ),\n                          ),\n                          onPressed: () {\n                            if (downloadedSongs == null ||\n                                downloadedSongs.isEmpty) {\n                              BottomMessage.showText(\n                                context,\n                                S.of(context).No_Offline_Songs,\n                              );\n                            } else {\n                              GetIt.I<MediaPlayer>().playAll(downloadedSongs);\n                            }\n                          },\n                          icon: const Icon(FluentIcons.play_24_filled),\n                          label: Text(S.of(context).Play_All),\n                        ),\n                        SizedBox(width: 4),\n                        FilledButton.tonalIcon(\n                          style: const ButtonStyle(\n                            padding: WidgetStatePropertyAll(\n                              .symmetric(horizontal: 24, vertical: 16),\n                            ),\n                            shape: WidgetStatePropertyAll(\n                              RoundedRectangleBorder(\n                                borderRadius: .only(\n                                  topLeft: .circular(8),\n                                  bottomLeft: .circular(8),\n                                  topRight: .circular(24),\n                                  bottomRight: .circular(24),\n                                ),\n                              ),\n                            ),\n                          ),\n\n                          onPressed: () {\n                            if (downloadedSongs == null ||\n                                downloadedSongs.isEmpty) {\n                              BottomMessage.showText(\n                                context,\n                                S.of(context).No_Offline_Songs,\n                              );\n                            } else {\n                              final shuffled = List.from(downloadedSongs);\n                              shuffled.shuffle();\n                              GetIt.I<MediaPlayer>().playAll(shuffled);\n                            }\n                          },\n                          icon: const Icon(FluentIcons.arrow_shuffle_24_filled),\n                          label: Text(S.of(context).Shuffle),\n                        ),\n                        SizedBox(width: 8),\n                        IconButton.filled(\n                          enableFeedback: true,\n                          onPressed: () {\n                            Modals.showDownloadDetailsBottomModal(\n                              context,\n                              playlist,\n                            );\n                          },\n                          icon: Icon(Icons.more_vert),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n                SliverList(\n                  delegate: SliverChildBuilderDelegate((context, index) {\n                    final song = songs[index];\n                    final config = statusMap[song['status']];\n                    return Padding(\n                      padding: const .symmetric(horizontal: 8, vertical: 4),\n                      child: SwipeActionCell(\n                        key: ObjectKey(song['videoId']),\n                        backgroundColor: Colors.transparent,\n                        trailingActions: [\n                          SwipeAction(\n                            title: S.of(context).Remove,\n                            color: Colors.red,\n                            onTap: (handler) async {\n                              final confirm =\n                                  await Modals.showConfirmBottomModal(\n                                    context,\n                                    message: S.of(context).Remove_Message,\n                                    isDanger: true,\n                                  );\n                              if (confirm && context.mounted) {\n                                await context\n                                    .read<DownloadPlaylistCubit>()\n                                    .removeSong(song);\n                              } else {\n                                handler(false);\n                              }\n                            },\n                          ),\n                        ],\n                        child: SongTile(\n                          song: context\n                              .read<DownloadPlaylistCubit>()\n                              .getCleanSong(song),\n                          onTap: config?.onTap,\n                          onLongPress: config?.onLongPress,\n                          icon: config?.icon,\n                          onIconPress: config?.onIconPress,\n                          isFirst: index == 0,\n                          isLast: index == songs.length - 1,\n                        ),\n                      ),\n                    );\n                  }, childCount: songs.length),\n                ),\n                const SliverToBoxAdapter(child: SizedBox(height: 20)),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass _SongStatusConfig {\n  final void Function(BuildContext, Map)? onTap;\n  final void Function(BuildContext, Map)? onLongPress;\n  final IconData icon;\n  final void Function(BuildContext, Map)? onIconPress;\n\n  const _SongStatusConfig({\n    required this.onTap,\n    required this.onLongPress,\n    required this.icon,\n    this.onIconPress,\n  });\n}\n"
  },
  {
    "path": "lib/screens/library/downloads/playlist/widgets/download_playlist_header.dart",
    "content": "// import 'dart:io';\n\n// import 'package:flutter/cupertino.dart';\n// import 'package:flutter/material.dart';\n// import 'package:get_it/get_it.dart';\n// import 'package:gyawun/generated/l10n.dart';\n// import 'package:gyawun/services/media_player.dart';\n// import 'package:gyawun/themes/colors.dart';\n// import 'package:gyawun/utils/adaptive_widgets/buttons.dart';\n// import 'package:gyawun/utils/bottom_modals.dart';\n// import 'package:gyawun/utils/extensions.dart';\n// import 'package:gyawun/utils/playlist_thumbnail.dart';\n\n// class DownloadPlaylistHeader extends StatelessWidget {\n//   const DownloadPlaylistHeader({\n//     super.key,\n//     required this.playlist,\n//     required this.imageType,\n//   });\n\n//   final Map playlist;\n//   final String imageType;\n\n//   Widget _buildImage(List songs, double maxWidth,\n//       {bool isRound = false, bool isDark = false}) {\n//     return (songs.isNotEmpty && imageType == \"SONGS\")\n//         ? Container(\n//             height: 200,\n//             width: 200,\n//             decoration: BoxDecoration(\n//               color: greyColor,\n//               borderRadius: BorderRadius.circular(3),\n//             ),\n//             child: Icon(\n//               CupertinoIcons.music_note_list,\n//               color: isDark ? Colors.white : Colors.black,\n//             ),\n//           )\n//         : (songs.isNotEmpty && imageType == \"ALBUM\")\n//             ? PlaylistThumbnail(playslist: [songs[0]], size: 225, radius: 8)\n//             : PlaylistThumbnail(playslist: songs, size: 225, radius: 8);\n//   }\n\n//   Padding _buildContent(Map playlist, BuildContext context,\n//       {bool isRow = false}) {\n//     return Padding(\n//       padding: const EdgeInsets.only(left: 8, top: 4),\n//       child: Column(\n//         crossAxisAlignment:\n//             isRow ? CrossAxisAlignment.start : CrossAxisAlignment.center,\n//         mainAxisAlignment:\n//             isRow ? MainAxisAlignment.start : MainAxisAlignment.center,\n//         children: [\n//           if (playlist['songs'] != null)\n//             Padding(\n//               padding: const EdgeInsets.symmetric(vertical: 4),\n//               child: Text(S.of(context).nSongs(playlist['songs'].length),\n//                   maxLines: 2),\n//             ),\n//           Wrap(\n//             spacing: 8,\n//             runSpacing: 8,\n//             alignment: WrapAlignment.center,\n//             runAlignment: WrapAlignment.center,\n//             crossAxisAlignment: WrapCrossAlignment.center,\n//             children: [\n//               if (playlist['songs'].isNotEmpty)\n//                 AdaptiveFilledButton(\n//                   onPressed: () {\n//                     GetIt.I<MediaPlayer>().playAll(playlist['songs']);\n//                   },\n//                   padding:\n//                       const EdgeInsets.symmetric(horizontal: 16, vertical: 10),\n//                   shape: RoundedRectangleBorder(\n//                     borderRadius:\n//                         BorderRadius.circular(Platform.isWindows ? 8 : 35),\n//                   ),\n//                   color: context.isDarkMode ? Colors.white : Colors.black,\n//                   child: Row(\n//                     mainAxisSize: MainAxisSize.min,\n//                     crossAxisAlignment: CrossAxisAlignment.center,\n//                     children: [\n//                       Icon(\n//                         Icons.play_arrow,\n//                         color: context.isDarkMode ? Colors.black : Colors.white,\n//                         size: 24,\n//                       ),\n//                       const SizedBox(width: 8),\n//                       const Text(\"Play All\", style: TextStyle(fontSize: 18))\n//                     ],\n//                   ),\n//                 ),\n//               AdaptiveFilledButton(\n//                 shape: const CircleBorder(),\n//                 color: greyColor,\n//                 padding: const EdgeInsets.all(14),\n//                 onPressed: () {\n//                   Modals.showDownloadDetailsBottomModal(context, playlist);\n//                 },\n//                 child: Icon(\n//                   Icons.more_vert,\n//                   size: 20,\n//                   color: context.isDarkMode ? Colors.white : Colors.black,\n//                 ),\n//               )\n//             ],\n//           )\n//         ],\n//       ),\n//     );\n//   }\n\n//   @override\n//   Widget build(BuildContext context) {\n//     return SizedBox(\n//       width: double.maxFinite,\n//       child: Card(\n//         child: LayoutBuilder(builder: (context, constraints) {\n//           return constraints.maxWidth > 600\n//               ? Row(\n//                   children: [\n//                     if (playlist['songs'] != null)\n//                       _buildImage(playlist['songs'], constraints.maxWidth,\n//                           isRound: playlist['type'] == 'ARTIST',\n//                           isDark: context.isDarkMode),\n//                     const SizedBox(width: 4),\n//                     Expanded(\n//                         child: _buildContent(playlist, context, isRow: true)),\n//                   ],\n//                 )\n//               : Column(\n//                   children: [\n//                     if (playlist['songs'] != null)\n//                       _buildImage(playlist['songs'], constraints.maxWidth,\n//                           isRound: playlist['type'] == 'ARTIST',\n//                           isDark: context.isDarkMode),\n//                     SizedBox(height: playlist['thumbnails'] != null ? 4 : 0),\n//                     _buildContent(playlist, context),\n//                   ],\n//                 );\n//         }),\n//       ),\n//     );\n//   }\n// }\n"
  },
  {
    "path": "lib/screens/library/downloads/playlist/widgets/download_song_tile.dart",
    "content": "// import 'package:expandable_text/expandable_text.dart';\n// import 'package:flutter/material.dart';\n// import 'package:get_it/get_it.dart';\n// import 'package:gyawun/generated/l10n.dart';\n// import 'package:gyawun/services/download_manager.dart';\n// import 'package:gyawun/services/media_player.dart';\n// import 'package:gyawun/utils/adaptive_widgets/listtile.dart';\n// import 'package:gyawun/utils/bottom_modals.dart';\n// import 'package:gyawun/utils/extensions.dart';\n// import 'package:gyawun/utils/song_thumbnail.dart';\n\n// class DownloadedSongTile extends StatelessWidget {\n//   const DownloadedSongTile({required this.song, super.key});\n//   final Map song;\n\n//   @override\n//   Widget build(BuildContext context) {\n//     double height =\n//         (song['aspectRatio'] != null ? 50 / song['aspectRatio'] : 50)\n//             .toDouble();\n//     return AdaptiveListTile(\n//       onTap: () async {\n//         if (song['videoId'] != null && song['status'] == 'DOWNLOADED') {\n//           await GetIt.I<MediaPlayer>().playSong(Map.from(song));\n//         }\n//       },\n//       onSecondaryTap: () {\n//         if (song['videoId'] != null && song['status'] == 'DOWNLOADED') {\n//           Modals.showSongBottomModal(context, song);\n//         }\n//       },\n//       onLongPress: () {\n//         if (song['videoId'] != null && song['status'] == 'DOWNLOADED') {\n//           Modals.showSongBottomModal(context, song);\n//         }\n//       },\n//       title: Text(song['title'] ?? \"\", maxLines: 1),\n//       leading: ClipRRect(\n//         borderRadius: BorderRadius.circular(3),\n//         child: SongThumbnail(\n//           song: song,\n//           height: height,\n//           width: 50,\n//           fit: BoxFit.cover,\n//         ),\n//       ),\n//       subtitle: Text(\n//         song['status'] == 'DELETED'\n//             ? S.of(context).FileNotFound\n//             : song['status'] == 'DOWNLOADING'\n//                 ? S.of(context).Downloading\n//                 : song['status'] == 'QUEUED'\n//                     ? S.of(context).Queued\n//                     : _buildSubtitle(song),\n//         maxLines: 1,\n//         style: TextStyle(\n//           color: song['status'] == 'DELETED'\n//               ? Colors.red\n//               : song['status'] == 'DOWNLOADING'\n//                   ? Theme.of(context).colorScheme.primary\n//                   : song['status'] == 'QUEUED'\n//                       ? Theme.of(context)\n//                           .colorScheme\n//                           .primary\n//                           .withValues(alpha: 0.5)\n//                       : Colors.grey.withAlpha(250),\n//         ),\n//         overflow: TextOverflow.ellipsis,\n//       ),\n//       trailing: song['status'] == 'DELETED'\n//           ? IconButton(\n//               onPressed: () {\n//                 GetIt.I<DownloadManager>().downloadSong(song);\n//               },\n//               icon: const Icon(Icons.refresh))\n//           : null,\n//       description: song['type'] == 'EPISODE' && song['description'] != null\n//           ? ExpandableText(\n//               song['description'].split('\\n')?[0] ?? '',\n//               expandText: S.of(context).Show_More,\n//               collapseText: S.of(context).Show_Less,\n//               maxLines: 3,\n//               style: TextStyle(color: context.subtitleColor),\n//             )\n//           : null,\n//     );\n//   }\n\n//   String _buildSubtitle(Map item) {\n//     List sub = [];\n//     if (sub.isEmpty && item['artists'] != null) {\n//       for (Map artist in item['artists']) {\n//         sub.add(artist['name']);\n//       }\n//     }\n//     if (sub.isEmpty && item['album'] != null) {\n//       sub.add(item['album']['name']);\n//     }\n//     String s = sub.join(' · ');\n//     return item['subtitle'] ?? s;\n//   }\n// }\n"
  },
  {
    "path": "lib/screens/library/favourites/cubit/favourites_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/favourites_manager.dart';\n\npart 'favourites_state.dart';\n\nclass FavouritesCubit extends Cubit<FavouritesState> {\n  late final FavouritesManager _manager;\n  late final VoidCallback _listener;\n\n  FavouritesCubit() : super(const FavouritesLoading()) {\n    _manager = GetIt.I<FavouritesManager>();\n\n    _listener = () {\n      if (!isClosed) {\n        _emitState();\n      }\n    };\n\n    _manager.listenable.addListener(_listener);\n  }\n\n  void load() {\n    _emitState();\n  }\n\n  void _emitState() {\n    if (isClosed) return;\n\n    try {\n      emit(FavouritesLoaded(_manager.playlist));\n    } catch (e) {\n      if (!isClosed) {\n        emit(FavouritesError(e.toString()));\n      }\n    }\n  }\n\n  Future<void> remove(dynamic key) async {\n    await _manager.remove(key);\n  }\n\n  @override\n  Future<void> close() {\n    _manager.listenable.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/favourites/cubit/favourites_state.dart",
    "content": "part of 'favourites_cubit.dart';\n\n@immutable\nsealed class FavouritesState {\n  const FavouritesState();\n}\n\nclass FavouritesLoading extends FavouritesState {\n  const FavouritesLoading();\n}\n\nclass FavouritesLoaded extends FavouritesState {\n  final Map favourites;\n\n  const FavouritesLoaded(this.favourites);\n}\n\nclass FavouritesError extends FavouritesState {\n  final String message;\n  const FavouritesError(this.message);\n}\n"
  },
  {
    "path": "lib/screens/library/favourites/favourites_page.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_swipe_action_cell/core/cell.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/song_tile.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/themes/text_styles.dart';\n\nimport '../../../../generated/l10n.dart';\nimport '../../../../utils/bottom_modals.dart';\nimport '../../../../utils/adaptive_widgets/adaptive_widgets.dart';\nimport 'cubit/favourites_cubit.dart';\n\nclass FavouritesPage extends StatelessWidget {\n  const FavouritesPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => FavouritesCubit()..load(),\n      child: Scaffold(\n        body: BlocBuilder<FavouritesCubit, FavouritesState>(\n          builder: (context, state) {\n            return switch (state) {\n              FavouritesLoading() => const Center(\n                child: AdaptiveProgressRing(),\n              ),\n              FavouritesError(:final message) => Center(child: Text(message)),\n              FavouritesLoaded(favourites: final playlist) => _FavouritesBody(\n                playlist: playlist,\n              ),\n            };\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass _FavouritesBody extends StatelessWidget {\n  const _FavouritesBody({required this.playlist});\n\n  final Map playlist;\n\n  @override\n  Widget build(BuildContext context) {\n    final songs = playlist['songs'];\n    return NestedScrollView(\n      headerSliverBuilder: (context, innerBoxIsScrolled) {\n        return [\n          ExpressiveAppBar(\n            hasLeading: true,\n            child: Column(\n              mainAxisSize: MainAxisSize.min,\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                Text(\n                  'Favourites',\n                  maxLines: 1,\n                  overflow: TextOverflow.ellipsis,\n                  style: textStyle(context).copyWith(fontSize: 16),\n                ),\n                SizedBox(height: 2),\n                Text(\n                  S.of(context).nSongs(songs.length),\n                  maxLines: 1,\n                  overflow: TextOverflow.ellipsis,\n                  style: textStyle(\n                    context,\n                  ).copyWith(fontSize: 11, fontWeight: FontWeight.w600),\n                ),\n              ],\n            ),\n          ),\n        ];\n      },\n      body: CustomScrollView(\n        slivers: [\n          SliverToBoxAdapter(\n            child: Padding(\n              padding: const EdgeInsets.all(16.0),\n              child: Row(\n                mainAxisAlignment: MainAxisAlignment.start,\n                children: [\n                  FilledButton.icon(\n                    style: const ButtonStyle(\n                      padding: WidgetStatePropertyAll(\n                        .symmetric(horizontal: 24, vertical: 16),\n                      ),\n                      shape: WidgetStatePropertyAll(\n                        RoundedRectangleBorder(\n                          borderRadius: .only(\n                            topRight: .circular(8),\n                            bottomRight: .circular(8),\n                            topLeft: .circular(24),\n                            bottomLeft: .circular(24),\n                          ),\n                        ),\n                      ),\n                    ),\n                    onPressed: () {\n                      if (songs.isEmpty) return;\n                      GetIt.I<MediaPlayer>().playAll(songs);\n                    },\n                    icon: const Icon(FluentIcons.play_24_filled),\n                    label: const Text('Play it'),\n                  ),\n                  SizedBox(width: 4),\n                  FilledButton.tonalIcon(\n                    style: const ButtonStyle(\n                      padding: WidgetStatePropertyAll(\n                        .symmetric(horizontal: 24, vertical: 16),\n                      ),\n                      shape: WidgetStatePropertyAll(\n                        RoundedRectangleBorder(\n                          borderRadius: .only(\n                            topLeft: .circular(8),\n                            bottomLeft: .circular(8),\n                            topRight: .circular(24),\n                            bottomRight: .circular(24),\n                          ),\n                        ),\n                      ),\n                    ),\n\n                    onPressed: () {\n                      if (songs.isEmpty) return;\n                      final shuffled = List.from(songs);\n                      shuffled.shuffle();\n\n                      GetIt.I<MediaPlayer>().playAll(shuffled);\n                    },\n                    icon: const Icon(FluentIcons.arrow_shuffle_24_filled),\n                    label: const Text('Shuffle'),\n                  ),\n                  SizedBox(width: 8),\n                  IconButton.filled(\n                    enableFeedback: true,\n                    onPressed: () {\n                      Modals.showFavouritesBottomModal(context, playlist);\n                    },\n                    icon: Icon(Icons.more_vert),\n                  ),\n                ],\n              ),\n            ),\n          ),\n          if (songs.isNotEmpty)\n            SliverList(\n              delegate: SliverChildBuilderDelegate((context, index) {\n                final song = songs[index];\n                return Padding(\n                  padding: const .symmetric(horizontal: 8, vertical: 4),\n                  child: SwipeActionCell(\n                    backgroundColor: Theme.of(\n                      context,\n                    ).colorScheme.surfaceContainerLowest,\n                    key: ObjectKey(song['videoId']),\n                    trailingActions: [\n                      SwipeAction(\n                        title: S.of(context).Remove,\n                        color: Colors.red,\n                        onTap: (handler) async {\n                          final confirm = await Modals.showConfirmBottomModal(\n                            context,\n                            message: S.of(context).Remove_Message,\n                            isDanger: true,\n                          );\n                          if (confirm && context.mounted) {\n                            await context.read<FavouritesCubit>().remove(song);\n                          } else {\n                            handler(false);\n                          }\n                        },\n                      ),\n                    ],\n                    child: SongTile(\n                      song: song,\n                      isFirst: index == 0,\n                      isLast: index == songs.length - 1,\n                    ),\n                  ),\n                );\n              }, childCount: songs.length),\n            ),\n          const SliverToBoxAdapter(child: SizedBox(height: 16)),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/history/cubit/history_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\n\nimport '../../../../services/history_manager.dart';\n\npart 'history_state.dart';\n\nclass HistoryCubit extends Cubit<HistoryState> {\n  late final SongHistory _songHistory;\n  late final VoidCallback _listener;\n\n  HistoryCubit() : super(const HistoryLoading()) {\n    _songHistory = GetIt.I<HistoryManager>().songs;\n\n    _listener = () {\n      if (!isClosed) {\n        _emitState();\n      }\n    };\n\n    _songHistory.listenable.addListener(_listener);\n  }\n\n  void load() {\n    _emitState();\n  }\n\n  void _emitState() {\n    if (isClosed) return;\n\n    try {\n      final songs = _songHistory.getList();\n      emit(HistoryLoaded(songs));\n    } catch (e) {\n      if (!isClosed) {\n        emit(HistoryError(e.toString()));\n      }\n    }\n  }\n\n  Future<void> remove(Map song) async {\n    await _songHistory.remove(song);\n  }\n\n  @override\n  Future<void> close() {\n    _songHistory.listenable.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/history/cubit/history_state.dart",
    "content": "part of 'history_cubit.dart';\n\n@immutable\nsealed class HistoryState {\n  const HistoryState();\n}\n\nclass HistoryLoading extends HistoryState {\n  const HistoryLoading();\n}\n\nclass HistoryLoaded extends HistoryState {\n  final List songs;\n  const HistoryLoaded(this.songs);\n}\n\nclass HistoryError extends HistoryState {\n  final String message;\n  const HistoryError(this.message);\n}\n"
  },
  {
    "path": "lib/screens/library/history/history_page.dart",
    "content": "import 'dart:ui';\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_swipe_action_cell/core/cell.dart';\nimport 'package:gyawun/core/widgets/song_tile.dart';\nimport 'package:gyawun/themes/text_styles.dart';\n\nimport '../../../../generated/l10n.dart';\nimport '../../../../utils/bottom_modals.dart';\nimport '../../../../utils/adaptive_widgets/adaptive_widgets.dart';\nimport 'cubit/history_cubit.dart';\n\nclass HistoryPage extends StatelessWidget {\n  const HistoryPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => HistoryCubit()..load(),\n      child: Scaffold(\n        body: BlocBuilder<HistoryCubit, HistoryState>(\n          builder: (context, state) {\n            return switch (state) {\n              HistoryLoading() => const Center(child: AdaptiveProgressRing()),\n              HistoryError(:final message) => Center(child: Text(message)),\n              HistoryLoaded(:final songs) => _HistoryBody(songs: songs),\n            };\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass _HistoryBody extends StatelessWidget {\n  const _HistoryBody({required this.songs});\n\n  final List songs;\n\n  @override\n  Widget build(BuildContext context) {\n    if (songs.isEmpty) {\n      return Center(child: Text(\"No History Found\"));\n    }\n\n    return Center(\n      child: Container(\n        constraints: const BoxConstraints(maxWidth: 1000),\n        padding: const EdgeInsets.symmetric(horizontal: 8),\n        child: NestedScrollView(\n          headerSliverBuilder: (context, innerBoxIsScrolled) {\n            return [\n              SliverAppBar(\n                pinned: true,\n                expandedHeight: 120,\n                flexibleSpace: LayoutBuilder(\n                  builder: (context, constraints) {\n                    final maxHeight = 120.0;\n                    final t = (constraints.maxHeight / (maxHeight + 30)).clamp(\n                      0.0,\n                      1.0,\n                    );\n                    final paddingLeft = lerpDouble(100, 16, t)!;\n\n                    return FlexibleSpaceBar(\n                      titlePadding: EdgeInsets.only(\n                        left: paddingLeft,\n                        bottom: 8,\n                      ),\n                      title: Column(\n                        mainAxisSize: MainAxisSize.min,\n                        crossAxisAlignment: CrossAxisAlignment.start,\n                        children: [\n                          Text(\n                            S.of(context).History,\n                            maxLines: 1,\n                            overflow: TextOverflow.ellipsis,\n                            style: textStyle(context).copyWith(fontSize: 16),\n                          ),\n                          SizedBox(height: 2),\n                          Text(\n                            S.of(context).nSongs(songs.length),\n                            maxLines: 1,\n                            overflow: TextOverflow.ellipsis,\n                            style: textStyle(context).copyWith(\n                              fontSize: 11,\n                              fontWeight: FontWeight.w600,\n                            ),\n                          ),\n                        ],\n                      ),\n                    );\n                  },\n                ),\n              ),\n            ];\n          },\n          body: CustomScrollView(\n            slivers: [\n              SliverList(\n                delegate: SliverChildBuilderDelegate((context, index) {\n                  final song = songs[index];\n                  return Padding(\n                    padding: const .symmetric(horizontal: 8, vertical: 4),\n                    child: SwipeActionCell(\n                      backgroundColor: Colors.transparent,\n                      key: ObjectKey(song['videoId']),\n                      trailingActions: [\n                        SwipeAction(\n                          title: S.of(context).Remove,\n                          color: Colors.red,\n                          onTap: (handler) async {\n                            final confirm = await Modals.showConfirmBottomModal(\n                              context,\n                              message: S.of(context).Remove_Message,\n                              isDanger: true,\n                            );\n\n                            if (confirm && context.mounted) {\n                              context.read<HistoryCubit>().remove(song);\n                            } else {\n                              handler(false);\n                            }\n                          },\n                        ),\n                      ],\n                      child: SongTile(\n                        song: song,\n                        isFirst: index == 0,\n                        isLast: index == songs.length - 1,\n                      ),\n                    ),\n                  );\n                }, childCount: songs.length),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/library_page.dart",
    "content": "import 'dart:collection';\n\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/expressive_list_group.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\nimport 'package:gyawun/core/widgets/internet_guard.dart';\nimport 'package:gyawun/core/utils/service_locator.dart';\nimport 'package:gyawun/screens/settings/widgets/color_icon.dart';\nimport '../../../../generated/l10n.dart';\nimport '../../../../services/library.dart';\nimport '../../../../utils/adaptive_widgets/adaptive_widgets.dart';\nimport '../../../../utils/bottom_modals.dart';\nimport '../../utils/playlist_icon_widget.dart';\nimport '../../utils/playlist_icons.dart';\nimport 'cubit/library_cubit.dart';\n\nclass LibraryPage extends StatelessWidget {\n  const LibraryPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => LibraryCubit(sl<LibraryService>())..loadLibrary(),\n      child: BlocBuilder<LibraryCubit, LibraryState>(\n        builder: (context, state) {\n          return InternetGuard(\n            child: Scaffold(\n              floatingActionButton: Column(\n                mainAxisSize: .min,\n                children: [\n                  FloatingActionButton.small(\n                    heroTag: 'import_playlist',\n                    onPressed: () {\n                      Modals.showImportplaylistModal(context);\n                    },\n                    child: const Icon(Icons.import_export_rounded),\n                  ),\n                  SizedBox(height: 8),\n                  FloatingActionButton(\n                    heroTag: 'create_playlist',\n\n                    onPressed: () {\n                      Modals.showCreateplaylistModal(context);\n                    },\n                    child: const Icon(FluentIcons.add_24_filled),\n                  ),\n                ],\n              ),\n              body: switch (state) {\n                LibraryLoading() => const Center(child: AdaptiveProgressRing()),\n                LibraryError(:final message) => Center(child: Text(message)),\n                LibraryLoaded(\n                  :final playlists,\n                  favourites: final favourites,\n                  :final downloadsCount,\n                  :final historyCount,\n                ) =>\n                  _LibraryBody(\n                    playlists: playlists,\n                    favourites: favourites,\n                    downloadsCount: downloadsCount,\n                    historyCount: historyCount,\n                  ),\n              },\n            ),\n          );\n        },\n      ),\n    );\n  }\n}\n\nclass _LibraryBody extends StatelessWidget {\n  const _LibraryBody({\n    required this.playlists,\n    required this.favourites,\n    this.downloadsCount = 0,\n    this.historyCount = 0,\n  });\n\n  final Map playlists;\n  final Map favourites;\n  final int downloadsCount;\n  final int historyCount;\n\n  @override\n  Widget build(BuildContext context) {\n    final favSongs = favourites['songs'];\n    return NestedScrollView(\n      headerSliverBuilder: (context, innerBoxIsScrolled) {\n        return [ExpressiveAppBar(title: \"Library\")];\n      },\n      body: CustomScrollView(\n        slivers: [\n          SliverPadding(\n            padding: const EdgeInsets.symmetric(horizontal: 8.0),\n            sliver: SliverToBoxAdapter(\n              child: Padding(\n                padding: const .symmetric(vertical: 4, horizontal: 8),\n                child: Column(\n                  children: [\n                    ExpressiveListGroup(\n                      title: \"Default\",\n                      children: [\n                        ExpressiveListTile(\n                          title: Text(S.of(context).Favourites),\n                          leading: ColorIcon(\n                            icon: FluentIcons.heart_24_filled,\n                            boxColor: Theme.of(\n                              context,\n                            ).colorScheme.primaryContainer,\n                            iconColor: Theme.of(\n                              context,\n                            ).colorScheme.onPrimaryContainer,\n                            size: 30,\n                          ),\n                          subtitle: Text(S.of(context).nSongs(favSongs.length)),\n                          trailing: Icon(FluentIcons.chevron_right_24_filled),\n                          onTap: () => context.push('/library/favourites'),\n                          onLongPress: () {\n                            Modals.showFavouritesBottomModal(\n                              context,\n                              favourites,\n                            );\n                          },\n                        ),\n                        ExpressiveListTile(\n                          title: Text(S.of(context).Downloads),\n                          leading: ColorIcon(\n                            icon: FluentIcons.cloud_arrow_down_24_filled,\n                            boxColor: Theme.of(\n                              context,\n                            ).colorScheme.primaryContainer,\n                            iconColor: Theme.of(\n                              context,\n                            ).colorScheme.onPrimaryContainer,\n                            size: 30,\n                          ),\n                          subtitle: Text(S.of(context).nSongs(downloadsCount)),\n                          trailing: Icon(FluentIcons.chevron_right_24_filled),\n                          onTap: () => context.push('/library/downloads'),\n                          onLongPress: () {\n                            Modals.showDownloadBottomModal(context);\n                          },\n                        ),\n                        ExpressiveListTile(\n                          title: Text(S.of(context).History),\n                          leading: ColorIcon(\n                            icon: FluentIcons.history_24_filled,\n                            boxColor: Theme.of(\n                              context,\n                            ).colorScheme.primaryContainer,\n                            iconColor: Theme.of(\n                              context,\n                            ).colorScheme.onPrimaryContainer,\n                            size: 30,\n                          ),\n                          subtitle: Text(S.of(context).nSongs(historyCount)),\n                          trailing: Icon(FluentIcons.chevron_right_24_filled),\n                          onTap: () => context.push('/library/history'),\n                        ),\n                      ],\n                    ),\n\n                    SizedBox(height: 17),\n                    if (playlists.isNotEmpty)\n                      ExpressiveListGroup(\n                        title: \"Custom\",\n                        children: SplayTreeMap.from(playlists)\n                            .map((key, item) {\n                              if (item == null) {\n                                return MapEntry(key, const SizedBox());\n                              }\n                              return MapEntry(\n                                key,\n                                Padding(\n                                  padding: const EdgeInsets.symmetric(\n                                    horizontal: 8,\n                                    vertical: 4,\n                                  ),\n                                  child: ExpressiveListTile(\n                                    title: Text(item['title']),\n                                    leading: _playlistLeading(\n                                      context,\n                                      key,\n                                      item,\n                                    ),\n                                    subtitle:\n                                        (item['songs'] != null ||\n                                            item['isPredefined'])\n                                        ? Text(\n                                            item['isPredefined'] == true\n                                                ? item['subtitle']\n                                                : S\n                                                      .of(context)\n                                                      .nSongs(\n                                                        item['songs'].length,\n                                                      ),\n                                          )\n                                        : null,\n                                    trailing: Icon(\n                                      FluentIcons.chevron_right_24_filled,\n                                    ),\n                                    onTap: () {\n                                      if (item['isPredefined'] == true) {\n                                        context.push(\n                                          '/browse',\n                                          extra: {\n                                            'endpoint': item['endpoint']\n                                                .cast<String, dynamic>(),\n                                          },\n                                        );\n                                      } else {\n                                        context.push(\n                                          '/library/playlist_details',\n                                          extra: {'playlistkey': key},\n                                        );\n                                      }\n                                    },\n                                    onLongPress: () =>\n                                        _showPlaylistMenu(context, key, item),\n                                  ),\n                                ),\n                              );\n                            })\n                            .values\n                            .toList(),\n                      ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  Widget _playlistLeading(BuildContext context, String key, Map item) {\n    if (item['isPredefined'] == true) {\n      return ClipRRect(\n        borderRadius: BorderRadius.circular(item['type'] == 'ARTIST' ? 30 : 8),\n        child: CachedNetworkImage(\n          imageUrl: item['thumbnails'].first['url'].replaceAll(\n            'w540-h225',\n            'w60-h60',\n          ),\n          height: 40,\n          width: 40,\n        ),\n      );\n    }\n    return Container(\n      padding: const EdgeInsets.all(6),\n      decoration: BoxDecoration(\n        color: Theme.of(context).colorScheme.primaryContainer,\n        borderRadius: BorderRadius.circular(8),\n      ),\n      child: PlaylistIconWidget(\n        data: PlaylistIcons.byId(item['iconId']),\n        size: 30,\n      ),\n    );\n  }\n\n  void _showPlaylistMenu(BuildContext context, String key, Map item) {\n    if (item['videoId'] == null && item['playlistId'] != null) {\n      Modals.showPlaylistBottomModal(context, item);\n    } else if (item['isPredefined'] == false) {\n      Modals.showPlaylistBottomModal(context, {...item, 'playlistId': key});\n    }\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/playlist/cubit/playlist_details_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/library.dart';\n\npart 'playlist_details_state.dart';\n\nclass PlaylistDetailsCubit extends Cubit<PlaylistDetailsState> {\n  final String playlistKey;\n  final LibraryService _library = GetIt.I<LibraryService>();\n\n  late final VoidCallback _listener;\n\n  PlaylistDetailsCubit(this.playlistKey)\n      : super(const PlaylistDetailsLoading()) {\n    _listener = () {\n      if (!isClosed) {\n        _emitState();\n      }\n    };\n\n    _library.addListener(_listener);\n  }\n\n  void load() {\n    _emitState();\n  }\n\n  void _emitState() {\n    if (isClosed) return;\n\n    try {\n      final playlist = _library.getPlaylist(playlistKey);\n\n      if (playlist == null) {\n        emit(const PlaylistDetailsError('Playlist not available'));\n        return;\n      }\n\n      emit(PlaylistDetailsLoaded(playlist));\n    } catch (e) {\n      if (!isClosed) {\n        emit(PlaylistDetailsError(e.toString()));\n      }\n    }\n  }\n\n  Future<String> removeSong(Map song) {\n    return _library.removeFromPlaylist(\n      item: song,\n      playlistId: playlistKey,\n    );\n  }\n\n  @override\n  Future<void> close() {\n    _library.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/playlist/cubit/playlist_details_state.dart",
    "content": "part of 'playlist_details_cubit.dart';\n\n@immutable\nsealed class PlaylistDetailsState {\n  const PlaylistDetailsState();\n}\n\nclass PlaylistDetailsLoading extends PlaylistDetailsState {\n  const PlaylistDetailsLoading();\n}\n\nclass PlaylistDetailsLoaded extends PlaylistDetailsState {\n  final Map playlist;\n  const PlaylistDetailsLoaded(this.playlist);\n}\n\nclass PlaylistDetailsError extends PlaylistDetailsState {\n  final String message;\n  const PlaylistDetailsError(this.message);\n}\n"
  },
  {
    "path": "lib/screens/library/playlist/playlist_details_page.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_swipe_action_cell/core/cell.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/song_tile.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/themes/text_styles.dart';\n\nimport '../../../../../generated/l10n.dart';\nimport '../../../../../utils/bottom_modals.dart';\nimport '../../../utils/adaptive_widgets/appbar.dart';\nimport '../../../utils/adaptive_widgets/scaffold.dart';\nimport 'cubit/playlist_details_cubit.dart';\n\nclass PlaylistDetailsPage extends StatelessWidget {\n  const PlaylistDetailsPage({super.key, required this.playlistkey});\n\n  final String playlistkey;\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => PlaylistDetailsCubit(playlistkey)..load(),\n      child: BlocBuilder<PlaylistDetailsCubit, PlaylistDetailsState>(\n        builder: (context, state) {\n          return switch (state) {\n            PlaylistDetailsLoading() => const Scaffold(\n              body: Center(child: CircularProgressIndicator()),\n            ),\n            PlaylistDetailsError() => AdaptiveScaffold(\n              appBar: AdaptiveAppBar(),\n              body: Center(child: Text(S.of(context).Playlist_Not_Available)),\n            ),\n            PlaylistDetailsLoaded(:final playlist) => _PlaylistView(\n              playlist: playlist,\n              playlistKey: playlistkey,\n            ),\n          };\n        },\n      ),\n    );\n  }\n}\n\nclass _PlaylistView extends StatelessWidget {\n  const _PlaylistView({required this.playlist, required this.playlistKey});\n\n  final Map playlist;\n  final String playlistKey;\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: NestedScrollView(\n        headerSliverBuilder: (context, innerBoxIsScrolled) {\n          return [\n            ExpressiveAppBar(\n              hasLeading: true,\n              child: Column(\n                mainAxisSize: MainAxisSize.min,\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: [\n                  Text(\n                    playlist['title'],\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: textStyle(context).copyWith(fontSize: 16),\n                  ),\n                  SizedBox(height: 2),\n                  Text(\n                    S.of(context).nSongs(playlist['songs'].length),\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: textStyle(\n                      context,\n                    ).copyWith(fontSize: 11, fontWeight: FontWeight.w600),\n                  ),\n                ],\n              ),\n            ),\n          ];\n        },\n        body: CustomScrollView(\n          slivers: [\n            SliverToBoxAdapter(\n              child: Padding(\n                padding: const EdgeInsets.all(16.0),\n                child: Row(\n                  mainAxisAlignment: MainAxisAlignment.start,\n                  children: [\n                    FilledButton.icon(\n                      style: const ButtonStyle(\n                        padding: WidgetStatePropertyAll(\n                          .symmetric(horizontal: 24, vertical: 16),\n                        ),\n                        shape: WidgetStatePropertyAll(\n                          RoundedRectangleBorder(\n                            borderRadius: .only(\n                              topRight: .circular(8),\n                              bottomRight: .circular(8),\n                              topLeft: .circular(24),\n                              bottomLeft: .circular(24),\n                            ),\n                          ),\n                        ),\n                      ),\n                      onPressed: () {\n                        GetIt.I<MediaPlayer>().playAll(playlist['songs']);\n                      },\n                      icon: const Icon(FluentIcons.play_24_filled),\n                      label: const Text('Play it'),\n                    ),\n                    SizedBox(width: 4),\n                    FilledButton.tonalIcon(\n                      style: const ButtonStyle(\n                        padding: WidgetStatePropertyAll(\n                          .symmetric(horizontal: 24, vertical: 16),\n                        ),\n                        shape: WidgetStatePropertyAll(\n                          RoundedRectangleBorder(\n                            borderRadius: .only(\n                              topLeft: .circular(8),\n                              bottomLeft: .circular(8),\n                              topRight: .circular(24),\n                              bottomRight: .circular(24),\n                            ),\n                          ),\n                        ),\n                      ),\n\n                      onPressed: () {\n                        final shuffled = List.from(playlist['songs']);\n                        shuffled.shuffle();\n\n                        GetIt.I<MediaPlayer>().playAll(shuffled);\n                      },\n                      icon: const Icon(FluentIcons.arrow_shuffle_24_filled),\n                      label: const Text('Shuffle'),\n                    ),\n                    SizedBox(width: 8),\n                    IconButton.filled(\n                      enableFeedback: true,\n                      onPressed: () {\n                        Modals.showPlaylistBottomModal(context, {\n                          ...playlist,\n                          'playlistId': playlistKey,\n                        });\n                      },\n                      icon: Icon(Icons.more_vert),\n                    ),\n                  ],\n                ),\n              ),\n            ),\n            if (playlist['songs'].isNotEmpty)\n              SliverList(\n                delegate: SliverChildBuilderDelegate((context, index) {\n                  final song = playlist['songs'][index];\n\n                  return Padding(\n                    padding: const .symmetric(horizontal: 8, vertical: 4),\n                    child: SwipeActionCell(\n                      key: ObjectKey(song['videoId']),\n                      backgroundColor: Colors.transparent,\n                      trailingActions: [\n                        SwipeAction(\n                          title: S.of(context).Remove,\n                          color: Colors.red,\n                          onTap: (handler) async {\n                            final confirm = await Modals.showConfirmBottomModal(\n                              context,\n                              message: S.of(context).Remove_Message,\n                              isDanger: true,\n                            );\n                            if (confirm && context.mounted) {\n                              await context\n                                  .read<PlaylistDetailsCubit>()\n                                  .removeSong(song);\n                            } else {\n                              handler(false);\n                            }\n                          },\n                        ),\n                      ],\n                      child: SongTile(\n                        song: song,\n                        isFirst: index == 0,\n                        isLast: index == playlist['songs'].length - 1,\n                      ),\n                    ),\n                  );\n                }, childCount: playlist['songs'].length),\n              ),\n\n            const SliverToBoxAdapter(child: SizedBox(height: 16)),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/library/widgets/my_playlist_header.dart",
    "content": "import 'dart:io';\nimport 'dart:math';\n\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/generated/l10n.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/themes/colors.dart';\nimport 'package:gyawun/utils/adaptive_widgets/buttons.dart';\nimport 'package:gyawun/utils/extensions.dart';\n\nclass MyPlayistHeader extends StatelessWidget {\n  const MyPlayistHeader({super.key, required this.playlist});\n\n  final Map playlist;\n\n  Widget _buildImage(\n    List songs,\n    double maxWidth, {\n    bool isRound = false,\n    bool isDark = false,\n  }) {\n    return (songs.isNotEmpty)\n        ? ClipRRect(\n            borderRadius: BorderRadius.circular(8),\n            child: SizedBox(\n              height: 225,\n              width: 225,\n              child: StaggeredGrid.count(\n                crossAxisCount: songs.length > 1 ? 2 : 1,\n                axisDirection: AxisDirection.down,\n                children: songs.sublist(0, min(songs.length, 4)).indexed.map((\n                  ind,\n                ) {\n                  int index = ind.$1;\n                  Map song = ind.$2;\n                  return CachedNetworkImage(\n                    imageUrl: song['thumbnails'].first['url']\n                        .replaceAll('w540-h225', 'w225-h225')\n                        .replaceAll('w60-h60', 'w225-h225'),\n                    height:\n                        (songs.length <= 2 || (songs.length == 3 && index == 0))\n                        ? 225\n                        : 225 / 2,\n                    width: 255 / 2,\n                    fit: BoxFit.cover,\n                  );\n                }).toList(),\n              ),\n            ),\n          )\n        : Container(\n            height: 200,\n            width: 200,\n            decoration: BoxDecoration(\n              color: greyColor,\n              borderRadius: BorderRadius.circular(3),\n            ),\n            child: Icon(\n              CupertinoIcons.music_note_list,\n              color: isDark ? Colors.white : Colors.black,\n            ),\n          );\n  }\n\n  Padding _buildContent(\n    Map playlist,\n    BuildContext context, {\n    bool isRow = false,\n  }) {\n    return Padding(\n      padding: const EdgeInsets.only(left: 8, top: 4),\n      child: Column(\n        crossAxisAlignment: isRow\n            ? CrossAxisAlignment.start\n            : CrossAxisAlignment.center,\n        mainAxisAlignment: isRow\n            ? MainAxisAlignment.start\n            : MainAxisAlignment.center,\n        children: [\n          if (playlist['songs'] != null)\n            Padding(\n              padding: const EdgeInsets.symmetric(vertical: 4),\n              child: Text(\n                S.of(context).nSongs(playlist['songs'].length),\n                maxLines: 2,\n              ),\n            ),\n          Wrap(\n            spacing: 8,\n            runSpacing: 8,\n            alignment: WrapAlignment.center,\n            runAlignment: WrapAlignment.center,\n            crossAxisAlignment: WrapCrossAlignment.center,\n            children: [\n              if (playlist['songs'].isNotEmpty)\n                AdaptiveFilledButton(\n                  onPressed: () {\n                    GetIt.I<MediaPlayer>().playAll(playlist['songs']);\n                  },\n                  padding: const EdgeInsets.symmetric(\n                    horizontal: 16,\n                    vertical: 10,\n                  ),\n                  shape: RoundedRectangleBorder(\n                    borderRadius: BorderRadius.circular(\n                      Platform.isWindows ? 8 : 35,\n                    ),\n                  ),\n                  color: context.isDarkMode ? Colors.white : Colors.black,\n                  child: Row(\n                    mainAxisSize: MainAxisSize.min,\n                    crossAxisAlignment: CrossAxisAlignment.center,\n                    children: [\n                      Icon(\n                        Icons.play_arrow,\n                        color: context.isDarkMode ? Colors.black : Colors.white,\n                        size: 24,\n                      ),\n                      const SizedBox(width: 8),\n                      Text(\n                        S.of(context).Play_All,\n                        style: TextStyle(fontSize: 18),\n                      ),\n                    ],\n                  ),\n                ),\n            ],\n          ),\n        ],\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      width: double.maxFinite,\n      child: Card(\n        child: LayoutBuilder(\n          builder: (context, constraints) {\n            return constraints.maxWidth > 600\n                ? Row(\n                    children: [\n                      if (playlist['songs'] != null)\n                        _buildImage(\n                          playlist['songs'],\n                          constraints.maxWidth,\n                          isRound: playlist['type'] == 'ARTIST',\n                          isDark: context.isDarkMode,\n                        ),\n                      const SizedBox(width: 4),\n                      Expanded(\n                        child: _buildContent(playlist, context, isRow: true),\n                      ),\n                    ],\n                  )\n                : Column(\n                    children: [\n                      if (playlist['songs'] != null)\n                        _buildImage(\n                          playlist['songs'],\n                          constraints.maxWidth,\n                          isRound: playlist['type'] == 'ARTIST',\n                          isDark: context.isDarkMode,\n                        ),\n                      SizedBox(height: playlist['thumbnails'] != null ? 4 : 0),\n                      _buildContent(playlist, context),\n                    ],\n                  );\n          },\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/player/player_page.dart",
    "content": "import 'dart:io';\nimport 'dart:math';\nimport 'dart:ui';\nimport 'package:audio_video_progress_bar/audio_video_progress_bar.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/screens/player/widgets/play_pause_button.dart';\nimport 'package:gyawun/utils/song_thumbnail.dart';\nimport 'package:just_audio/just_audio.dart';\nimport 'package:just_audio_background/just_audio_background.dart';\nimport 'package:provider/provider.dart';\nimport 'package:sliding_up_panel/sliding_up_panel.dart';\nimport 'package:text_scroll/text_scroll.dart';\nimport 'package:yt_music/ytmusic.dart';\n\nimport '../../generated/l10n.dart';\nimport '../../services/download_manager.dart';\nimport '../../services/favourites_manager.dart';\nimport '../../services/media_player.dart';\nimport '../../themes/colors.dart';\nimport '../../themes/dark.dart';\nimport '../../themes/text_styles.dart';\nimport '../../utils/adaptive_widgets/adaptive_widgets.dart';\nimport '../../utils/bottom_modals.dart';\nimport 'widgets/lyrics_box.dart';\nimport 'widgets/queue_list.dart';\n\nclass PlayerPage extends StatefulWidget {\n  const PlayerPage({super.key, this.videoId});\n  final String? videoId;\n\n  @override\n  State<PlayerPage> createState() => _PlayerPageState();\n}\n\nclass _PlayerPageState extends State<PlayerPage> {\n  late PanelController panelController;\n  final GlobalKey<ScaffoldState> _key = GlobalKey();\n  Color? color;\n  ImageProvider? image;\n  bool canPop = false;\n  bool showLyrics = false;\n  bool fetchedSong = false;\n  late MediaItem? currentSong;\n\n  @override\n  void initState() {\n    super.initState();\n    panelController = PanelController();\n    if (widget.videoId != null) {\n      GetIt.I<YTMusic>().getSongDetails(widget.videoId!).then((song) {\n        if (song != null) {\n          GetIt.I<MediaPlayer>().playSong(song);\n          setState(() {\n            fetchedSong = true;\n          });\n        }\n      });\n    }\n    currentSong = GetIt.I<MediaPlayer>().currentSongNotifier.value;\n    GetIt.I<MediaPlayer>().currentSongNotifier.addListener(songListener);\n  }\n\n  @override\n  dispose() {\n    GetIt.I<MediaPlayer>().currentSongNotifier.removeListener(songListener);\n    super.dispose();\n  }\n\n  void songListener() {\n    if (currentSong != GetIt.I<MediaPlayer>().currentSongNotifier.value) {\n      if (mounted) {\n        setState(() {\n          currentSong = GetIt.I<MediaPlayer>().currentSongNotifier.value;\n        });\n      }\n    }\n  }\n\n  void setShowLyrics() {\n    if (mounted) {\n      setState(() {\n        showLyrics = !showLyrics;\n      });\n    }\n  }\n\n  Future<void> updateBackgroundColor(ImageProvider image) async {\n    final c = await ColorScheme.fromImageProvider(provider: image);\n    if (mounted) {\n      setState(() {\n        color = c.primary;\n      });\n    }\n  }\n\n  MaterialColor primaryWhite = const MaterialColor(0xFFFFFFFF, <int, Color>{\n    50: Color(0xFFFFFFFF),\n    100: Color(0xFFFFFFFF),\n    200: Color(0xFFFFFFFF),\n    300: Color(0xFFFFFFFF),\n    400: Color(0xFFFFFFFF),\n    500: Color(0xFFFFFFFF),\n    600: Color(0xFFFFFFFF),\n    700: Color(0xFFFFFFFF),\n    800: Color(0xFFFFFFFF),\n    900: Color(0xFFFFFFFF),\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Theme(\n      data: darkTheme(\n        colorScheme: ColorScheme.fromSeed(\n          seedColor: primaryWhite,\n          primary: primaryWhite,\n          brightness: Brightness.dark,\n        ),\n      ),\n      child: (widget.videoId != null && fetchedSong == false)\n          ? const Center(child: AdaptiveProgressRing())\n          // ignore: deprecated_member_use\n          : WillPopScope(\n              onWillPop: () async {\n                if (panelController.isAttached && panelController.isPanelOpen) {\n                  await panelController.close();\n                  return false;\n                }\n                return true;\n              },\n              child: AnnotatedRegion<SystemUiOverlayStyle>(\n                value: const SystemUiOverlayStyle(\n                  statusBarBrightness: Brightness.dark,\n                  statusBarColor: Colors.transparent,\n                  statusBarIconBrightness: Brightness.light,\n                  systemNavigationBarColor: Colors.transparent,\n                ),\n                child: Container(\n                  color: Colors.black,\n                  child: AnimatedContainer(\n                    duration: const Duration(milliseconds: 300),\n                    curve: Curves.easeIn,\n                    decoration: BoxDecoration(\n                      gradient: LinearGradient(\n                        colors: [\n                          (color ??\n                                  Theme.of(\n                                    context,\n                                  ).colorScheme.surfaceContainerLow)\n                              .withAlpha(200),\n                          (color ??\n                                  Theme.of(\n                                    context,\n                                  ).colorScheme.surfaceContainerLow)\n                              .withAlpha(80),\n                        ],\n                        begin: Alignment.topLeft,\n                        end: Alignment.bottomRight,\n                      ),\n                    ),\n                    child: Scaffold(\n                      resizeToAvoidBottomInset: false,\n                      appBar: PreferredSize(\n                        preferredSize: AppBar().preferredSize,\n                        child: AppBar(\n                          backgroundColor: Colors.transparent,\n                          surfaceTintColor: Colors.transparent,\n                          elevation: 0,\n                          iconTheme: const IconThemeData(color: Colors.white),\n                          leading: AdaptiveIconButton(\n                            onPressed: () {\n                              context.pop();\n                            },\n                            icon: Icon(AdaptiveIcons.chevron_down),\n                          ),\n                          actions: [\n                            AdaptiveIconButton(\n                              onPressed: () {\n                                setState(() {\n                                  showLyrics = !showLyrics;\n                                });\n                              },\n                              icon: Icon(AdaptiveIcons.lyrics),\n                            ),\n                            if (MediaQuery.of(context).size.width >\n                                    MediaQuery.of(context).size.height ||\n                                Platform.isWindows)\n                              AdaptiveIconButton(\n                                onPressed: () {\n                                  _key.currentState?.openEndDrawer();\n                                },\n                                icon: Icon(AdaptiveIcons.queue),\n                              ),\n                          ],\n                        ),\n                      ),\n                      key: _key,\n                      backgroundColor: Colors.transparent,\n                      endDrawer:\n                          MediaQuery.of(context).size.width >\n                                  MediaQuery.of(context).size.height ||\n                              Platform.isWindows\n                          ? SizedBox(\n                              width:\n                                  min(400, MediaQuery.of(context).size.width) -\n                                  50,\n                              child: const QueueList(),\n                            )\n                          : null,\n                      body: SizedBox(\n                        width: double.maxFinite,\n                        child: LayoutBuilder(\n                          builder: (context, constraints) {\n                            EdgeInsets padding = MediaQuery.of(\n                              context,\n                            ).viewPadding;\n                            double maxWidth =\n                                constraints.maxWidth -\n                                padding.left -\n                                padding.right;\n                            double maxHeight =\n                                constraints.maxHeight -\n                                padding.top -\n                                padding.bottom;\n                            if (maxWidth > maxHeight) {\n                              return Row(\n                                crossAxisAlignment: CrossAxisAlignment.center,\n                                mainAxisAlignment:\n                                    MainAxisAlignment.spaceEvenly,\n                                children: [\n                                  Artwork(\n                                    setShowLyrics: setShowLyrics,\n                                    showLyrics: showLyrics,\n                                    width: maxWidth / 2.3,\n                                    song: currentSong,\n                                    onImageReady: updateBackgroundColor,\n                                  ),\n                                  NameAndControls(\n                                    song: currentSong,\n                                    width: maxWidth - (maxWidth / 2.3),\n                                    height: maxHeight,\n                                    isRow: true,\n                                  ),\n                                ],\n                              );\n                            }\n                            return Stack(\n                              children: [\n                                Column(\n                                  mainAxisAlignment:\n                                      MainAxisAlignment.spaceAround,\n                                  crossAxisAlignment: CrossAxisAlignment.center,\n                                  children: [\n                                    Artwork(\n                                      setShowLyrics: setShowLyrics,\n                                      showLyrics: showLyrics,\n                                      width:\n                                          min(maxWidth, maxHeight / 2.2) - 24,\n                                      song: currentSong,\n                                      onImageReady: updateBackgroundColor,\n                                    ),\n                                    NameAndControls(\n                                      song: currentSong,\n                                      width: maxWidth,\n                                      height:\n                                          maxHeight -\n                                          min(maxWidth, maxHeight / 2.2) -\n                                          24,\n                                    ),\n                                  ],\n                                ),\n                                SlidingUpPanel(\n                                  controller: panelController,\n                                  color: Colors.transparent,\n                                  padding: EdgeInsets.zero,\n                                  margin: EdgeInsets.zero,\n                                  borderRadius: const BorderRadius.only(\n                                    topLeft: Radius.circular(20),\n                                    topRight: Radius.circular(20),\n                                  ),\n                                  boxShadow: const [],\n                                  minHeight:\n                                      50 +\n                                      MediaQuery.of(context).viewPadding.bottom,\n                                  panel: ClipRRect(\n                                    borderRadius: const BorderRadius.only(\n                                      topLeft: Radius.circular(20),\n                                      topRight: Radius.circular(20),\n                                    ),\n                                    child: Container(\n                                      width: constraints.maxWidth,\n                                      alignment: Alignment.center,\n                                      decoration: const BoxDecoration(\n                                        borderRadius: BorderRadius.only(\n                                          topLeft: Radius.circular(20),\n                                          topRight: Radius.circular(20),\n                                        ),\n                                      ),\n                                      child: Column(\n                                        crossAxisAlignment:\n                                            CrossAxisAlignment.center,\n                                        mainAxisSize: MainAxisSize.max,\n                                        children: [\n                                          ClipRRect(\n                                            child: BackdropFilter(\n                                              filter: ImageFilter.blur(\n                                                sigmaX: 3,\n                                                sigmaY: 3,\n                                              ),\n                                              child: Container(\n                                                height:\n                                                    50 +\n                                                    MediaQuery.of(\n                                                      context,\n                                                    ).viewPadding.bottom,\n                                                width: double.maxFinite,\n                                                decoration: BoxDecoration(\n                                                  color: Theme.of(context)\n                                                      .scaffoldBackgroundColor\n                                                      .withAlpha(70),\n                                                  borderRadius:\n                                                      const BorderRadius.only(\n                                                        topLeft:\n                                                            Radius.circular(20),\n                                                        topRight:\n                                                            Radius.circular(20),\n                                                      ),\n                                                ),\n                                                child: Column(\n                                                  mainAxisSize:\n                                                      MainAxisSize.max,\n                                                  mainAxisAlignment:\n                                                      MainAxisAlignment.center,\n                                                  children: [\n                                                    Container(\n                                                      height: 5,\n                                                      width: 50,\n                                                      decoration: BoxDecoration(\n                                                        color: greyColor,\n                                                        borderRadius:\n                                                            BorderRadius.circular(\n                                                              20,\n                                                            ),\n                                                      ),\n                                                    ),\n                                                    const SizedBox(height: 8),\n                                                    Text(\n                                                      S.of(context).Next_Up,\n                                                      style: textStyle(\n                                                        context,\n                                                        bold: true,\n                                                      ),\n                                                    ),\n                                                  ],\n                                                ),\n                                              ),\n                                            ),\n                                          ),\n                                          const Expanded(child: QueueList()),\n                                        ],\n                                      ),\n                                    ),\n                                  ),\n                                ),\n                              ],\n                            );\n                          },\n                        ),\n                      ),\n                    ),\n                  ),\n                ),\n              ),\n            ),\n    );\n  }\n}\n\nclass Artwork extends StatelessWidget {\n  const Artwork({\n    this.song,\n    required this.width,\n    required this.showLyrics,\n    required this.setShowLyrics,\n    this.onImageReady,\n    super.key,\n  });\n  final double width;\n  final MediaItem? song;\n  final bool showLyrics;\n  final Function setShowLyrics;\n  final void Function(ImageProvider)? onImageReady;\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      width: width,\n      height: width,\n      child: Padding(\n        padding: const EdgeInsets.all(8.0),\n        child: song == null\n            ? Icon(Icons.music_note, size: width * 0.5)\n            : Padding(\n                padding: MediaQuery.of(context).viewPadding,\n                child: LayoutBuilder(\n                  builder: (context, constraints) {\n                    return GestureDetector(\n                      onTap: () {\n                        setShowLyrics();\n                      },\n                      child: Center(\n                        child: showLyrics\n                            ? LyricsBox(\n                                currentSong: song!,\n                                size: Size(width, width),\n                              )\n                            : Container(\n                                decoration: BoxDecoration(\n                                  borderRadius: BorderRadius.circular(8),\n                                  boxShadow: [\n                                    BoxShadow(\n                                      color: Colors.black.withAlpha(30),\n                                      spreadRadius: 10,\n                                      blurRadius: 10,\n                                      offset: const Offset(0, 3),\n                                    ),\n                                  ],\n                                ),\n                                child: ClipRRect(\n                                  borderRadius: BorderRadius.circular(8),\n                                  child: SongThumbnail(\n                                    song: song!.extras!,\n                                    onImageReady: onImageReady,\n                                  ),\n                                ),\n                              ),\n                      ),\n                    );\n                  },\n                ),\n              ),\n      ),\n    );\n  }\n}\n\nclass NameAndControls extends StatelessWidget {\n  const NameAndControls({\n    this.song,\n    required this.height,\n    required this.width,\n    this.isRow = false,\n    super.key,\n  });\n  final double width;\n  final double height;\n  final MediaItem? song;\n  final bool isRow;\n\n  @override\n  Widget build(BuildContext context) {\n    MediaPlayer mediaPlayer = context.watch<MediaPlayer>();\n    return SizedBox(\n      height: height,\n      width: width,\n      child: Padding(\n        padding: const EdgeInsets.all(16),\n        child: Column(\n          mainAxisSize: MainAxisSize.max,\n          mainAxisAlignment: MainAxisAlignment.spaceAround,\n          children: [\n            Column(\n              mainAxisAlignment: MainAxisAlignment.spaceAround,\n              mainAxisSize: MainAxisSize.max,\n              crossAxisAlignment: CrossAxisAlignment.center,\n              children: [\n                TextScroll(\n                  song?.title ?? 'Title',\n                  key: Key(song?.title ?? 'Title'),\n                  style: bigTextStyle(context, bold: true),\n                  mode: TextScrollMode.endless,\n                ),\n                Text(\n                  song?.artist ??\n                      song?.album ??\n                      song?.extras?['subtitle'] ??\n                      '',\n                  style: smallTextStyle(context),\n                ),\n              ],\n            ),\n            Column(\n              mainAxisSize: MainAxisSize.max,\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: [\n                ValueListenableBuilder(\n                  valueListenable: mediaPlayer.progressBarState,\n                  builder: (context, ProgressBarState value, child) {\n                    return ProgressBar(\n                      progress: value.current,\n                      total: value.total,\n                      buffered: value.buffered,\n                      barHeight: 3,\n                      thumbRadius: 5,\n                      onSeek: (value) => mediaPlayer.player.seek(value),\n                    );\n                  },\n                ),\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                  children: [\n                    ListenableBuilder(\n                      listenable: GetIt.I<FavouritesManager>().listenable,\n                      builder: (context, child) {\n                        return AdaptiveIconButton(\n                          icon: Icon(\n                            GetIt.I<FavouritesManager>().isFavourite(\n                                  song?.extras,\n                                )\n                                ? AdaptiveIcons.heart_fill\n                                : AdaptiveIcons.heart,\n                            size: 30,\n                          ),\n                          onPressed: () async {\n                            GetIt.I<FavouritesManager>().addOrRemove(\n                              song?.extras,\n                            );\n                          },\n                        );\n                      },\n                    ),\n                    AdaptiveIconButton(\n                      onPressed: () {\n                        mediaPlayer.player.seekToPrevious();\n                      },\n                      icon: Icon(AdaptiveIcons.skip_previous, size: 30),\n                    ),\n                    const PlayPauseButton(size: 40),\n                    AdaptiveIconButton(\n                      onPressed: () {\n                        mediaPlayer.player.seekToNext();\n                      },\n                      icon: Icon(AdaptiveIcons.skip_next, size: 30),\n                    ),\n                    ValueListenableBuilder(\n                      valueListenable: mediaPlayer.loopMode,\n                      builder: (context, value, child) {\n                        return AdaptiveIconButton(\n                          onPressed: () {\n                            mediaPlayer.changeLoopMode();\n                          },\n                          isSelected: value != LoopMode.off,\n                          icon: Icon(\n                            value == LoopMode.off || value == LoopMode.all\n                                ? AdaptiveIcons.repeat_all\n                                : AdaptiveIcons.repeat_one,\n                            size: 30,\n                            color: value == LoopMode.off\n                                ? Colors.white.withValues(alpha: 0.3)\n                                : null,\n                          ),\n                        );\n                      },\n                    ),\n                  ],\n                ),\n              ],\n            ),\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                if (song != null)\n                  RepaintBoundary(\n                    child: ListenableBuilder(\n                      listenable: GetIt.I<DownloadManager>().songListenable(\n                        song!.id,\n                      ),\n                      builder: (context, child) {\n                        final Map? item = GetIt.I<DownloadManager>()\n                            .getDownload(song!.id);\n                        if (item != null) {\n                          if (item['status'] == 'DOWNLOADING') {\n                            final notifier =\n                                GetIt.I<DownloadManager>().getProgressNotifier(\n                                  song!.id,\n                                ) ??\n                                ValueNotifier(0.0);\n                            return ValueListenableBuilder(\n                              valueListenable: notifier,\n                              builder: (context, double progress, child) {\n                                return CircularProgressIndicator(\n                                  value:\n                                      item['status'] == 'DOWNLOADING' &&\n                                          progress > 0.0\n                                      ? progress\n                                      : null,\n                                  color: Colors.white,\n                                  backgroundColor: Colors.black,\n                                );\n                              },\n                            );\n                          } else if (item['status'] == 'DOWNLOADED') {\n                            return const Icon(Icons.download_done_outlined);\n                          }\n                        }\n                        return AdaptiveIconButton(\n                          onPressed: () {\n                            GetIt.I<DownloadManager>().downloadSong(\n                              song!.extras!,\n                            );\n                          },\n                          icon: Icon(AdaptiveIcons.download, size: 30),\n                        );\n                      },\n                    ),\n                  ),\n                AdaptiveIconButton(\n                  onPressed: () {\n                    Modals.showPlayerOptionsModal(\n                      context,\n                      mediaPlayer.currentSongNotifier.value!.extras!,\n                    );\n                  },\n                  icon: Icon(AdaptiveIcons.more_vertical, size: 30),\n                ),\n              ],\n            ),\n            if (song != null && !isRow)\n              SizedBox(height: 55 + MediaQuery.of(context).viewPadding.bottom),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/player/widgets/lyrics_box.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_lyric/lyrics_reader.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/lyrics.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:just_audio_background/just_audio_background.dart';\nimport 'package:gyawun/services/settings_manager.dart';\nimport 'package:loading_indicator_m3e/loading_indicator_m3e.dart';\nimport 'package:provider/provider.dart';\nimport 'package:wakelock_plus/wakelock_plus.dart';\n\nclass LyricsBox extends StatefulWidget {\n  const LyricsBox({required this.currentSong, required this.size, super.key});\n  final MediaItem currentSong;\n  final Size size;\n\n  @override\n  State<LyricsBox> createState() => _LyricsBoxState();\n}\n\nclass _LyricsBoxState extends State<LyricsBox> {\n  Future<Map>? _fetchLyricsFuture;\n  bool _lyricsLoaded = false;\n\n  @override\n  void initState() {\n    super.initState();\n    _initFetchLyrics();\n    _initWakelock();\n  }\n\n  void _initFetchLyrics() {\n    GetIt.I<MediaPlayer>().progressBarState.addListener(_progressListener);\n\n    if (GetIt.I<MediaPlayer>().progressBarState.value.total.inSeconds > 0) {\n      _fetchLyrics();\n    }\n  }\n\n  void _progressListener() {\n    if (GetIt.I<MediaPlayer>().progressBarState.value.total.inSeconds > 0) {\n      _fetchLyrics();\n      GetIt.I<MediaPlayer>().progressBarState.removeListener(_progressListener);\n    }\n  }\n\n  void _initWakelock() {\n    GetIt.I<MediaPlayer>().buttonState.addListener(_updateWakelock);\n  }\n\n  void _updateWakelock() {\n    if (!mounted) return;\n    final isPlaying =\n        GetIt.I<MediaPlayer>().buttonState.value == ButtonState.playing;\n    if (isPlaying && _lyricsLoaded) {\n      WakelockPlus.enable();\n    } else {\n      WakelockPlus.disable();\n    }\n  }\n\n  @override\n  void didUpdateWidget(covariant LyricsBox oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (widget.currentSong != oldWidget.currentSong) {\n      _initFetchLyrics();\n    }\n  }\n\n  void _fetchLyrics() {\n    if (context.mounted) {\n      setState(() {\n        _fetchLyricsFuture = GetIt.I<Lyrics>().getLyrics(\n          videoId: widget.currentSong.id,\n          title: widget.currentSong.title,\n          artist: widget.currentSong.artist,\n          album: widget.currentSong.album,\n          durationInSeconds:\n              GetIt.I<MediaPlayer>().progressBarState.value.total.inSeconds,\n          translation: context.read<SettingsManager>().translateLyrics\n              ? context.read<SettingsManager>().language['value']\n              : null,\n        );\n        _lyricsLoaded = false;\n        _fetchLyricsFuture!\n            .then((lyrics) {\n              _lyricsLoaded =\n                  lyrics['syncedLyrics'] != null ||\n                  lyrics['plainLyrics'] != null;\n              _updateWakelock();\n            })\n            .catchError((_) {\n              _lyricsLoaded = false;\n              _updateWakelock();\n            });\n      });\n    }\n  }\n\n  @override\n  void dispose() {\n    GetIt.I<MediaPlayer>().progressBarState.removeListener(_progressListener);\n    GetIt.I<MediaPlayer>().buttonState.removeListener(_updateWakelock);\n    WakelockPlus.disable();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.all(16),\n      child: Center(\n        child: ValueListenableBuilder(\n          valueListenable: GetIt.I<MediaPlayer>().progressBarState,\n          builder: (context, progress, child) {\n            return progress.total.inSeconds > 0 && _fetchLyricsFuture != null\n                ? FutureBuilder(\n                    future: _fetchLyricsFuture,\n                    builder: (context, snapshot) {\n                      if (snapshot.hasData) {\n                        if (snapshot.data == null) {\n                          return const Text('No Lyrics');\n                        }\n                        if (snapshot.data!['success'] == false) {\n                          return const Text('No Lyrics');\n                        }\n                        Map lyrics = snapshot.data!;\n                        return ValueListenableBuilder(\n                          valueListenable:\n                              GetIt.I<MediaPlayer>().progressBarState,\n                          builder: (context, progress, child) {\n                            try {\n                              return LyricsReader(\n                                padding: EdgeInsets.zero,\n                                position: progress.current.inMilliseconds,\n                                playing: context\n                                    .watch<MediaPlayer>()\n                                    .player\n                                    .playing,\n                                lyricUi: UINetease(\n                                  highlight: false,\n                                  defaultSize: 19,\n                                ),\n                                model: LyricsModelBuilder.create()\n                                    .bindLyricToMain(lyrics['syncedLyrics'])\n                                    .bindLyricToExt(lyrics['transLyrics'])\n                                    .getModel(),\n                                emptyBuilder: () => SingleChildScrollView(\n                                  child: Center(\n                                    child: Text(\n                                      lyrics['plainLyrics'] ?? \"No Lyrics\",\n                                      style: UINetease(\n                                        highlight: false,\n                                        defaultSize: 19,\n                                      ).getOtherMainTextStyle(),\n                                      textAlign: TextAlign.center,\n                                    ),\n                                  ),\n                                ),\n                                size: widget.size,\n                              );\n                            } catch (e) {\n                              debugPrint(\"Error parsing lyrics: $e\");\n                              return SingleChildScrollView(\n                                child: Center(\n                                  child: Text(\n                                    lyrics['plainLyrics'] ?? \"No Lyrics\",\n                                    style: UINetease(\n                                      highlight: false,\n                                      defaultSize: 19,\n                                    ).getOtherMainTextStyle(),\n                                    textAlign: TextAlign.center,\n                                  ),\n                                ),\n                              );\n                            }\n                          },\n                        );\n                      }\n                      if (snapshot.hasError) {\n                        return const Text('No Lyrics');\n                      }\n                      return const ExpressiveLoadingIndicator();\n                    },\n                  )\n                : const ExpressiveLoadingIndicator();\n          },\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/player/widgets/play_pause_button.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/utils/extensions.dart';\nimport 'package:loading_indicator_m3e/loading_indicator_m3e.dart';\n\nclass PlayPauseButton extends StatefulWidget {\n  const PlayPauseButton({\n    super.key,\n    this.size = 30,\n  });\n\n  final double size;\n\n  @override\n  State<PlayPauseButton> createState() => _PlayPauseButtonState();\n}\n\nclass _PlayPauseButtonState extends State<PlayPauseButton>\n    with TickerProviderStateMixin {\n  late AnimationController _animationController;\n  bool playing = false;\n\n  @override\n  void initState() {\n    super.initState();\n    _animationController = AnimationController(\n      duration: const Duration(milliseconds: 200),\n      vsync: this,\n    );\n  }\n\n  @override\n  void dispose() {\n    _animationController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return GestureDetector(\n      onTap: () {\n        GetIt.I<MediaPlayer>().player.playing\n            ? GetIt.I<MediaPlayer>().player.pause()\n            : GetIt.I<MediaPlayer>().player.play();\n      },\n      child: ValueListenableBuilder(\n        valueListenable: GetIt.I<MediaPlayer>().buttonState,\n        builder: (context, buttonState, child) {\n          if (GetIt.I<MediaPlayer>().player.playing != playing) {\n            playing = GetIt.I<MediaPlayer>().player.playing;\n            playing\n                ? _animationController.forward()\n                : _animationController.reverse();\n          }\n          return AnimatedContainer(\n            duration: const Duration(milliseconds: 200),\n            height: 60,\n            width: 60,\n            alignment: Alignment.center,\n            decoration: BoxDecoration(\n              color: (context.isDarkMode ? Colors.white : Colors.black)\n                  .withAlpha(50),\n              borderRadius: BorderRadius.circular(\n                  buttonState == ButtonState.playing ? 15 : 40),\n            ),\n            child: (buttonState == ButtonState.loading)\n                ? const ExpressiveLoadingIndicator()\n                : AnimatedIcon(\n                    icon: AnimatedIcons.play_pause,\n                    progress: _animationController,\n                    size: 40,\n                  ),\n          );\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/player/widgets/queue_list.dart",
    "content": "import 'dart:io';\nimport 'dart:ui';\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/generated/l10n.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/utils/song_thumbnail.dart';\nimport 'package:just_audio/just_audio.dart';\nimport 'package:just_audio_background/just_audio_background.dart';\n\nclass QueueList extends StatelessWidget {\n  const QueueList({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final mediaPlayer = GetIt.I<MediaPlayer>();\n    final player = mediaPlayer.player;\n\n    return StreamBuilder(\n      stream: mediaPlayer.currentTrackStream,\n      builder: (context, snapshot) {\n        final sequence = snapshot.data?.sequence ?? [];\n        final currentIndex = snapshot.data?.currentIndex ?? 0;\n\n        return Container(\n          decoration: BoxDecoration(\n            color: Theme.of(context).scaffoldBackgroundColor.withAlpha(70),\n          ),\n          child: ClipRRect(\n            child: BackdropFilter(\n              filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),\n              child: SafeArea(\n                top: false,\n                child: Column(\n                  children: [\n                    Expanded(\n                      child: ReorderableListView(\n                        onReorder: (oldIndex, newIndex) async {\n                          if (newIndex > oldIndex) newIndex -= 1;\n                          await player.moveAudioSource(oldIndex, newIndex);\n                        },\n                        children: [\n                          for (int i = 0; i < sequence.length; i++)\n                            QueueTile(\n                              key:\n                                  Key('${sequence[i].tag?.id ?? \"unknown\"}_$i'),\n                              index: i,\n                              isCurrent: i == currentIndex,\n                              source: sequence[i],\n                            ),\n                        ],\n                      ),\n                    ),\n                    if (Platform.isAndroid)\n                      StreamBuilder<bool>(\n                        stream: player.shuffleModeEnabledStream,\n                        builder: (context, snapshot) {\n                          final shuffle = snapshot.data ?? false;\n                          return Padding(\n                            padding:\n                                const EdgeInsets.symmetric(horizontal: 16.0),\n                            child: Row(\n                              mainAxisAlignment: MainAxisAlignment.end,\n                              children: [\n                                TextButton.icon(\n                                  style: ButtonStyle(\n                                    backgroundColor: WidgetStatePropertyAll(\n                                      shuffle\n                                          ? Colors.white\n                                          : Colors.white.withAlpha(50),\n                                    ),\n                                    foregroundColor: WidgetStatePropertyAll(\n                                      shuffle\n                                          ? Theme.of(context)\n                                              .scaffoldBackgroundColor\n                                          : Colors.white,\n                                    ),\n                                  ),\n                                  onPressed: () {\n                                    player.setShuffleModeEnabled(!shuffle);\n                                  },\n                                  icon: const Icon(Icons.shuffle_outlined),\n                                  label: Text(S.of(context).Shuffle),\n                                ),\n                              ],\n                            ),\n                          );\n                        },\n                      ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        );\n      },\n    );\n  }\n}\n\nclass QueueTile extends StatelessWidget {\n  final int index;\n  final bool isCurrent;\n  final IndexedAudioSource source;\n\n  const QueueTile({\n    super.key,\n    required this.index,\n    required this.isCurrent,\n    required this.source,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final player = GetIt.I<MediaPlayer>().player;\n    final MediaItem? song = source.tag as MediaItem?;\n\n    if (song == null) return const SizedBox();\n\n    return Dismissible(\n      key: Key(song.id),\n      direction: DismissDirection.endToStart,\n      confirmDismiss: (_) async {\n        await player.removeAudioSourceAt(index);\n        return true;\n      },\n      child: ListTile(\n        key: Key(index.toString()),\n        title: Text(song.title, maxLines: 1),\n        leading: ArtworkWidget(song: song, isCurrent: isCurrent),\n        subtitle: Text(\n          song.artist ?? song.album ?? song.extras?['subtitle'] ?? '',\n          maxLines: 1,\n        ),\n        trailing: const Icon(Icons.drag_handle),\n        onTap: () {\n          player.seek(Duration.zero, index: index);\n        },\n      ),\n    );\n  }\n}\n\nclass ArtworkWidget extends StatelessWidget {\n  final MediaItem song;\n  final bool isCurrent;\n\n  const ArtworkWidget({\n    super.key,\n    required this.song,\n    required this.isCurrent,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final double dp = MediaQuery.of(context).devicePixelRatio;\n\n    return ClipRRect(\n      borderRadius: BorderRadius.circular(8),\n      child: Stack(\n        children: [\n          SongThumbnail(\n            song: song.extras!,\n            dp: dp,\n            height: 50,\n            width: 50,\n            fit: BoxFit.fill,\n            errorWidget: (_, _, _) => const Icon(Icons.music_note, size: 32),\n          ),\n          if (isCurrent)\n            Container(\n              height: 50,\n              width: 50,\n              color: Colors.black.withValues(alpha:0.6),\n            ),\n          if (isCurrent)\n            const Positioned(\n              width: 34,\n              height: 34,\n              left: 8,\n              top: 8,\n              child: Center(\n                child: Icon(\n                  Icons.music_note_outlined,\n                  color: Colors.white,\n                ),\n              ),\n            ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/search/cubit/search_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/widgets.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:yt_music/ytmusic.dart';\n\nimport '../../../services/history_manager.dart';\n\npart 'search_state.dart';\n\nclass SearchCubit extends Cubit<SearchState> {\n  final YTMusic _ytmusic;\n  Map<String, dynamic>? endpoint;\n  late final SearchHistory _searchHistory;\n\n  SearchCubit(this._ytmusic, {this.endpoint}) : super(SearchInitial()) {\n    _searchHistory = GetIt.I<HistoryManager>().searches;\n    if (endpoint != null) {\n      search('');\n    }\n  }\n\n  Future<void> search(String query) async {\n    emit(const SearchLoading());\n    try {\n      await _searchHistory.add(query);\n      final feed = await _ytmusic.search(query, endpoint: endpoint);\n      emit(\n        SearchSuccess(\n          sections: feed['sections'],\n          continuation: feed['continuation'],\n          loadingMore: false,\n        ),\n      );\n    } catch (e, st) {\n      debugPrint(e.toString());\n      debugPrint(st.toString());\n      emit(SearchError(e.toString(), st.toString()));\n    }\n  }\n\n  Future<void> fetchNext() async {\n    final current = state;\n    if (current is! SearchSuccess) return;\n    if (current.loadingMore || current.continuation == null) return;\n    emit(current.copyWith(loadingMore: true));\n    try {\n      final feed = await _ytmusic.search(\n        '',\n        endpoint: endpoint,\n        additionalParams: current.continuation!,\n      );\n      SearchSuccess(\n        sections: [...current.sections, ...feed['sections']],\n        continuation: feed['continuation'],\n        loadingMore: false,\n      );\n    } catch (e, st) {\n      emit(SearchError(e.toString(), st.toString()));\n    }\n  }\n\n  Future<List<Map<String, dynamic>>> getSuggestions(String query) async {\n    try {\n      List<Map<String, dynamic>> suggestions = await _ytmusic\n          .getSearchSuggestions(query);\n      return suggestions.isNotEmpty ? suggestions : _searchHistory.getList();\n    } catch (e) {\n      return [];\n    }\n  }\n}\n"
  },
  {
    "path": "lib/screens/search/cubit/search_state.dart",
    "content": "part of 'search_cubit.dart';\n\n@immutable\nsealed class SearchState {\n  const SearchState();\n}\n\nfinal class SearchInitial extends SearchState{\n  const SearchInitial();\n}\n\nfinal class SearchLoading extends SearchState {\n  const SearchLoading();\n}\n\nfinal class SearchError extends SearchState {\n  final String? message;\n  final String? stackTrace;\n  const SearchError([this.message, this.stackTrace]);\n}\n\nfinal class SearchSuccess extends SearchState {\n  final List sections;\n  final bool loadingMore;\n  final String? continuation;\n  const SearchSuccess({\n    required this.sections,\n    required this.continuation,\n    required this.loadingMore,\n  });\n\n  SearchSuccess copyWith({\n    List? sections,\n    String? continuation,\n    bool? loadingMore,\n  }) {\n    return SearchSuccess(\n      sections: sections ?? this.sections,\n      continuation: continuation ?? this.continuation,\n      loadingMore: loadingMore ?? this.loadingMore,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/search/search_page.dart",
    "content": "import 'dart:io';\n\nimport 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_typeahead/flutter_typeahead.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/core/widgets/internet_guard.dart';\nimport 'package:gyawun/core/utils/service_locator.dart';\nimport 'package:gyawun/screens/search/cubit/search_cubit.dart';\nimport 'package:loading_indicator_m3e/loading_indicator_m3e.dart';\n\nimport '../../../generated/l10n.dart';\nimport '../../../services/media_player.dart';\nimport '../../../utils/adaptive_widgets/adaptive_widgets.dart';\nimport '../../../utils/bottom_modals.dart';\nimport '../../core/widgets/tiles/section_list_tile.dart';\n\nclass SearchPage extends StatelessWidget {\n  const SearchPage({super.key, this.endpoint, this.isMore = false});\n  final Map<String, dynamic>? endpoint;\n  final bool isMore;\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => SearchCubit(sl(), endpoint: endpoint),\n      child: _SearchPage(title: endpoint?['query'], isMore: isMore),\n    );\n  }\n}\n\nclass _SearchPage extends StatefulWidget {\n  const _SearchPage({this.title, this.isMore = false});\n  final String? title;\n  final bool isMore;\n\n  @override\n  State<_SearchPage> createState() => _SearchPageState();\n}\n\nclass _SearchPageState extends State<_SearchPage> with WidgetsBindingObserver {\n  late ScrollController _scrollController;\n  final TextEditingController _textEditingController = TextEditingController();\n  final SuggestionsController _suggestionsController = SuggestionsController();\n  final FocusNode _focusNode = FocusNode();\n\n  @override\n  void initState() {\n    super.initState();\n    _scrollController = ScrollController();\n    _scrollController.addListener(_scrollListener);\n    WidgetsBinding.instance.addObserver(this);\n    _focusNode.requestFocus();\n  }\n\n  @override\n  void dispose() {\n    WidgetsBinding.instance.removeObserver(this);\n    _scrollController.dispose();\n    _textEditingController.dispose();\n    _suggestionsController.dispose();\n    _focusNode.dispose();\n    super.dispose();\n  }\n\n  @override\n  void didChangeMetrics() {\n    super.didChangeMetrics();\n    if (_focusNode.hasFocus) {\n      Future.delayed(const Duration(milliseconds: 300), () {\n        if (mounted) {\n          _suggestionsController.resize();\n        }\n      });\n    }\n  }\n\n  Future<void> _scrollListener() async {\n    if (_scrollController.position.pixels ==\n        _scrollController.position.maxScrollExtent) {\n      await context.read<SearchCubit>().fetchNext();\n    }\n  }\n\n  Future<void> onSubmit(String query) async {\n    if (query.trim() == '') return;\n    _focusNode.unfocus();\n    await context.read<SearchCubit>().search(query);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return InternetGuard(\n      onConnectivityRestored: () {\n        if (_textEditingController.text.isNotEmpty) {\n          context.read<SearchCubit>().search(_textEditingController.text);\n        }\n      },\n      child: Scaffold(\n        appBar: PreferredSize(\n          preferredSize: const AdaptiveAppBar().preferredSize,\n          child: LayoutBuilder(\n            builder: (context, constraints) {\n              return AdaptiveAppBar(\n                title: widget.title != null\n                    ? Text(widget.title!)\n                    : Material(\n                        color: Colors.transparent,\n                        child: Row(\n                          children: [\n                            Expanded(\n                              child: TypeAheadField(\n                                focusNode: _focusNode,\n                                controller: _textEditingController,\n                                suggestionsController: _suggestionsController,\n                                suggestionsCallback: (query) => context\n                                    .read<SearchCubit>()\n                                    .getSuggestions(query),\n                                loadingBuilder: (context) => Container(\n                                  height: 60,\n                                  alignment: Alignment.center,\n                                  child: ExpressiveLoadingIndicator(),\n                                ),\n                                builder: (context, controller, focusNode) {\n                                  return AdaptiveTextField(\n                                    focusNode: focusNode,\n                                    controller: controller,\n                                    onSubmitted: onSubmit,\n                                    keyboardType: TextInputType.text,\n                                    maxLines: 1,\n                                    autofocus: true,\n                                    textInputAction: TextInputAction.search,\n                                    fillColor: Theme.of(\n                                      context,\n                                    ).colorScheme.surfaceContainer,\n                                    contentPadding: const EdgeInsets.symmetric(\n                                      vertical: 2,\n                                      horizontal: 8,\n                                    ),\n                                    borderRadius: BorderRadius.circular(\n                                      Platform.isWindows ? 4.0 : 35,\n                                    ),\n                                    hintText: S.of(context).Search_Gyawun,\n                                    prefix: constraints.maxWidth > 400\n                                        ? null\n                                        : const AdaptiveBackButton(),\n                                    suffix: GestureDetector(\n                                      onTap: () {\n                                        setState(() {\n                                          _textEditingController.text = '';\n                                        });\n                                      },\n                                      child: const Icon(\n                                        FluentIcons.dismiss_24_filled,\n                                      ),\n                                    ),\n                                  );\n                                },\n                                decorationBuilder: (context, child) {\n                                  if (Platform.isWindows) {\n                                    return Ink(\n                                      padding: EdgeInsets.zero,\n                                      decoration: BoxDecoration(\n                                        color: AdaptiveTheme.of(\n                                          context,\n                                        ).inactiveBackgroundColor,\n                                        borderRadius: BorderRadius.circular(4),\n                                      ),\n                                      child: child,\n                                    );\n                                  } else {\n                                    return Material(\n                                      elevation: 5,\n                                      color: Theme.of(\n                                        context,\n                                      ).colorScheme.surfaceContainerLowest,\n                                      borderRadius: BorderRadius.circular(10),\n                                      child: child,\n                                    );\n                                  }\n                                },\n                                itemBuilder: (context, value) {\n                                  if (value['type'] == 'TEXT') {\n                                    return AdaptiveListTile(\n                                      leading: value['isHistory'] != null\n                                          ? const Icon(Icons.history)\n                                          : null,\n                                      title: Text(value['query']),\n                                      onTap: () {\n                                        setState(() {\n                                          _textEditingController.text =\n                                              value['query'];\n                                        });\n                                        onSubmit(value['query']);\n                                      },\n                                    );\n                                  }\n                                  return _SearchListTile(item: value);\n                                },\n                                onSelected: (value) => (),\n                                hideOnEmpty: true,\n                              ),\n                            ),\n                          ],\n                        ),\n                      ),\n                automaticallyImplyLeading: (constraints.maxWidth <= 400)\n                    ? false\n                    : true,\n              );\n            },\n          ),\n        ),\n        body: BlocBuilder<SearchCubit, SearchState>(\n          builder: (context, state) {\n            switch (state) {\n              case SearchInitial():\n                return SizedBox.shrink();\n              case SearchLoading():\n                return Center(child: LoadingIndicatorM3E());\n              case SearchError():\n                return Center(child: Text(state.message ?? ''));\n              case SearchSuccess():\n                return SingleChildScrollView(\n                  controller: _scrollController,\n                  child: Center(\n                    child: Container(\n                      constraints: const BoxConstraints(maxWidth: 1000),\n                      padding: const EdgeInsets.all(8),\n                      child: Column(\n                        children: [\n                          ...state.sections.asMap().entries.map((entry) {\n                            int index = entry.key;\n                            var section = entry.value;\n                            if (Platform.isWindows) {\n                              return Center(\n                                child: Adaptivecard(\n                                  borderRadius: BorderRadius.circular(8),\n                                  child: _SearchSectionItem(\n                                    section: section,\n                                    isFirst: index == 0,\n                                    isMore: widget.isMore,\n                                  ),\n                                ),\n                              );\n                            }\n                            return _SearchSectionItem(\n                              section: section,\n                              isFirst: index == 0,\n                              isMore: widget.isMore,\n                            );\n                          }),\n                          if (state.loadingMore)\n                            const Center(child: ExpressiveLoadingIndicator()),\n                        ],\n                      ),\n                    ),\n                  ),\n                );\n            }\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass _SearchSectionItem extends StatelessWidget {\n  const _SearchSectionItem({\n    required this.section,\n    this.isFirst = false,\n    this.isMore = false,\n  });\n  final Map<String, dynamic> section;\n  final bool isFirst;\n  final bool isMore;\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.only(bottom: 8.0),\n      child: Column(\n        children: [\n          if (!isMore)\n            AdaptiveListTile(\n              contentPadding: const EdgeInsets.symmetric(\n                vertical: 4,\n                horizontal: 4,\n              ),\n              title: Text(\n                section['title'] ?? isFirst\n                    ? S().Top_Results\n                    : S().Other_Results,\n                style: Theme.of(context).textTheme.headlineSmall?.copyWith(\n                  color: Theme.of(context).colorScheme.primary,\n                  fontWeight: FontWeight.w700,\n                  fontSize: 18,\n                ),\n              ),\n              trailing: section['trailing']?['text'] != null\n                  ? Padding(\n                      padding: EdgeInsets.symmetric(\n                        vertical: Platform.isAndroid ? 12 : 0,\n                      ),\n                      child: AdaptiveOutlinedButton(\n                        onPressed: () {\n                          context.push(\n                            '/search',\n                            extra: {\n                              'endpoint': section['trailing']['endpoint'],\n                              'isMore': true,\n                            },\n                          );\n                        },\n                        child: Text(\n                          section['trailing']['text'],\n                          style: const TextStyle(fontSize: 12),\n                        ),\n                      ),\n                    )\n                  : null,\n            ),\n          ...section['contents'].asMap().entries.map((entry) {\n            int index = entry.key;\n            var item = entry.value;\n            return Padding(\n              padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 4),\n              child: SectionListTile(\n                item: item,\n                isFirst: index == 0,\n                isLast: index == section['contents'].length - 1,\n              ),\n            );\n          }).toList(),\n        ],\n      ),\n    );\n  }\n}\n\nclass _SearchListTile extends StatelessWidget {\n  const _SearchListTile({required this.item});\n  final Map item;\n  @override\n  Widget build(BuildContext context) {\n    return AdaptiveListTile(\n      onSecondaryTap: () {\n        if (item['videoId'] != null) {\n          Modals.showSongBottomModal(context, item);\n        } else if (item['endpoint'] != null) {\n          Modals.showPlaylistBottomModal(context, item);\n        }\n      },\n      onTap: () async {\n        if (item['videoId'] != null) {\n          await GetIt.I<MediaPlayer>().playSong(Map.from(item));\n        } else if (item['endpoint'] != null && item['videoId'] == null) {\n          context.push('/browse', extra: {'endpoint': item['endpoint']});\n        }\n      },\n      onLongPress: () {\n        if (item['videoId'] != null) {\n          Modals.showSongBottomModal(context, item);\n        } else if (item['endpoint'] != null) {\n          Modals.showPlaylistBottomModal(context, item);\n        }\n      },\n      dense: false,\n      title: Text(item['title'], maxLines: 1, overflow: TextOverflow.ellipsis),\n      subtitle: item['subtitle'] != null\n          ? Text(\n              item['subtitle'],\n              maxLines: 1,\n              style: TextStyle(color: Colors.grey.withValues(alpha: 0.9)),\n              overflow: TextOverflow.ellipsis,\n            )\n          : null,\n      leading: item['thumbnails'] != null && item['thumbnails'].isNotEmpty\n          ? ClipRRect(\n              borderRadius: BorderRadius.circular(\n                ['ARTIST', 'PROFILE'].contains(item['type']) ? 30 : 3,\n              ),\n              child: Image.network(item['thumbnails'].first['url'], width: 50),\n            )\n          : null,\n      trailing: item['videoId'] == null && item['endpoint'] != null\n          ? const Icon(CupertinoIcons.chevron_right)\n          : null,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/about/about_page.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/expressive_list_group.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\nimport 'package:package_info_plus/package_info_plus.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nimport '../../../generated/l10n.dart';\nimport '../widgets/color_icon.dart';\n\nclass AboutPage extends StatefulWidget {\n  const AboutPage({super.key});\n\n  @override\n  State<AboutPage> createState() => _AboutPageState();\n}\n\nclass _AboutPageState extends State<AboutPage> {\n  String? _version;\n\n  @override\n  void initState() {\n    super.initState();\n    _loadVersion();\n  }\n\n  Future<void> _loadVersion() async {\n    final info = await PackageInfo.fromPlatform();\n    if (!mounted) return;\n\n    setState(() {\n      // Example: 2.0.16-beta.3 or 2.0.16\n      _version = info.version;\n    });\n  }\n\n  void _open(String url) {\n    launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final colorScheme = Theme.of(context).colorScheme;\n    final textTheme = Theme.of(context).textTheme;\n    return Scaffold(\n      body: NestedScrollView(\n        headerSliverBuilder: (context, innerBoxIsScrolled) {\n          return [\n            ExpressiveAppBar(title: S.of(context).About, hasLeading: true),\n          ];\n        },\n        body: Center(\n          child: Container(\n            constraints: const BoxConstraints(maxWidth: 1000),\n            child: ListView(\n              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),\n              children: [\n                Center(\n                  child: Column(\n                    children: [\n                      Container(\n                        height: 100,\n                        width: 100,\n                        decoration: BoxDecoration(\n                          color: colorScheme.surfaceContainerHighest,\n                          shape: BoxShape.circle,\n                          boxShadow: [\n                            BoxShadow(\n                              color: colorScheme.shadow.withValues(alpha: 0.08),\n                              blurRadius: 12,\n                              offset: const Offset(0, 4),\n                            ),\n                          ],\n                        ),\n                        clipBehavior: Clip.antiAlias,\n                        child: Image.asset(\n                          'assets/images/icon.png',\n                          height: 100,\n                          width: 100,\n                          fit: BoxFit.cover,\n                          errorBuilder: (_, _, _) => Icon(\n                            Icons.music_note_rounded,\n                            size: 48,\n                            color: colorScheme.primary,\n                          ),\n                        ),\n                      ),\n                      const SizedBox(height: 16),\n                      Text(\n                        'Gyawun Music',\n                        style: textTheme.headlineMedium?.copyWith(\n                          fontWeight: FontWeight.w700,\n                          color: colorScheme.onSurface,\n                          letterSpacing: -0.5,\n                        ),\n                      ),\n                      const SizedBox(height: 8),\n                      if (_version != null)\n                        Container(\n                          padding: const EdgeInsets.symmetric(\n                            horizontal: 12,\n                            vertical: 6,\n                          ),\n                          decoration: BoxDecoration(\n                            borderRadius: BorderRadius.circular(20),\n                            color: colorScheme.tertiaryContainer,\n                          ),\n                          child: Text(\n                            'Version $_version',\n                            style: textTheme.labelLarge?.copyWith(\n                              color: colorScheme.onTertiaryContainer,\n                              fontWeight: FontWeight.w600,\n                              letterSpacing: 0.5,\n                            ),\n                          ),\n                        ),\n                    ],\n                  ),\n                ),\n\n                const SizedBox(height: 16),\n\n                ExpressiveListGroup(\n                  title: \"About\",\n                  children: [\n                    ExpressiveListTile(\n                      leading: const SettingsColorIcon(icon: Icons.person),\n                      title: const Text(\"Developer\"),\n                      subtitle: const Text(\"Sheikh Haziq\"),\n                      trailing: const Icon(FluentIcons.chevron_right_24_filled),\n                      onTap: () => _open('https://github.com/sheikhhaziq'),\n                    ),\n                    ExpressiveListTile(\n                      leading: const SettingsColorIcon(icon: Icons.link),\n                      title: const Text(\"Website\"),\n                      trailing: const Icon(FluentIcons.chevron_right_24_filled),\n                      onTap: () => _open('https://gyawunmusic.vercel.app'),\n                    ),\n                  ],\n                ),\n\n                const SizedBox(height: 24),\n\n                ExpressiveListGroup(\n                  title: \"Community\",\n                  children: [\n                    ExpressiveListTile(\n                      leading: const SettingsColorIcon(icon: Icons.people),\n                      title: const Text(\"Contributors\"),\n                      trailing: const Icon(FluentIcons.chevron_right_24_filled),\n                      onTap: () => _open(\n                        'https://github.com/jhelumcorp/gyawun/contributors',\n                      ),\n                    ),\n                    ExpressiveListTile(\n                      leading: const SettingsColorIcon(icon: Icons.send),\n                      title: const Text(\"Telegram\"),\n                      trailing: const Icon(FluentIcons.chevron_right_24_filled),\n                      onTap: () => _open('https://t.me/jhelumcorp'),\n                    ),\n                  ],\n                ),\n\n                const SizedBox(height: 24),\n\n                ExpressiveListGroup(\n                  title: \"Development\",\n                  children: [\n                    ExpressiveListTile(\n                      leading: const SettingsColorIcon(icon: Icons.code),\n                      title: const Text(\"Source Code\"),\n                      trailing: const Icon(FluentIcons.chevron_right_24_filled),\n                      onTap: () =>\n                          _open('https://github.com/jhelumcorp/gyawun'),\n                    ),\n                    ExpressiveListTile(\n                      leading: const SettingsColorIcon(icon: Icons.bug_report),\n                      title: const Text(\"Bug Report\"),\n                      trailing: const Icon(FluentIcons.chevron_right_24_filled),\n                      onTap: () => _open(\n                        'https://github.com/sheikhhaziq/gyawun_music/issues/new?template=bug_report.yml',\n                      ),\n                    ),\n                    ExpressiveListTile(\n                      leading: const SettingsColorIcon(icon: Icons.description),\n                      title: const Text(\"Feature Request\"),\n                      trailing: const Icon(FluentIcons.chevron_right_24_filled),\n                      onTap: () => _open(\n                        'https://github.com/sheikhhaziq/gyawun_music/discussions',\n                      ),\n                    ),\n                  ],\n                ),\n\n                const SizedBox(height: 16),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/appearance/appearance_page.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/core/extensions/string_extensions.dart';\nimport 'package:gyawun/core/utils/expressive_sheet.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/expressive_list_group.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\nimport 'package:gyawun/core/widgets/expressive_switch_list_tile.dart';\nimport 'package:gyawun/screens/settings/widgets/color_icon.dart';\nimport 'package:gyawun/services/settings_manager.dart';\n\nimport '../../../generated/l10n.dart';\nimport 'cubit/appearance_cubit.dart';\n\nclass AppearancePage extends StatelessWidget {\n  const AppearancePage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => AppearanceCubit(),\n      child: Scaffold(\n        body: NestedScrollView(\n          headerSliverBuilder: (context, innerBoxIsScrolled) {\n            return [\n              ExpressiveAppBar(\n                title: S.of(context).Appearence,\n                hasLeading: true,\n              ),\n            ];\n          },\n          body: Center(\n            child: Container(\n              constraints: const BoxConstraints(maxWidth: 1000),\n              child: BlocBuilder<AppearanceCubit, AppearanceState>(\n                builder: (context, state) {\n                  final s = state as AppearanceLoaded;\n\n                  return ListView(\n                    padding: const EdgeInsets.symmetric(\n                      horizontal: 16,\n                      vertical: 8,\n                    ),\n                    children: [\n                      ExpressiveListGroup(\n                        title: 'Theme',\n                        children: [\n                          ExpressiveListTile(\n                            title: Text(S.of(context).Theme_Mode),\n                            subtitle: Text(s.themeMode.name.capitalize()),\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.dark_theme_24_filled,\n                            ),\n                            onTap: () async {\n                              final selected =\n                                  await ExpressiveSheet.showSelection(\n                                    context,\n                                    title: \"Choose Theme\",\n                                    options: [\n                                      ExpressiveSheetOption(\n                                        label: \"System Default\",\n                                        icon: FluentIcons.system_24_filled,\n                                        value: ThemeMode.system,\n                                      ),\n                                      ExpressiveSheetOption(\n                                        label: \"Light Mode\",\n                                        icon: FluentIcons.lightbulb_24_filled,\n                                        value: ThemeMode.light,\n                                      ),\n                                      ExpressiveSheetOption(\n                                        label: \"Dark Mode\",\n                                        icon: FluentIcons.dark_theme_24_filled,\n                                        value: ThemeMode.dark,\n                                      ),\n                                    ],\n                                  );\n                              if (selected == null) return;\n                              if (context.mounted) {\n                                context.read<AppearanceCubit>().setThemeMode(\n                                  selected,\n                                );\n                              }\n                            },\n                          ),\n                          ExpressiveListTile(\n                            title: Text('Accent Color'),\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.color_24_filled,\n                            ),\n                            trailing: CircleAvatar(\n                              radius: 20,\n                              child: ClipRRect(\n                                borderRadius: BorderRadius.circular(20),\n                                child: Row(\n                                  children: [\n                                    Container(\n                                      color: s.accentColor ?? Colors.black,\n                                      width: 20,\n                                    ),\n                                    Container(\n                                      color: s.accentColor ?? Colors.white,\n                                      width: 20,\n                                    ),\n                                  ],\n                                ),\n                              ),\n                            ),\n                            onTap: () async {\n                              final selected =\n                                  await ExpressiveSheet.showColorSelection(\n                                    context,\n                                    title: 'Select Accent Color',\n                                  );\n                              if (selected != null) {\n                                GetIt.I<SettingsManager>().accentColor =\n                                    selected;\n                              }\n                            },\n                          ),\n                          ExpressiveSwitchListTile(\n                            title: Text('Amoled Black'),\n                            leading: const SettingsColorIcon(\n                              icon: FluentIcons.drop_24_filled,\n                            ),\n                            value: s.amoledBlack,\n                            onChanged: (value) {\n                              context.read<AppearanceCubit>().setAmoledBlack(\n                                value,\n                              );\n                            },\n                          ),\n\n                          /// Dynamic colors\n                          ExpressiveSwitchListTile(\n                            title: Text(S.of(context).Dynamic_Colors),\n                            leading: const SettingsColorIcon(\n                              icon: FluentIcons.color_background_24_filled,\n                            ),\n                            value: s.dynamicColors,\n                            onChanged: (value) {\n                              context.read<AppearanceCubit>().setDynamicColors(\n                                value,\n                              );\n                            },\n                          ),\n                        ],\n                      ),\n                    ],\n                  );\n                },\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/appearance/cubit/appearance_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/settings_manager.dart';\n\npart 'appearance_state.dart';\n\nclass AppearanceCubit extends Cubit<AppearanceState> {\n  final SettingsManager _settings = GetIt.I<SettingsManager>();\n\n  late final VoidCallback _listener;\n\n  AppearanceCubit()\n      : super(\n          AppearanceLoaded(\n            themeMode: GetIt.I<SettingsManager>().themeMode,\n            accentColor: GetIt.I<SettingsManager>().accentColor,\n            amoledBlack: GetIt.I<SettingsManager>().amoledBlack,\n            dynamicColors: GetIt.I<SettingsManager>().dynamicColors,\n          ),\n        ) {\n    _listener = () {\n      if (!isClosed) {\n        _emitState();\n      }\n    };\n\n    _settings.addListener(_listener);\n  }\n\n  void _emitState() {\n    if (isClosed) return;\n\n    emit(\n      AppearanceLoaded(\n        themeMode: _settings.themeMode,\n        accentColor: _settings.accentColor,\n        amoledBlack: _settings.amoledBlack,\n        dynamicColors: _settings.dynamicColors,\n      ),\n    );\n  }\n\n  Future<void> setThemeMode(ThemeMode mode) async {\n    await _settings.setThemeMode(mode);\n    // listener will emit\n  }\n\n  void setAmoledBlack(bool value) {\n    _settings.amoledBlack = value;\n  }\n\n  void setDynamicColors(bool value) {\n    _settings.dynamicColors = value;\n  }\n\n  @override\n  Future<void> close() {\n    _settings.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/appearance/cubit/appearance_state.dart",
    "content": "part of 'appearance_cubit.dart';\n\n@immutable\nsealed class AppearanceState {\n  const AppearanceState();\n}\n\nclass AppearanceLoaded extends AppearanceState {\n  final ThemeMode themeMode;\n  final Color? accentColor;\n  final bool amoledBlack;\n  final bool dynamicColors;\n\n  const AppearanceLoaded({\n    required this.themeMode,\n    required this.accentColor,\n    required this.amoledBlack,\n    required this.dynamicColors,\n  });\n}\n"
  },
  {
    "path": "lib/screens/settings/backup_storage/backup_storage_page.dart",
    "content": "import 'dart:io';\n\nimport 'package:easy_folder_picker/FolderPicker.dart';\nimport 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/expressive_list_group.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\nimport 'package:gyawun/screens/settings/widgets/color_icon.dart';\n\nimport '../../../generated/l10n.dart';\nimport '../../../themes/text_styles.dart';\nimport '../../../utils/bottom_modals.dart';\nimport '../../../services/bottom_message.dart';\n\nimport 'cubit/backup_storage_cubit.dart';\n\nclass BackupStoragePage extends StatelessWidget {\n  const BackupStoragePage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => BackupStorageCubit(),\n      child: BlocListener<BackupStorageCubit, BackupStorageState>(\n        listenWhen: (_, state) => state.lastResult != null,\n        listener: (context, state) {\n          final result = state.lastResult;\n          if (result == null) return;\n\n          if (result is BackupSuccess) {\n            BottomMessage.showText(\n              context,\n              '${S.of(context).Backup_Success} ${result.path}',\n            );\n          } else if (result is BackupFailure) {\n            BottomMessage.showText(context, S.of(context).Backup_Failed);\n          } else if (result is RestoreSuccess) {\n            BottomMessage.showText(context, S.of(context).Restore_Success);\n          } else if (result is RestoreFailure) {\n            BottomMessage.showText(context, S.of(context).Restore_Failed);\n          }\n        },\n        child: const _BackupStoragePage(),\n      ),\n    );\n  }\n}\n\nclass _BackupStoragePage extends StatelessWidget {\n  const _BackupStoragePage();\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: NestedScrollView(\n        headerSliverBuilder: (context, innerBoxIsScrolled) {\n          return [\n            ExpressiveAppBar(\n              title: S.of(context).Backup_And_Restore,\n              hasLeading: true,\n            ),\n          ];\n        },\n        body: Center(\n          child: Container(\n            constraints: const BoxConstraints(maxWidth: 1000),\n            child: BlocBuilder<BackupStorageCubit, BackupStorageState>(\n              builder: (context, state) {\n                final cubit = context.read<BackupStorageCubit>();\n\n                return ListView(\n                  padding: const EdgeInsets.symmetric(\n                    horizontal: 16,\n                    vertical: 8,\n                  ),\n                  children: [\n                    if (Platform.isAndroid) ...[\n                      ExpressiveListGroup(\n                        title: 'Storage',\n                        children: [\n                          ExpressiveListTile(\n                            title: Text(\"App Folder\"),\n                            leading: const SettingsColorIcon(\n                              icon: FluentIcons.folder_24_filled,\n                            ),\n\n                            subtitle: Text(state.appFolder),\n                            trailing: FilledButton.tonal(\n                              child: const Text('Change'),\n                              onPressed: () async {\n                                final appFolder = Directory(state.appFolder);\n                                final rootDirectory = await appFolder.exists()\n                                    ? appFolder\n                                    : Directory(state.defaultPath);\n                                if (!context.mounted) return;\n                                final dir = await FolderPicker.pick(\n                                  context: context,\n                                  allowFolderCreation: true,\n                                  rootDirectory: rootDirectory,\n                                );\n                                if (dir != null) {\n                                  cubit.setAppFolder(dir.path);\n                                }\n                              },\n                            ),\n                          ),\n                        ],\n                      ),\n                    ],\n\n                    SizedBox(height: 24),\n\n                    ExpressiveListGroup(\n                      title: S.of(context).Backup_And_Restore,\n                      children: [\n                        ExpressiveListTile(\n                          title: Text(S.of(context).Backup),\n                          leading: const SettingsColorIcon(\n                            icon: Icons.backup_rounded,\n                          ),\n                          onTap: () async {\n                            final result = await showBackupSelector(context);\n                            if (result == null) return;\n\n                            cubit.backup(action: result.$1, items: result.$2);\n                          },\n                        ),\n\n                        ExpressiveListTile(\n                          title: Text(S.of(context).Restore),\n                          leading: const SettingsColorIcon(\n                            icon: Icons.restore_rounded,\n                          ),\n                          onTap: cubit.restore,\n                        ),\n                      ],\n                    ),\n                  ],\n                );\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\n/* ──────────────────────────────────────────────────────────────── */\n/* BACKUP SELECTION MODAL (UI ONLY)                                  */\n/* ──────────────────────────────────────────────────────────────── */\n\nFuture<(String, List)?> showBackupSelector(BuildContext context) async {\n  return await showModalBottomSheet(\n    useRootNavigator: false,\n    backgroundColor: Colors.transparent,\n    useSafeArea: true,\n    isScrollControlled: true,\n    context: context,\n    builder: (context) {\n      final items = ValueNotifier<List<Map<String, dynamic>>>([\n        {'name': 'Favourites', 'selected': false},\n        {'name': 'Playlists', 'selected': false},\n        {'name': 'Settings', 'selected': false},\n        {'name': 'Song History', 'selected': false},\n        {'name': 'Downloads', 'selected': false},\n      ]);\n\n      return BottomModalLayout(\n        title: Text(\n          S.of(context).Select_Backup,\n          style: mediumTextStyle(context),\n        ),\n        child: SingleChildScrollView(\n          child: Column(\n            mainAxisSize: MainAxisSize.min,\n            children: [\n              const Divider(),\n              ValueListenableBuilder(\n                valueListenable: items,\n                builder: (_, backups, _) {\n                  return Column(\n                    children: backups.indexed.map((el) {\n                      final index = el.$1;\n                      final item = el.$2;\n\n                      return CheckboxListTile(\n                        title: Text(item['name']),\n                        value: item['selected'],\n                        onChanged: (val) {\n                          final newItems = List<Map<String, dynamic>>.from(\n                            items.value,\n                          );\n                          newItems[index]['selected'] = val;\n                          items.value = newItems;\n                        },\n                      );\n                    }).toList(),\n                  );\n                },\n              ),\n              Padding(\n                padding: const EdgeInsets.symmetric(\n                  horizontal: 16,\n                  vertical: 8,\n                ),\n                child: Row(\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  children: [\n                    _backupActionButton(\n                      context,\n                      label: S.of(context).Share,\n                      action: 'Share',\n                      items: items,\n                    ),\n                    const SizedBox(width: 20),\n                    _backupActionButton(\n                      context,\n                      label: S.of(context).Save,\n                      action: 'Save',\n                      items: items,\n                    ),\n                  ],\n                ),\n              ),\n            ],\n          ),\n        ),\n      );\n    },\n  );\n}\n\nWidget _backupActionButton(\n  BuildContext context, {\n  required String label,\n  required String action,\n  required ValueNotifier<List<Map<String, dynamic>>> items,\n}) {\n  return MaterialButton(\n    color: Theme.of(context).colorScheme.primary,\n    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),\n    onPressed: () {\n      final selected = items.value\n          .where((e) => e['selected'] == true)\n          .map((e) => e['name'].toLowerCase())\n          .toList();\n\n      Navigator.pop(context, selected.isEmpty ? null : (action, selected));\n    },\n    child: Text(\n      label,\n      style: TextStyle(color: Theme.of(context).scaffoldBackgroundColor),\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/screens/settings/backup_storage/cubit/backup_storage_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\n\nimport '../../../../../../services/file_storage.dart';\nimport '../../../../../../services/library.dart';\nimport '../../../../../../services/settings_manager.dart';\nimport '../../../../../../services/favourites_manager.dart';\nimport '../../../../services/download_manager.dart';\nimport '../../../../services/history_manager.dart';\n\npart 'backup_storage_state.dart';\n\nclass BackupStorageCubit extends Cubit<BackupStorageState> {\n  final SettingsManager _settingsManager = GetIt.I<SettingsManager>();\n  final FileStorage _fileStorage = GetIt.I<FileStorage>();\n  final DownloadManager _downloadsManager = GetIt.I<DownloadManager>();\n  final LibraryService _library = GetIt.I<LibraryService>();\n\n  late final VoidCallback _listener;\n\n  BackupStorageCubit()\n    : super(\n        BackupStorageState(\n          appFolder: GetIt.I<SettingsManager>().appFolder,\n          defaultPath: FileStorage.defaultPath,\n        ),\n      ) {\n    _listener = _emit;\n    _settingsManager.addListener(_listener);\n  }\n\n  void _emit() {\n    if (isClosed) return;\n    emit(\n      state.copyWith(\n        appFolder: _settingsManager.appFolder,\n        lastResult: null, // clear one-shot result\n      ),\n    );\n  }\n\n  Future<void> setAppFolder(String path) async {\n    _settingsManager.appFolder = path;\n    await _fileStorage.setupPaths();\n  }\n\n  Future<void> restore() async {\n    final success = await _fileStorage.loadBackup();\n    if (success) {\n      await _downloadsManager.reInit();\n      await _library.reInit();\n    }\n    emit(\n      state.copyWith(\n        lastResult: success ? const RestoreSuccess() : const RestoreFailure(),\n      ),\n    );\n  }\n\n  Future<void> backup({required String action, required List items}) async {\n    final Map backup = {\n      'name': 'Gyawun',\n      'type': 'backup',\n      'version': 1,\n      'data': {},\n    };\n\n    if (items.contains('playlists')) {\n      backup['data']['playlists'] = GetIt.I<LibraryService>().playlists;\n    }\n\n    if (items.contains('settings')) {\n      final settings = Map<String, dynamic>.from(\n        GetIt.I<SettingsManager>().settings,\n      );\n      settings.remove('YTMUSIC_AUTH');\n      backup['data']['settings'] = settings;\n    }\n\n    if (items.contains('favourites')) {\n      Map favourites = GetIt.I<FavouritesManager>().songs;\n      backup['data']['favourites'] = favourites;\n    }\n\n    if (items.contains('song history')) {\n      Map history = GetIt.I<HistoryManager>().songs.all;\n      backup['data']['song_history'] = history;\n    }\n\n    if (items.contains('downloads')) {\n      Map downloads = GetIt.I<DownloadManager>().downloads;\n      backup['data']['downloads'] = downloads;\n    }\n\n    String? path;\n    if (action == 'Save') {\n      path = await _fileStorage.saveBackUp(backup);\n    } else {\n      path = await _fileStorage.shareBackUp(backup);\n    }\n\n    emit(\n      state.copyWith(\n        lastResult: (path.isEmpty)\n            ? const BackupFailure()\n            : BackupSuccess(path),\n      ),\n    );\n  }\n\n  @override\n  Future<void> close() {\n    _settingsManager.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/backup_storage/cubit/backup_storage_state.dart",
    "content": "part of 'backup_storage_cubit.dart';\n\n@immutable\nclass BackupStorageState {\n  final String appFolder;\n  final String defaultPath;\n\n  /// one-shot results\n  final BackupResult? lastResult;\n\n  const BackupStorageState({\n    required this.appFolder,\n    required this.defaultPath,\n    this.lastResult,\n  });\n\n  BackupStorageState copyWith({\n    String? appFolder,\n    String? defaultPath,\n    BackupResult? lastResult,\n  }) {\n    return BackupStorageState(\n      appFolder: appFolder ?? this.appFolder,\n      defaultPath: defaultPath ?? this.defaultPath,\n      lastResult: lastResult,\n    );\n  }\n}\n\nsealed class BackupResult {\n  const BackupResult();\n}\n\nclass BackupSuccess extends BackupResult {\n  final String path;\n  const BackupSuccess(this.path);\n}\n\nclass BackupFailure extends BackupResult {\n  const BackupFailure();\n}\n\nclass RestoreSuccess extends BackupResult {\n  const RestoreSuccess();\n}\n\nclass RestoreFailure extends BackupResult {\n  const RestoreFailure();\n}\n"
  },
  {
    "path": "lib/screens/settings/cubit/settings_system_cubit.dart",
    "content": "import 'dart:io';\n\nimport 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:permission_handler/permission_handler.dart';\n\npart 'settings_system_state.dart';\n\nclass SettingsSystemCubit extends Cubit<SettingsSystemState> {\n  SettingsSystemCubit() : super(const SettingsSystemInitial());\n\n  Future<void> load() async {\n    if (!Platform.isAndroid) {\n      emit(const SettingsSystemLoaded(\n        isBatteryOptimizationDisabled: null,\n      ));\n      return;\n    }\n\n    final granted = await Permission.ignoreBatteryOptimizations.isGranted;\n\n    emit(\n      SettingsSystemLoaded(\n        isBatteryOptimizationDisabled: granted,\n      ),\n    );\n  }\n\n  Future<void> requestBatteryOptimizationIgnore() async {\n    if (!Platform.isAndroid) return;\n\n    await Permission.ignoreBatteryOptimizations.request();\n    await load();\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/cubit/settings_system_state.dart",
    "content": "part of 'settings_system_cubit.dart';\n\n@immutable\nsealed class SettingsSystemState {\n  const SettingsSystemState();\n}\n\nclass SettingsSystemInitial extends SettingsSystemState {\n  const SettingsSystemInitial();\n}\n\nclass SettingsSystemLoaded extends SettingsSystemState {\n  final bool? isBatteryOptimizationDisabled;\n\n  const SettingsSystemLoaded({\n    required this.isBatteryOptimizationDisabled,\n  });\n}\n"
  },
  {
    "path": "lib/screens/settings/player/cubit/player_settings_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\n\nimport '../../../../../../services/media_player.dart';\nimport '../../../../../../services/settings_manager.dart';\n\npart 'player_settings_state.dart';\n\nclass PlayerSettingsCubit extends Cubit<PlayerSettingsState> {\n  final SettingsManager _settings = GetIt.I<SettingsManager>();\n  final MediaPlayer _player = GetIt.I<MediaPlayer>();\n\n  late final VoidCallback _listener;\n\n  PlayerSettingsCubit()\n      : super(\n          PlayerSettingsLoaded(\n            skipSilence: GetIt.I<SettingsManager>().skipSilence,\n          ),\n        ) {\n    _listener = () {\n      if (!isClosed) {\n        _emitState();\n      }\n    };\n\n    _settings.addListener(_listener);\n  }\n\n  void _emitState() {\n    if (isClosed) return;\n\n    emit(\n      PlayerSettingsLoaded(\n        skipSilence: _settings.skipSilence,\n      ),\n    );\n  }\n\n  Future<void> setSkipSilence(bool value) async {\n    await _player.skipSilence(value);\n    _settings.skipSilence = value;\n    // listener will re-emit\n  }\n\n  @override\n  Future<void> close() {\n    _settings.removeListener(_listener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/player/cubit/player_settings_state.dart",
    "content": "part of 'player_settings_cubit.dart';\n\n@immutable\nsealed class PlayerSettingsState {\n  const PlayerSettingsState();\n}\n\nclass PlayerSettingsLoaded extends PlayerSettingsState {\n  final bool skipSilence;\n\n  const PlayerSettingsLoaded({\n    required this.skipSilence,\n  });\n}\n"
  },
  {
    "path": "lib/screens/settings/player/equalizer/cubit/equalizer_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/services/settings_manager.dart';\nimport 'equalizer_state.dart';\n\nclass EqualizerCubit extends Cubit<EqualizerState> {\n  final MediaPlayer _mediaPlayer = GetIt.I<MediaPlayer>();\n\n  EqualizerCubit() : super(EqualizerLoading()) {\n    _getEqualizerParameters();\n  }\n\n  Future<void> _getEqualizerParameters() async {\n    if (isClosed) return;\n    final parameters = await _mediaPlayer.getEqualizerParameters();\n    emit(\n      EqualizerLoaded(\n        enabled: GetIt.I<SettingsManager>().equalizerEnabled,\n        maxDb: parameters['maxDecibels'],\n        minDb: parameters['minDecibels'],\n        bands: parameters['bands'],\n      ),\n    );\n  }\n\n  Future<void> toggle(bool enabled) async {\n    if (state is EqualizerLoading) return;\n    await _mediaPlayer.setEqualizerEnabled(enabled);\n    _getEqualizerParameters();\n  }\n\n  Future<void> setBandGain(int index, double gain) async {\n    if (state is EqualizerLoading) return;\n    await _mediaPlayer.setEqualizerBandGain(index, gain);\n    _getEqualizerParameters();\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/player/equalizer/cubit/equalizer_state.dart",
    "content": "import 'package:flutter/foundation.dart';\n\n@immutable\nclass EqualizerState {\n  const EqualizerState();\n}\n\nclass EqualizerLoading extends EqualizerState {\n  const EqualizerLoading();\n}\n\nclass EqualizerLoaded extends EqualizerState {\n  final bool enabled;\n  final double minDb;\n  final double maxDb;\n  final List bands;\n\n  const EqualizerLoaded({\n    required this.enabled,\n    required this.minDb,\n    required this.maxDb,\n    required this.bands,\n  });\n}\n"
  },
  {
    "path": "lib/screens/settings/player/equalizer/cubit/loudness_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/services/settings_manager.dart';\nimport 'loudness_state.dart';\n\nclass LoudnessCubit extends Cubit<LoudnessState> {\n  final MediaPlayer _mediaPlayer = GetIt.I<MediaPlayer>();\n\n  LoudnessCubit()\n    : super(\n        LoudnessState(\n          enabled: GetIt.I<SettingsManager>().loudnessEnabled,\n          targetGain: GetIt.I<SettingsManager>().loudnessTargetGain,\n        ),\n      );\n\n  Future<void> toggle(bool enabled) async {\n    await _mediaPlayer.setLoudnessEnabled(enabled);\n    emit(state.copyWith(enabled: enabled));\n  }\n\n  Future<void> setTargetGain(double gain) async {\n    await _mediaPlayer.setLoudnessTargetGain(gain);\n    emit(state.copyWith(targetGain: gain));\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/player/equalizer/cubit/loudness_state.dart",
    "content": "import 'package:flutter/foundation.dart';\n\n@immutable\nclass LoudnessState {\n  final bool enabled;\n  final double targetGain;\n\n  const LoudnessState({\n    required this.enabled,\n    required this.targetGain,\n  });\n\n  LoudnessState copyWith({\n    bool? enabled,\n    double? targetGain,\n  }) {\n    return LoudnessState(\n      enabled: enabled ?? this.enabled,\n      targetGain: targetGain ?? this.targetGain,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/player/equalizer/equalizer_page.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:gyawun/screens/settings/player/equalizer/cubit/equalizer_cubit.dart';\nimport 'package:gyawun/screens/settings/player/equalizer/cubit/equalizer_state.dart';\nimport 'package:gyawun/screens/settings/player/equalizer/cubit/loudness_cubit.dart';\nimport 'package:gyawun/screens/settings/player/equalizer/cubit/loudness_state.dart';\nimport 'package:gyawun/generated/l10n.dart';\nimport 'package:gyawun/screens/settings/widgets/setting_item.dart';\nimport 'package:gyawun/themes/text_styles.dart';\nimport 'package:gyawun/utils/adaptive_widgets/slider.dart';\n\nclass EqualizerPage extends StatelessWidget {\n  const EqualizerPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MultiBlocProvider(\n      providers: [\n        BlocProvider(create: (_) => EqualizerCubit()),\n        BlocProvider(create: (_) => LoudnessCubit()),\n      ],\n      child: const _EqualizerView(),\n    );\n  }\n}\n\nclass _EqualizerView extends StatelessWidget {\n  const _EqualizerView();\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\n          S.of(context).Loudness_And_Equalizer,\n          style: mediumTextStyle(context, bold: false),\n        ),\n        centerTitle: true,\n      ),\n      body: ListView(\n        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),\n        children: [\n          /// LOUDNESS\n          GroupTitle(title: \"Loudness\"),\n          BlocBuilder<LoudnessCubit, LoudnessState>(\n            builder: (context, state) {\n              return SettingSwitchTile(\n                leading: const Icon(Icons.volume_up),\n                title: S.of(context).Loudness_Enhancer,\n                isFirst: true,\n                value: state.enabled,\n                onChanged: (val) async {\n                  context.read<LoudnessCubit>().toggle(val);\n                },\n              );\n            },\n          ),\n          SettingEmptyTile(\n            isLast: true,\n            child: BlocBuilder<LoudnessCubit, LoudnessState>(\n              builder: (context, state) {\n                return Slider(\n                  min: -1,\n                  max: 1,\n                  value: state.targetGain,\n                  onChanged: state.enabled\n                      ? (val) async {\n                          context.read<LoudnessCubit>().setTargetGain(val);\n                        }\n                      : null,\n                );\n              },\n            ),\n          ),\n\n          /// EQUALIZER\n          GroupTitle(title: \"Equalizer\"),\n          BlocBuilder<EqualizerCubit, EqualizerState>(\n            builder: (context, state) {\n              return SettingSwitchTile(\n                leading: const Icon(Icons.equalizer),\n                title: S.of(context).Enable_Equalizer,\n                isFirst: true,\n                value: state is EqualizerLoaded ? state.enabled : false,\n                onChanged: (val) async {\n                  context.read<EqualizerCubit>().toggle(val);\n                },\n              );\n            },\n          ),\n          SettingEmptyTile(\n            isLast: true,\n            child: BlocBuilder<EqualizerCubit, EqualizerState>(\n              builder: (context, state) {\n                if (state is EqualizerLoaded) {\n                  if (!state.enabled) {\n                    return const SizedBox();\n                  }\n                  return SizedBox(\n                    height: 250,\n                    child: Row(\n                      children: [\n                        for (final band in state.bands)\n                          Expanded(\n                            child: Column(\n                              children: [\n                                Text(band['gain'].toStringAsFixed(1)),\n                                Expanded(\n                                  child: AdaptiveSlider(\n                                    vertical: true,\n                                    min: state.minDb,\n                                    max: state.maxDb,\n                                    value: band['gain'],\n                                    onChanged: (val) async {\n                                      context\n                                          .read<EqualizerCubit>()\n                                          .setBandGain(band['index'], val);\n                                    },\n                                  ),\n                                ),\n                                Text('${band['centerFrequency'].round()} Hz'),\n                              ],\n                            ),\n                          ),\n                      ],\n                    ),\n                  );\n                } else {\n                  return SizedBox(\n                    child: Text(\n                      S.of(context).View_Equalizer,\n                      textAlign: TextAlign.center,\n                      style: TextStyle(\n                        color: Colors.grey.withValues(alpha: 0.5),\n                        fontStyle: FontStyle.italic,\n                      ),\n                    ),\n                  );\n                }\n              },\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/player/player_settings_page.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/expressive_list_group.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\nimport 'package:gyawun/core/widgets/expressive_switch_list_tile.dart';\nimport 'package:gyawun/screens/settings/widgets/color_icon.dart';\n\nimport '../../../generated/l10n.dart';\nimport 'cubit/player_settings_cubit.dart';\n\nclass PlayerSettingsPage extends StatelessWidget {\n  const PlayerSettingsPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => PlayerSettingsCubit(),\n      child: Scaffold(\n        body: NestedScrollView(\n          headerSliverBuilder: (context, innerBoxIsScrolled) {\n            return [ExpressiveAppBar(title: \"Player\", hasLeading: true)];\n          },\n          body: Center(\n            child: Container(\n              constraints: const BoxConstraints(maxWidth: 1000),\n              child: BlocBuilder<PlayerSettingsCubit, PlayerSettingsState>(\n                builder: (context, state) {\n                  final s = state as PlayerSettingsLoaded;\n\n                  return ListView(\n                    padding: const EdgeInsets.symmetric(\n                      horizontal: 16,\n                      vertical: 8,\n                    ),\n                    children: [\n                      ExpressiveListGroup(\n                        children: [\n                          ExpressiveListTile(\n                            title: Text(S.of(context).Loudness_And_Equalizer),\n                            leading: SettingsColorIcon(\n                              icon: Icons.equalizer_rounded,\n                            ),\n                            trailing: Icon(FluentIcons.chevron_right_24_filled),\n                            onTap: () =>\n                                context.go('/settings/player/equalizer'),\n                          ),\n                          ExpressiveSwitchListTile(\n                            title: Text(S.of(context).Skip_Silence),\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.fast_forward_24_filled,\n                            ),\n                            value: s.skipSilence,\n                            onChanged: (value) {\n                              context\n                                  .read<PlayerSettingsCubit>()\n                                  .setSkipSilence(value);\n                            },\n                          ),\n                        ],\n                      ),\n                    ],\n                  );\n                },\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/privacy/cubit/privacy_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/settings_manager.dart';\n\nimport '../../../../services/history_manager.dart';\n\npart 'privacy_state.dart';\n\nclass PrivacyCubit extends Cubit<PrivacyState> {\n  late final SettingsManager _settingsManager;\n  late final HistoryManager _historyManager;\n\n  PrivacyCubit() : super(PrivacyState.initial()) {\n    _settingsManager = GetIt.I<SettingsManager>();\n    _historyManager = GetIt.I<HistoryManager>();\n    _load();\n  }\n\n  void _load() {\n    emit(\n      state.copyWith(\n        playbackHistory: _settingsManager.playbackHistory,\n        searchHistory: _settingsManager.searchHistory,\n      ),\n    );\n  }\n\n  Future<void> togglePlaybackHistory(bool value) async {\n    _settingsManager.playbackHistory = value;\n    emit(state.copyWith(playbackHistory: value));\n  }\n\n  Future<void> toggleSearchHistory(bool value) async {\n    _settingsManager.searchHistory = value;\n    emit(state.copyWith(searchHistory: value));\n  }\n\n  Future<void> clearPlaybackHistory() async {\n    await _historyManager.songs.clear();\n    emit(state.copyWith(lastAction: PrivacyAction.playbackDeleted));\n  }\n\n  Future<void> clearSearchHistory() async {\n    await _historyManager.searches.clear();\n    emit(state.copyWith(lastAction: PrivacyAction.searchDeleted));\n  }\n\n  void consumeAction() {\n    emit(state.copyWith(lastAction: null));\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/privacy/cubit/privacy_state.dart",
    "content": "part of 'privacy_cubit.dart';\n\nenum PrivacyAction {\n  playbackDeleted,\n  searchDeleted,\n}\n\nclass PrivacyState {\n  final bool playbackHistory;\n  final bool searchHistory;\n  final PrivacyAction? lastAction;\n\n  const PrivacyState({\n    required this.playbackHistory,\n    required this.searchHistory,\n    this.lastAction,\n  });\n\n  factory PrivacyState.initial() => const PrivacyState(\n        playbackHistory: true,\n        searchHistory: true,\n      );\n\n  PrivacyState copyWith({\n    bool? playbackHistory,\n    bool? searchHistory,\n    PrivacyAction? lastAction,\n  }) {\n    return PrivacyState(\n      playbackHistory: playbackHistory ?? this.playbackHistory,\n      searchHistory: searchHistory ?? this.searchHistory,\n      lastAction: lastAction,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/privacy/privacy_page.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/expressive_list_group.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\nimport 'package:gyawun/core/widgets/expressive_switch_list_tile.dart';\nimport 'package:gyawun/screens/settings/widgets/color_icon.dart';\n\nimport '../../../../generated/l10n.dart';\nimport '../../../../utils/bottom_modals.dart';\nimport '../../../../services/bottom_message.dart';\n\nimport 'cubit/privacy_cubit.dart';\n\nclass PrivacyPage extends StatelessWidget {\n  const PrivacyPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => PrivacyCubit(),\n      child: BlocListener<PrivacyCubit, PrivacyState>(\n        listenWhen: (_, state) => state.lastAction != null,\n        listener: (context, state) {\n          final action = state.lastAction;\n          if (action == null) return;\n\n          if (action == PrivacyAction.playbackDeleted) {\n            BottomMessage.showText(\n              context,\n              S.of(context).Playback_History_Deleted,\n            );\n          } else if (action == PrivacyAction.searchDeleted) {\n            BottomMessage.showText(\n              context,\n              S.of(context).Search_History_Deleted,\n            );\n          }\n\n          context.read<PrivacyCubit>().consumeAction();\n        },\n        child: const _PrivacyView(),\n      ),\n    );\n  }\n}\n\nclass _PrivacyView extends StatelessWidget {\n  const _PrivacyView();\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: NestedScrollView(\n        headerSliverBuilder: (context, innerBoxIsScrolled) {\n          return [ExpressiveAppBar(title: \"Privacy\", hasLeading: true)];\n        },\n        body: Center(\n          child: Container(\n            constraints: const BoxConstraints(maxWidth: 1000),\n            child: BlocBuilder<PrivacyCubit, PrivacyState>(\n              builder: (context, state) {\n                final cubit = context.read<PrivacyCubit>();\n\n                return ListView(\n                  padding: const EdgeInsets.symmetric(horizontal: 16),\n                  children: [\n                    ExpressiveListGroup(\n                      title: \"Playback\",\n                      children: [\n                        ExpressiveSwitchListTile(\n                          title: Text(S.of(context).Enable_Playback_History),\n                          leading: const SettingsColorIcon(\n                            icon: Icons.play_arrow_rounded,\n                          ),\n                          value: state.playbackHistory,\n                          onChanged: cubit.togglePlaybackHistory,\n                        ),\n\n                        ExpressiveListTile(\n                          title: Text(S.of(context).Delete_Playback_History),\n                          leading: const SettingsColorIcon(\n                            icon: FluentIcons.history_dismiss_24_filled,\n                          ),\n                          onTap: () async {\n                            final confirm = await Modals.showConfirmBottomModal(\n                              context,\n                              message: S\n                                  .of(context)\n                                  .Delete_Playback_History_Confirm_Message,\n                              isDanger: true,\n                            );\n\n                            if (confirm == true) {\n                              cubit.clearPlaybackHistory();\n                            }\n                          },\n                        ),\n                      ],\n                    ),\n                    SizedBox(height: 24),\n                    ExpressiveListGroup(\n                      title: \"Search\",\n                      children: [\n                        ExpressiveSwitchListTile(\n                          title: Text(S.of(context).Enable_Search_History),\n                          leading: const SettingsColorIcon(\n                            icon: Icons.saved_search_rounded,\n                          ),\n                          value: state.searchHistory,\n                          onChanged: cubit.toggleSearchHistory,\n                        ),\n\n                        ExpressiveListTile(\n                          title: Text(S.of(context).Delete_Search_History),\n                          leading: const SettingsColorIcon(\n                            icon: Icons.manage_search_rounded,\n                          ),\n                          onTap: () async {\n                            final confirm = await Modals.showConfirmBottomModal(\n                              context,\n                              message: S\n                                  .of(context)\n                                  .Delete_Search_History_Confirm_Message,\n                              isDanger: true,\n                            );\n\n                            if (confirm == true) {\n                              cubit.clearSearchHistory();\n                            }\n                          },\n                        ),\n                      ],\n                    ),\n                  ],\n                );\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/services/yt_music/cubit/ytmusic_cubit.dart",
    "content": "import 'package:bloc/bloc.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:yt_music/client.dart';\nimport 'package:yt_music/ytmusic.dart';\n\nimport '../../../../../../services/settings_manager.dart';\n\npart 'ytmusic_state.dart';\n\nclass YTMusicCubit extends Cubit<YTMusicState> {\n  late final SettingsManager _settingsManager;\n  late final YTMusic _ytmusic;\n  late final VoidCallback _settingsListener;\n\n  List<Map<String, String>> get locations => _settingsManager.locations;\n  List<Map<String, String>> get languages => _settingsManager.languages;\n  List<AudioQuality> get audioQualities => _settingsManager.audioQualities;\n\n  YTMusicCubit()\n    : super(\n        YTMusicState(\n          location: GetIt.I<SettingsManager>().location,\n          language: GetIt.I<SettingsManager>().language,\n          autofetchSongs: GetIt.I<SettingsManager>().autofetchSongs,\n          streamingQuality: GetIt.I<SettingsManager>().streamingQuality,\n          downloadQuality: GetIt.I<SettingsManager>().downloadQuality,\n          translateLyrics: GetIt.I<SettingsManager>().translateLyrics,\n          personalisedContent: GetIt.I<SettingsManager>().personalisedContent,\n          visitorId: GetIt.I<SettingsManager>().visitorId!,\n        ),\n      ) {\n    _settingsManager = GetIt.I<SettingsManager>();\n    _ytmusic = GetIt.I<YTMusic>();\n    _settingsListener = _emit;\n\n    _settingsManager.addListener(_settingsListener);\n  }\n\n  void _emit() {\n    if (isClosed) return;\n\n    emit(\n      state.copyWith(\n        location: _settingsManager.location,\n        language: _settingsManager.language,\n        autofetchSongs: _settingsManager.autofetchSongs,\n        streamingQuality: _settingsManager.streamingQuality,\n        downloadQuality: _settingsManager.downloadQuality,\n        translateLyrics: _settingsManager.translateLyrics,\n        personalisedContent: _settingsManager.personalisedContent,\n        visitorId: _settingsManager.visitorId,\n      ),\n    );\n  }\n\n  void setLocation(Map<String, String> location) {\n    _settingsManager.location = location;\n  }\n\n  void setLanguage(Map<String, String> language) {\n    _settingsManager.language = language;\n  }\n\n  void setAutofetchSongs(bool value) {\n    _settingsManager.autofetchSongs = value;\n  }\n\n  void setStreamingQuality(AudioQuality quality) {\n    _settingsManager.streamingQuality = quality;\n  }\n\n  void setDownloadQuality(AudioQuality quality) {\n    _settingsManager.downloadQuality = quality;\n  }\n\n  Future<void> setTranslateLyrics(bool value) async {\n    _settingsManager.translateLyrics = value;\n  }\n\n  Future<void> setPersonalisedContent(bool value) async {\n    _settingsManager.personalisedContent = value;\n    final config = await YTClient.getConfig();\n    if (config != null) {\n      _settingsManager.visitorId = config.visitorData;\n    }\n  }\n\n  Future<void> setVisitorId(String id) async {\n    _settingsManager.visitorId = id;\n    _ytmusic.updateConfig(visitorData: id);\n  }\n\n  Future<void> resetVisitorId() async {\n    final config = await YTClient.getConfig();\n    if (config != null) {\n      _settingsManager.visitorId = config.visitorData;\n    }\n  }\n\n  @override\n  Future<void> close() {\n    _settingsManager.removeListener(_settingsListener);\n    return super.close();\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/services/yt_music/cubit/ytmusic_state.dart",
    "content": "part of 'ytmusic_cubit.dart';\n\n@immutable\nclass YTMusicState {\n  final Map<String, String> location;\n  final Map<String, String> language;\n  final bool autofetchSongs;\n\n  final AudioQuality streamingQuality;\n  final AudioQuality downloadQuality;\n\n  final bool translateLyrics;\n  final bool personalisedContent;\n  final String visitorId;\n\n  const YTMusicState({\n    required this.location,\n    required this.language,\n    required this.autofetchSongs,\n    required this.streamingQuality,\n    required this.downloadQuality,\n    required this.translateLyrics,\n    required this.personalisedContent,\n    required this.visitorId,\n  });\n\n  YTMusicState copyWith({\n    Map<String, String>? location,\n    Map<String, String>? language,\n    bool? autofetchSongs,\n    dynamic streamingQuality,\n    dynamic downloadQuality,\n    bool? translateLyrics,\n    bool? personalisedContent,\n    String? visitorId,\n  }) {\n    return YTMusicState(\n      location: location ?? this.location,\n      language: language ?? this.language,\n      autofetchSongs: autofetchSongs ?? this.autofetchSongs,\n      streamingQuality: streamingQuality ?? this.streamingQuality,\n      downloadQuality: downloadQuality ?? this.downloadQuality,\n      translateLyrics: translateLyrics ?? this.translateLyrics,\n      personalisedContent: personalisedContent ?? this.personalisedContent,\n      visitorId: visitorId ?? this.visitorId,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/services/yt_music/yt_music_page.dart",
    "content": "import 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/core/extensions/string_extensions.dart';\nimport 'package:gyawun/core/utils/expressive_sheet.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/expressive_list_group.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\nimport 'package:gyawun/core/widgets/expressive_switch_list_tile.dart';\nimport 'package:gyawun/generated/l10n.dart';\nimport 'package:gyawun/screens/settings/widgets/color_icon.dart';\nimport 'package:gyawun/services/settings_manager.dart';\nimport 'package:gyawun/utils/bottom_modals.dart';\n\nimport 'cubit/ytmusic_cubit.dart';\n\nclass YTMusicPage extends StatelessWidget {\n  const YTMusicPage({super.key});\n\n  Future<void> _setLocation(\n    BuildContext context,\n    Map<String, String> location,\n  ) async {\n    {\n      final selected = await ExpressiveSheet.showSelection(\n        context,\n        title: \"Choose Country\",\n        options: context\n            .read<YTMusicCubit>()\n            .locations\n            .map(\n              (l) => ExpressiveSheetOption(\n                value: l,\n                label: l['name']!.trim(),\n                selected: l == location,\n              ),\n            )\n            .toList(),\n      );\n      if (selected == null) return;\n      if (context.mounted) {\n        context.read<YTMusicCubit>().setLocation(selected);\n      }\n    }\n  }\n\n  Future<void> _setLanguage(\n    BuildContext context,\n    Map<String, String> language,\n  ) async {\n    {\n      final selected = await ExpressiveSheet.showSelection(\n        context,\n        title: \"Choose Language\",\n        options: context\n            .read<YTMusicCubit>()\n            .languages\n            .map(\n              (l) => ExpressiveSheetOption(\n                value: l,\n                label: l['name']!.trim(),\n                selected: l == language,\n              ),\n            )\n            .toList(),\n      );\n      if (selected == null) return;\n      if (context.mounted) {\n        context.read<YTMusicCubit>().setLanguage(selected);\n      }\n    }\n  }\n\n  Future<void> _setStreamingQuality(\n    BuildContext context,\n    AudioQuality quality,\n  ) async {\n    {\n      final selected = await ExpressiveSheet.showSelection(\n        context,\n        title: \"Choose Streaming Quality\",\n        options: context\n            .read<YTMusicCubit>()\n            .audioQualities\n            .map(\n              (l) => ExpressiveSheetOption(\n                value: l,\n                label: l.name.capitalize(),\n                selected: l == quality,\n              ),\n            )\n            .toList(),\n      );\n      if (selected == null) return;\n      if (context.mounted) {\n        context.read<YTMusicCubit>().setStreamingQuality(selected);\n      }\n    }\n  }\n\n  Future<void> _setDownloadingQuality(\n    BuildContext context,\n    AudioQuality quality,\n  ) async {\n    {\n      final selected = await ExpressiveSheet.showSelection(\n        context,\n        title: \"Choose Downloading Quality\",\n        options: context\n            .read<YTMusicCubit>()\n            .audioQualities\n            .map(\n              (l) => ExpressiveSheetOption(\n                value: l,\n                label: l.name.capitalize(),\n                selected: l == quality,\n              ),\n            )\n            .toList(),\n      );\n      if (selected == null) return;\n      if (context.mounted) {\n        context.read<YTMusicCubit>().setDownloadQuality(selected);\n      }\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => YTMusicCubit(),\n      child: Scaffold(\n        body: NestedScrollView(\n          headerSliverBuilder: (context, innerBoxIsScrolled) {\n            return [\n              ExpressiveAppBar(title: S.of(context).YTMusic, hasLeading: true),\n            ];\n          },\n          body: Center(\n            child: Container(\n              constraints: const BoxConstraints(maxWidth: 1000),\n              child: BlocBuilder<YTMusicCubit, YTMusicState>(\n                builder: (context, state) {\n                  final cubit = context.read<YTMusicCubit>();\n\n                  return ListView(\n                    padding: const EdgeInsets.symmetric(\n                      horizontal: 16,\n                      vertical: 8,\n                    ),\n                    children: [\n                      ExpressiveListGroup(\n                        title: \"General\",\n                        children: [\n                          ExpressiveListTile(\n                            title: Text(S.of(context).Country),\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.location_24_filled,\n                            ),\n                            subtitle: state.location['name'] != null\n                                ? Text(state.location['name']!)\n                                : null,\n                            onTap: () => _setLocation(context, state.location),\n                          ),\n                          ExpressiveListTile(\n                            title: Text(S.of(context).Language),\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.local_language_24_filled,\n                            ),\n                            subtitle: state.language['name'] != null\n                                ? Text(state.language['name']!)\n                                : null,\n                            onTap: () => _setLanguage(context, state.language),\n                          ),\n                          ExpressiveSwitchListTile(\n                            title: Text(S.of(context).Translate_Lyrics),\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.translate_24_filled,\n                            ),\n                            value: state.translateLyrics,\n                            onChanged: cubit.setTranslateLyrics,\n                          ),\n                          ExpressiveSwitchListTile(\n                            title: Text(S.of(context).Autofetch_Songs),\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons\n                                  .arrow_rotate_counterclockwise_24_filled,\n                            ),\n                            value: state.autofetchSongs,\n                            onChanged: cubit.setAutofetchSongs,\n                          ),\n                        ],\n                      ),\n                      SizedBox(height: 24),\n                      ExpressiveListGroup(\n                        title: 'Playback & download',\n                        children: [\n                          ExpressiveListTile(\n                            title: Text(S.of(context).Streaming_Quality),\n                            leading: SettingsColorIcon(\n                              icon: Icons.spatial_audio_rounded,\n                            ),\n                            subtitle: Text(\n                              state.streamingQuality.name.capitalize(),\n                            ),\n                            onTap: () => _setStreamingQuality(\n                              context,\n                              state.streamingQuality,\n                            ),\n                          ),\n                          ExpressiveListTile(\n                            title: Text(S.of(context).DOwnload_Quality),\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.cloud_arrow_down_24_filled,\n                            ),\n                            subtitle: Text(\n                              state.downloadQuality.name.capitalize(),\n                            ),\n                            onTap: () => _setDownloadingQuality(\n                              context,\n                              state.downloadQuality,\n                            ),\n                          ),\n                        ],\n                      ),\n                      SizedBox(height: 24),\n\n                      ExpressiveListGroup(\n                        title: 'Privacy',\n                        children: [\n                          ExpressiveSwitchListTile(\n                            title: Text(S.of(context).Personalised_Content),\n                            leading: const SettingsColorIcon(\n                              icon: Icons.recommend_rounded,\n                            ),\n                            value: state.personalisedContent,\n                            onChanged: (v) async {\n                              Modals.showCenterLoadingModal(context);\n                              await cubit.setPersonalisedContent(v);\n                              if (context.mounted) context.pop();\n                            },\n                          ),\n                          ExpressiveListTile(\n                            title: Text(S.of(context).Enter_Visitor_Id),\n                            leading: const SettingsColorIcon(\n                              icon: FluentIcons.edit_24_filled,\n                            ),\n                            onTap: () async {\n                              final text = await Modals.showTextField(\n                                context,\n                                title: S.of(context).Enter_Visitor_Id,\n                                hintText: S.of(context).Visitor_Id,\n                              );\n                              if (text != null) {\n                                cubit.setVisitorId(text);\n                              }\n                            },\n                          ),\n\n                          ExpressiveListTile(\n                            title: Text(S.of(context).Reset_Visitor_Id),\n                            leading: const SettingsColorIcon(\n                              icon: FluentIcons.key_reset_24_filled,\n                            ),\n                            subtitle: Text(state.visitorId),\n                            trailing: state.visitorId.isEmpty\n                                ? null\n                                : IconButton(\n                                    icon: const Icon(Icons.copy),\n                                    onPressed: () {\n                                      Clipboard.setData(\n                                        ClipboardData(text: state.visitorId),\n                                      );\n                                    },\n                                  ),\n                            onTap: () async {\n                              Modals.showCenterLoadingModal(context);\n                              await cubit.resetVisitorId();\n                              if (context.mounted) context.pop();\n                            },\n                          ),\n                        ],\n                      ),\n                    ],\n                  );\n                },\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/settings_page.dart",
    "content": "import 'dart:io';\n\nimport 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/core/widgets/expressive_app_bar.dart';\nimport 'package:gyawun/core/widgets/expressive_list_group.dart';\nimport 'package:gyawun/core/widgets/expressive_list_tile.dart';\nimport 'package:gyawun/services/update_service/update_service.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nimport '../../generated/l10n.dart';\nimport '../../themes/text_styles.dart';\nimport '../../utils/adaptive_widgets/adaptive_widgets.dart';\nimport '../../utils/bottom_modals.dart';\nimport 'widgets/color_icon.dart';\nimport 'cubit/settings_system_cubit.dart';\n\nclass SettingsPage extends StatelessWidget {\n  const SettingsPage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocProvider(\n      create: (_) => SettingsSystemCubit()..load(),\n      child: AdaptiveScaffold(\n        body: BlocBuilder<SettingsSystemCubit, SettingsSystemState>(\n          builder: (context, state) {\n            final bool? batteryDisabled = state is SettingsSystemLoaded\n                ? state.isBatteryOptimizationDisabled\n                : null;\n\n            return Center(\n              child: Container(\n                constraints: const BoxConstraints(maxWidth: 1000),\n                child: NestedScrollView(\n                  headerSliverBuilder: (context, innerBoxIsScrolled) {\n                    return [ExpressiveAppBar(title: S.of(context).Settings)];\n                  },\n                  body: ListView(\n                    padding: .symmetric(horizontal: 16, vertical: 8),\n                    children: [\n                      if (Platform.isAndroid && batteryDisabled != true)\n                        _BatteryWarningTile(),\n                      if (Platform.isAndroid && batteryDisabled != true)\n                        SizedBox(height: 24),\n                      ExpressiveListGroup(\n                        title: \"General\",\n                        children: [\n                          ExpressiveListTile(\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.color_background_24_filled,\n                              color: const Color.fromARGB(155, 183, 86, 118),\n                            ),\n                            title: Text(S.of(context).Appearence),\n                            subtitle: Text('Themes, layout, and visual style'),\n                            onTap: () => context.go('/settings/appearance'),\n                            trailing: const Icon(\n                              FluentIcons.chevron_right_24_filled,\n                            ),\n                          ),\n                          ExpressiveListTile(\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.play_24_filled,\n                              color: const Color.fromARGB(155, 70, 92, 141),\n                            ),\n                            title: Text('Player'),\n                            subtitle: Text('Audio effects & playback'),\n                            onTap: () => context.go('/settings/player'),\n                            trailing: const Icon(\n                              FluentIcons.chevron_right_24_filled,\n                            ),\n                          ),\n                        ],\n                      ),\n                      SizedBox(height: 24),\n\n                      ExpressiveListGroup(\n                        title: \"Services\",\n                        children: [\n                          ExpressiveListTile(\n                            leading: SettingsColorIcon(\n                              icon: Icons.play_circle_fill,\n                              color: const Color.fromARGB(155, 181, 54, 54),\n                            ),\n                            title: Text('Youtube Music'),\n                            subtitle: Text(\n                              'Content region, language, audio quality',\n                            ),\n                            onTap: () =>\n                                context.go('/settings/services/ytmusic'),\n                            trailing: const Icon(\n                              FluentIcons.chevron_right_24_filled,\n                            ),\n                          ),\n                        ],\n                      ),\n                      SizedBox(height: 24),\n\n                      ExpressiveListGroup(\n                        title: \"Storage & Privacy\",\n                        children: [\n                          ExpressiveListTile(\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.storage_24_filled,\n                              color: const Color.fromARGB(155, 130, 146, 66),\n                            ),\n                            title: Text('Backup and storage'),\n                            subtitle: Text('App folder, backup, and restore'),\n                            onTap: () => context.go('/settings/backup_storage'),\n                            trailing: const Icon(\n                              FluentIcons.chevron_right_24_filled,\n                            ),\n                          ),\n                          ExpressiveListTile(\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.shield_keyhole_24_filled,\n                              color: const Color.fromARGB(155, 46, 115, 76),\n                            ),\n                            title: Text('Privacy'),\n                            subtitle: Text('Playback & search history'),\n                            onTap: () => context.go('/settings/privacy'),\n                            trailing: const Icon(\n                              FluentIcons.chevron_right_24_filled,\n                            ),\n                          ),\n                        ],\n                      ),\n                      SizedBox(height: 24),\n\n                      ExpressiveListGroup(\n                        title: \"Updates & About\",\n                        children: [\n                          ExpressiveListTile(\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.info_24_filled,\n                              color: const Color.fromARGB(155, 115, 84, 46),\n                            ),\n                            title: Text(S.of(context).About),\n                            subtitle: Text('App info, support & links'),\n                            onTap: () => context.go('/settings/about'),\n                            trailing: const Icon(\n                              FluentIcons.chevron_right_24_filled,\n                            ),\n                          ),\n                          ExpressiveListTile(\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.arrow_circle_up_24_filled,\n                              color: const Color.fromARGB(155, 115, 46, 62),\n                            ),\n                            title: Text(S.of(context).Check_For_Update),\n                            subtitle: Text('Check GitHub for releases'),\n                            onTap: () => UpdateService.manualCheck(context),\n                            trailing: const Icon(\n                              FluentIcons.chevron_right_24_filled,\n                            ),\n                          ),\n                          ExpressiveListTile(\n                            leading: SettingsColorIcon(\n                              icon: FluentIcons.money_24_filled,\n                              color: const Color.fromARGB(155, 46, 100, 115),\n                            ),\n                            title: Text(S.of(context).Donate),\n                            subtitle: Text(S.of(context).Donate_Message),\n                            onTap: () => showPaymentsModal(context),\n                            trailing: const Icon(\n                              FluentIcons.chevron_right_24_filled,\n                            ),\n                          ),\n                        ],\n                      ),\n                    ],\n                  ),\n                ),\n              ),\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass _BatteryWarningTile extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return ListTile(\n      tileColor: Theme.of(context).colorScheme.errorContainer.withAlpha(200),\n      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),\n      leading: ColorIcon(\n        icon: Icons.battery_alert,\n        boxColor: Colors.red,\n        iconColor: Colors.white.withAlpha(255),\n      ),\n      title: Text(\n        S.of(context).Battery_Optimisation_title,\n        style: TextStyle(color: Theme.of(context).colorScheme.onErrorContainer),\n      ),\n      subtitle: Text(\n        S.of(context).Battery_Optimisation_message,\n        style: tinyTextStyle(context).copyWith(\n          color: Theme.of(\n            context,\n          ).colorScheme.onErrorContainer.withValues(alpha: 0.7),\n        ),\n      ),\n      onTap: () {\n        context.read<SettingsSystemCubit>().requestBatteryOptimizationIgnore();\n      },\n    );\n  }\n}\n\nvoid showPaymentsModal(BuildContext context) {\n  Widget title = AdaptiveListTile(\n    contentPadding: EdgeInsets.zero,\n    title: Text(S.of(context).Payment_Methods, style: mediumTextStyle(context)),\n    leading: Column(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        SizedBox(\n          height: 40,\n          width: 40,\n          child: ColorIcon(\n            boxColor: Colors.accents[14],\n            iconColor: Colors.white.withAlpha(255),\n            icon: Icons.money,\n          ),\n        ),\n      ],\n    ),\n  );\n  Widget child = Column(\n    mainAxisSize: MainAxisSize.min,\n    children: [\n      AdaptiveListTile(\n        leading: ClipRRect(\n          borderRadius: BorderRadius.circular(6),\n          child: Image.asset('assets/images/upi.jpg', height: 30, width: 30),\n        ),\n        title: Text(\n          S.of(context).Pay_With_UPI,\n          style: subtitleTextStyle(context),\n        ),\n        onTap: () async {\n          Navigator.pop(context);\n          await Clipboard.setData(\n            const ClipboardData(text: 'sheikhhaziq76@okaxis'),\n          );\n          if (context.mounted) {\n            ScaffoldMessenger.of(context).showSnackBar(\n              SnackBar(content: Text(\"Copied UPI ID to clipboard!\")),\n            );\n          }\n        },\n      ),\n      AdaptiveListTile(\n        leading: Container(\n          decoration: BoxDecoration(\n            color: const Color.fromRGBO(19, 195, 255, 1),\n            borderRadius: BorderRadius.circular(6),\n          ),\n          child: Image.asset('assets/images/kofi.png', height: 30, width: 30),\n        ),\n        title: Text(\n          S.of(context).Support_Me_On_Kofi,\n          style: subtitleTextStyle(context),\n        ),\n        onTap: () async {\n          Navigator.pop(context);\n          await launchUrl(\n            Uri.parse('https://ko-fi.com/sheikhhaziq'),\n            mode: LaunchMode.externalApplication,\n          );\n        },\n      ),\n      AdaptiveListTile(\n        leading: ClipRRect(\n          borderRadius: BorderRadius.circular(6),\n          child: Image.asset('assets/images/coffee.png', height: 30, width: 30),\n        ),\n        title: Text(\n          S.of(context).Buy_Me_A_Coffee,\n          style: subtitleTextStyle(context),\n        ),\n        onTap: () async {\n          Navigator.pop(context);\n          await launchUrl(\n            Uri.parse('https://buymeacoffee.com/sheikhhaziq'),\n            mode: LaunchMode.externalApplication,\n          );\n        },\n      ),\n    ],\n  );\n\n  showModalBottomSheet(\n    useSafeArea: true,\n    backgroundColor: Colors.transparent,\n    context: context,\n    builder: (context) => BottomModalLayout(title: title, child: child),\n  );\n}\n"
  },
  {
    "path": "lib/screens/settings/widgets/color_icon.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass ColorIcon extends StatelessWidget {\n  const ColorIcon({\n    required this.icon,\n    required this.boxColor,\n    required this.iconColor,\n    this.size,\n    this.borderRadius,\n    this.padding,\n    super.key,\n  });\n  final IconData icon;\n  final Color? boxColor;\n  final Color? iconColor;\n  final double? size;\n  final double? borderRadius;\n  final double? padding;\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      padding: EdgeInsets.all(padding ?? 6),\n      decoration: BoxDecoration(\n        color: boxColor,\n        borderRadius: BorderRadius.circular(borderRadius ?? 8),\n      ),\n      child: Icon(icon, color: iconColor, size: size ?? 20),\n    );\n  }\n}\n\nclass SettingsColorIcon extends StatelessWidget {\n  const SettingsColorIcon({super.key, required this.icon, this.color});\n  final IconData icon;\n  final Color? color;\n\n  @override\n  Widget build(BuildContext context) {\n    return ColorIcon(\n      icon: icon,\n      boxColor:\n          color ??\n          Theme.of(context).colorScheme.primaryContainer.withAlpha(150),\n      iconColor: color != null\n          ? Colors.white.withAlpha(255)\n          : Theme.of(context).colorScheme.onPrimaryContainer,\n      borderRadius: 24,\n      padding: 12,\n      size: 24,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/widgets/setting_item.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass GroupTitle extends StatelessWidget {\n  const GroupTitle({super.key, required this.title});\n  final String title;\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 8),\n      child: Text(\n        title,\n        style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n              fontWeight: FontWeight.bold,\n              color: Theme.of(context).colorScheme.primary,\n            ),\n      ),\n    );\n  }\n}\n\nclass SettingEmptyTile extends StatelessWidget {\n  final bool isFirst;\n  final bool isLast;\n  final Widget? leading;\n  final Widget? child;\n  const SettingEmptyTile(\n      {super.key,\n      this.isFirst = false,\n      this.isLast = false,\n      this.leading,\n      this.child});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListTile(\n      shape: RoundedRectangleBorder(\n        borderRadius: BorderRadius.only(\n          topLeft: Radius.circular(isFirst ? 20 : 4),\n          topRight: Radius.circular(isFirst ? 20 : 4),\n          bottomLeft: Radius.circular(isLast ? 20 : 4),\n          bottomRight: Radius.circular(isLast ? 20 : 4),\n        ),\n      ),\n      tileColor: Theme.of(context).colorScheme.surfaceContainer,\n      contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n      leading: leading == null\n          ? null\n          : Container(\n              padding: EdgeInsets.all(8),\n              decoration: BoxDecoration(\n                color: Theme.of(\n                  context,\n                ).colorScheme.primaryContainer.withAlpha(150),\n                borderRadius: BorderRadius.circular(12),\n              ),\n              child: leading,\n            ),\n      title: child,\n    );\n  }\n}\n\nclass SettingTile extends StatelessWidget {\n  final String title;\n  final String? subtitle;\n  final int subtitleLines;\n  final Widget leading;\n  final Widget? trailing;\n  final void Function()? onTap;\n  final bool isFirst;\n  final bool isLast;\n  const SettingTile({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.subtitleLines = 1,\n    required this.leading,\n    this.trailing,\n    this.onTap,\n    this.isFirst = false,\n    this.isLast = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.only(bottom: 2),\n      child: Material(\n        color: Colors.transparent,\n        shadowColor: Colors.transparent,\n        elevation: 0,\n        child: ListTile(\n          onTap: onTap,\n          shape: RoundedRectangleBorder(\n            borderRadius: BorderRadius.only(\n              topLeft: Radius.circular(isFirst ? 20 : 4),\n              topRight: Radius.circular(isFirst ? 20 : 4),\n              bottomLeft: Radius.circular(isLast ? 20 : 4),\n              bottomRight: Radius.circular(isLast ? 20 : 4),\n            ),\n          ),\n          tileColor: Theme.of(context).colorScheme.surfaceContainer,\n          contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n          title: Text(\n            title,\n            style: Theme.of(context).textTheme.bodyLarge?.copyWith(\n                  fontSize: 16,\n                  fontWeight: FontWeight.w500,\n                ),\n          ),\n          leading: Container(\n            padding: EdgeInsets.all(8),\n            decoration: BoxDecoration(\n              color: Theme.of(\n                context,\n              ).colorScheme.primaryContainer.withAlpha(150),\n              borderRadius: BorderRadius.circular(12),\n            ),\n            child: leading,\n          ),\n          subtitle: subtitle != null\n              ? Text(\n                  subtitle!,\n                  style: Theme.of(context).textTheme.labelLarge,\n                  maxLines: subtitleLines,\n                  overflow: TextOverflow.ellipsis,\n                )\n              : null,\n          trailing: trailing,\n        ),\n      ),\n    );\n  }\n}\n\nclass SettingSwitchTile extends StatelessWidget {\n  final bool value;\n  final String title;\n  final Widget leading;\n  final String? subtitle;\n\n  final ValueChanged<bool>? onChanged;\n  final bool isFirst;\n  final bool isLast;\n  const SettingSwitchTile({\n    super.key,\n    required this.value,\n    required this.title,\n    required this.leading,\n    this.onChanged,\n    this.isFirst = false,\n    this.isLast = false,\n    this.subtitle,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.only(bottom: 1),\n      child: SwitchListTile(\n        value: value,\n        onChanged: onChanged,\n        shape: RoundedRectangleBorder(\n          borderRadius: BorderRadius.only(\n            topLeft: Radius.circular(isFirst ? 20 : 4),\n            topRight: Radius.circular(isFirst ? 20 : 4),\n            bottomLeft: Radius.circular(isLast ? 20 : 4),\n            bottomRight: Radius.circular(isLast ? 20 : 4),\n          ),\n        ),\n        tileColor: Theme.of(context).colorScheme.surfaceContainer,\n        contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n        title: Text(\n          title,\n          style: Theme.of(context).textTheme.bodyLarge?.copyWith(\n                fontSize: 16,\n                fontWeight: FontWeight.w500,\n              ),\n        ),\n        subtitle: subtitle != null\n            ? Text(subtitle!, style: Theme.of(context).textTheme.labelLarge)\n            : null,\n        secondary: Container(\n          padding: EdgeInsets.all(8),\n          decoration: BoxDecoration(\n            color: Theme.of(\n              context,\n            ).colorScheme.primaryContainer.withAlpha(150),\n            borderRadius: BorderRadius.circular(12),\n          ),\n          child: leading,\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/shell/app_shell.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\nimport 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/services/update_service/update_service.dart';\nimport 'package:navigation_rail_m3e/navigation_rail_m3e.dart';\nimport 'package:receive_sharing_intent/receive_sharing_intent.dart';\n\nimport '../../generated/l10n.dart';\nimport 'widgets/bottom_player.dart';\n\nclass AppShell extends StatefulWidget {\n  const AppShell({Key? key, required this.navigationShell})\n    : super(key: key ?? const ValueKey('AppShell'));\n  final StatefulNavigationShell navigationShell;\n\n  @override\n  State<AppShell> createState() => _AppShellState();\n}\n\nclass _AppShellState extends State<AppShell> {\n  late StreamSubscription _intentSub;\n  @override\n  void initState() {\n    super.initState();\n    if (Platform.isAndroid) {\n      _intentSub = ReceiveSharingIntent.instance.getMediaStream().listen((\n        value,\n      ) {\n        if (value.isNotEmpty) _handleIntent(value.first);\n      });\n\n      ReceiveSharingIntent.instance.getInitialMedia().then((value) {\n        if (value.isNotEmpty) _handleIntent(value.first);\n        ReceiveSharingIntent.instance.reset();\n      });\n    }\n\n    UpdateService.autoCheck(context);\n  }\n\n  void _handleIntent(SharedMediaFile value) {\n    if (value.mimeType == 'text/plain' &&\n        value.path.contains('music.youtube.com')) {\n      Uri? uri = Uri.tryParse(value.path);\n      if (uri != null) {\n        if (uri.pathSegments.first == 'watch' &&\n            uri.queryParameters['v'] != null) {\n          context.push('/player', extra: uri.queryParameters['v']);\n        } else if (uri.pathSegments.first == 'playlist' &&\n            uri.queryParameters['list'] != null) {\n          String id = uri.queryParameters['list']!;\n          context.push(\n            '/browse',\n            extra: {\n              'endpoint': {'browseId': id.startsWith('VL') ? id : 'VL$id'},\n            },\n          );\n        }\n      }\n    }\n  }\n\n  @override\n  void dispose() {\n    _intentSub.cancel();\n    super.dispose();\n  }\n\n  void _goBranch(int index) {\n    widget.navigationShell.goBranch(\n      index,\n      initialLocation: index == widget.navigationShell.currentIndex,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    double screenWidth = MediaQuery.of(context).size.width;\n    return Scaffold(\n      body: Column(\n        children: [\n          Expanded(\n            child: Row(\n              children: [\n                if (screenWidth >= 450)\n                  NavigationRailM3E(\n                    type: screenWidth > 1000\n                        ? NavigationRailM3EType.expanded\n                        : NavigationRailM3EType.collapsed,\n                    onDestinationSelected: _goBranch,\n                    sections: [\n                      NavigationRailM3ESection(\n                        destinations: [\n                          NavigationRailM3EDestination(\n                            selectedIcon: const Icon(\n                              FluentIcons.home_24_filled,\n                            ),\n                            icon: const Icon(FluentIcons.home_24_regular),\n                            label: S.of(context).Home,\n                          ),\n                          NavigationRailM3EDestination(\n                            selectedIcon: const Icon(\n                              FluentIcons.library_24_filled,\n                            ),\n                            icon: const Icon(FluentIcons.library_24_regular),\n                            label: 'Library',\n                          ),\n                          NavigationRailM3EDestination(\n                            selectedIcon: const Icon(\n                              FluentIcons.settings_24_filled,\n                            ),\n                            icon: const Icon(FluentIcons.settings_24_regular),\n                            label: S.of(context).Settings,\n                          ),\n                        ],\n                      ),\n                    ],\n                    selectedIndex: widget.navigationShell.currentIndex,\n                  ),\n                Expanded(child: widget.navigationShell),\n              ],\n            ),\n          ),\n          const BottomPlayer(),\n        ],\n      ),\n      bottomNavigationBar: screenWidth < 450\n          ? NavigationBar(\n              selectedIndex: widget.navigationShell.currentIndex,\n              labelBehavior: .onlyShowSelected,\n              destinations: [\n                NavigationDestination(\n                  selectedIcon: const Icon(FluentIcons.home_24_filled),\n                  icon: const Icon(FluentIcons.home_24_regular),\n                  label: S.of(context).Home,\n                ),\n                NavigationDestination(\n                  selectedIcon: const Icon(FluentIcons.library_24_filled),\n                  icon: const Icon(FluentIcons.library_24_regular),\n                  label: 'Library',\n                ),\n                NavigationDestination(\n                  selectedIcon: const Icon(FluentIcons.settings_24_filled),\n                  icon: const Icon(FluentIcons.settings_24_regular),\n                  label: S.of(context).Settings,\n                ),\n              ],\n              backgroundColor: Theme.of(\n                context,\n              ).colorScheme.surfaceContainerLow,\n              // colo: Theme.of(context).colorScheme.onSurface,\n              onDestinationSelected: _goBranch,\n            )\n          : null,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/shell/widgets/bottom_player.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/services/media_player.dart';\nimport 'package:gyawun/utils/adaptive_widgets/buttons.dart';\nimport 'package:gyawun/utils/adaptive_widgets/listtile.dart';\nimport 'package:gyawun/utils/song_thumbnail.dart';\nimport 'package:m3e_collection/m3e_collection.dart';\nimport 'package:provider/provider.dart';\n\nclass BottomPlayer extends StatelessWidget {\n  const BottomPlayer({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final mediaPlayer = GetIt.I<MediaPlayer>();\n    return StreamBuilder(\n      stream: mediaPlayer.currentTrackStream,\n      builder: (context, snapshot) {\n        final data = snapshot.data;\n        final currentSong = data?.currentItem;\n        if (currentSong == null) {\n          return const SizedBox(); // or loading indicator\n        }\n        return Container(\n          color: Theme.of(context).colorScheme.surfaceContainerLow,\n          child: GestureDetector(\n            onTap: () {\n              context.push('/player');\n            },\n            child: SafeArea(\n              top: false,\n              child: Dismissible(\n                key: Key('bottomplayer${currentSong.id}'),\n                direction: DismissDirection.down,\n                confirmDismiss: (direction) async {\n                  await GetIt.I<MediaPlayer>().stop();\n                  return true;\n                },\n                child: Dismissible(\n                  key: Key(currentSong.id),\n                  confirmDismiss: (direction) async {\n                    if (direction == DismissDirection.startToEnd) {\n                      await GetIt.I<MediaPlayer>().player.seekToPrevious();\n                    } else {\n                      await GetIt.I<MediaPlayer>().player.seekToNext();\n                    }\n                    return Future.value(false);\n                  },\n                  child: AdaptiveListTile(\n                    contentPadding: const EdgeInsets.symmetric(\n                      horizontal: 8,\n                      vertical: 8,\n                    ),\n                    leading: ClipRRect(\n                      borderRadius: BorderRadius.circular(8),\n                      child: SongThumbnail(\n                        song: currentSong.extras!,\n                        dp: MediaQuery.of(context).devicePixelRatio,\n                        height: 50,\n                        width: 50,\n                        fit: BoxFit.fill,\n                      ),\n                    ),\n                    title: Text(\n                      currentSong.title,\n                      // style: textStyle(context, bold: true),\n                      maxLines: 1,\n                      overflow: TextOverflow.ellipsis,\n                    ),\n                    subtitle:\n                        (currentSong.artist != null ||\n                            currentSong.extras!['subtitle'] != null)\n                        ? Text(\n                            currentSong.artist ??\n                                currentSong.extras!['subtitle'],\n                            maxLines: 1,\n                            overflow: TextOverflow.ellipsis,\n                          )\n                        : null,\n                    trailing: Row(\n                      children: [\n                        ValueListenableBuilder(\n                          valueListenable: GetIt.I<MediaPlayer>().buttonState,\n                          builder: (context, buttonState, child) {\n                            return (buttonState == ButtonState.loading)\n                                ? const ExpressiveLoadingIndicator()\n                                : AdaptiveIconButton(\n                                    onPressed: () {\n                                      GetIt.I<MediaPlayer>().player.playing\n                                          ? GetIt.I<MediaPlayer>().player\n                                                .pause()\n                                          : GetIt.I<MediaPlayer>().player\n                                                .play();\n                                    },\n                                    icon: Icon(\n                                      buttonState == ButtonState.playing\n                                          ? Icons.pause\n                                          : Icons.play_arrow,\n                                      size: 30,\n                                    ),\n                                  );\n                          },\n                        ),\n                        StreamBuilder(\n                          stream: context\n                              .watch<MediaPlayer>()\n                              .player\n                              .sequenceStateStream,\n                          builder: (context, snapshot) {\n                            if (context.watch<MediaPlayer>().player.hasNext) {\n                              return AdaptiveIconButton(\n                                onPressed: () {\n                                  GetIt.I<MediaPlayer>().player.seekToNext();\n                                },\n                                icon: Icon(Icons.skip_next, size: 25),\n                              );\n                            }\n                            return const SizedBox.shrink();\n                          },\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ),\n            ),\n          ),\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/services/bottom_message.dart",
    "content": "import 'package:fl_toast/fl_toast.dart';\nimport 'package:flutter/material.dart';\n\nimport '../themes/text_styles.dart';\nimport '../utils/adaptive_widgets/theme.dart';\n\nclass BottomMessage {\n  static void showText(BuildContext context, String text,\n      {Duration duration = const Duration(milliseconds: 1500)}) {\n    showPlatformToast(\n      child: Text(\n        text,\n        style: smallTextStyle(context, bold: false, opacity: 0.8)\n            .copyWith(color: AdaptiveTheme.of(context).inactiveBackgroundColor),\n      ),\n      context: context,\n      duration: duration,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/services/custom_audio_stream.dart",
    "content": "// import 'dart:async';\n// import 'package:gyawun/services/stream_client.dart';\n// import 'package:just_audio/just_audio.dart';\n// import 'package:youtube_explode_dart/youtube_explode_dart.dart';\n\n// mixin HlsStreamInfo on StreamInfo {\n//   /// The tag of the audio stream related to this stream.\n//   int? get audioItag => null;\n// }\n\n// class CustomAudioStream extends StreamAudioSource {\n//   late YoutubeExplode ytExplode;\n//   StreamInfo? streamInfo;\n//   String videoId;\n//   String quality;\n\n//   CustomAudioStream(this.videoId, this.quality, {super.tag}) {\n//     ytExplode = YoutubeExplode();\n//   }\n\n//   Future _loadStreamInfo() async {\n//     StreamManifest manifest =\n//         await ytExplode.videos.streamsClient.getManifest(videoId);\n\n//     List<AudioOnlyStreamInfo> streamInfos = manifest.audioOnly\n//         .sortByBitrate()\n//         .reversed\n//         .where((stream) => stream.container == StreamContainer.mp4)\n//         .toList();\n\n//     int qualityIndex = quality == 'low' ? 0 : streamInfos.length - 1;\n//     streamInfo = streamInfos[qualityIndex];\n//     if (streamInfo!.fragments.isNotEmpty) {\n//       print(\"Using DASH Fragmented Stream\");\n//     } else if (streamInfo is HlsStreamInfo) {\n//       print(\"Using HLS Stream\");\n//     } else {\n//       print(\"Using Normal Stream\");\n//     }\n//     print(streamInfo!.url.toString());\n//   }\n\n//   @override\n//   Future<StreamAudioResponse> request([int? start, int? end]) async {\n//     if (streamInfo == null) {\n//       await _loadStreamInfo();\n//     }\n\n//     start ??= 0;\n\n//     int size = 10379935;\n//     end = start + size.clamp(0, streamInfo!.size.totalBytes - start);\n//     // if (!streamInfo!.isThrottled) {\n\n//     // }\n//     // end ??= streamInfo!.size.totalBytes;\n//     if (end > streamInfo!.size.totalBytes) {\n//       end = streamInfo!.size.totalBytes;\n//     }\n//     print('$start-$end');\n//     final response = ytExplode.videos.streams.get(streamInfo!, start, end);\n\n//     return StreamAudioResponse(\n//       sourceLength: streamInfo!.size.totalBytes,\n//       contentLength: null,\n//       offset: null,\n//       stream: response,\n//       contentType: streamInfo!.codec.type,\n//     );\n//   }\n// }\n"
  },
  {
    "path": "lib/services/download_manager.dart",
    "content": "import 'dart:collection';\nimport 'package:collection/collection.dart';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:hive_flutter/hive_flutter.dart';\nimport 'package:http/http.dart';\nimport 'package:youtube_explode_dart/youtube_explode_dart.dart';\nimport 'package:yt_music/ytmusic.dart';\n\nimport 'file_storage.dart';\nimport 'settings_manager.dart';\nimport 'favourites_manager.dart';\nimport 'stream_client.dart';\n\nYoutubeExplode ytExplode = YoutubeExplode();\n\nclass DownloadCanceledException implements Exception {}\n\nclass DownloadManager {\n  final Box _box;\n  Client client = Client();\n  ValueNotifier<List<Map>> downloadsNotifier = ValueNotifier([]);\n  ValueNotifier<Map<String, Map>> playlistsNotifier = ValueNotifier({});\n  final Map<String, ValueNotifier<double>> _activeDownloadProgress = {};\n  static const String songsPlaylistId = 'SNGS';\n  final int maxConcurrentDownloads = 3; // Limit concurrent downloads\n  final Queue<String> _activeDownloads =\n      Queue<String>(); // Currently active downloads\n  final Queue<Map> _downloadQueue = Queue<Map>(); // Queue for pending downloads\n\n  Map get downloads => _box.toMap();\n\n  Listenable songListenable(String songId) {\n    return _box.listenable(keys: [songId]);\n  }\n\n  Map? getDownload(String songId) {\n    return _box.get(songId);\n  }\n\n  Map getCleanSong(Map song) {\n    final Map clean = Map.from(song);\n    clean.remove('status');\n    clean.remove('path');\n    clean.remove('playlists');\n    return clean;\n  }\n\n  List? getDownloadedSongs(String? playlistId) {\n    List? allSongs;\n    if (playlistId == null) {\n      allSongs = downloadsNotifier.value;\n    } else {\n      allSongs = playlistsNotifier.value[playlistId]?[\"songs\"];\n    }\n    return allSongs\n        ?.where((s) => getDownload(s['videoId'])?['status'] == 'DOWNLOADED')\n        .map((s) => getCleanSong(s))\n        .toList();\n  }\n\n  DownloadManager._(this._box) {\n    _cleanAndMigrateData();\n    _refreshData();\n    _box.listenable().addListener(() {\n      _refreshData();\n    });\n  }\n\n  Future<void> reInit() async {\n    await _cleanAndMigrateData();\n    await _refreshData();\n  }\n\n  static Future<DownloadManager> create() async {\n    final boxName = 'DOWNLOADS';\n    await Hive.openBox(boxName);\n    final instance = DownloadManager._(Hive.box(boxName));\n    return instance;\n  }\n\n  Future<void> _cleanAndMigrateData() async {\n    final activeIds = _activeDownloads.toSet();\n    final queuedIds = _downloadQueue.map((e) => e['videoId']).toSet();\n    final mapToUpdate = <String, Map>{};\n\n    for (final key in _box.keys) {\n      final Map song = Map.from(_box.get(key) as Map);\n      final id = song['videoId'] ?? key.toString();\n      String status = song['status'] ?? '';\n\n      // 1) CHECK INTERRUPTED DOWNLOADS\n      final isInvalidDownloading =\n          status == 'DOWNLOADING' && !activeIds.contains(id);\n      final isInvalidQueued = status == 'QUEUED' && !queuedIds.contains(id);\n      if (isInvalidDownloading || isInvalidQueued) {\n        debugPrint(\"Cleaning up interrupted download: ${song['title']}\");\n        song['status'] = 'DELETED';\n        mapToUpdate[key.toString()] = song;\n      }\n\n      // 2) MIGRATE OLD DOWNLOADS TO SONGS PLAYLIST\n      if (song[\"playlists\"] == null || song[\"playlists\"] is! Map) {\n        song[\"playlists\"] = {\n          songsPlaylistId: {\n            \"id\": songsPlaylistId,\n            \"title\": \"Songs\",\n            \"timestamp\":\n                song[\"downloadedAt\"] ??\n                song[\"timestamp\"] ??\n                DateTime.now().millisecondsSinceEpoch,\n          },\n        };\n        mapToUpdate[key.toString()] = song;\n      } else if (song[\"playlists\"] is Map &&\n          (song[\"playlists\"] as Map).keys.contains(\"songs\")) {\n        // 2) RENAME OLD SONGS PLAYLIST\n        final pl = song[\"playlists\"].remove(\"songs\");\n        song[\"playlists\"][songsPlaylistId] = pl;\n        mapToUpdate[key.toString()] = song;\n      }\n    }\n    // 1) UPDATE DOWNLOADS\n    if (mapToUpdate.isNotEmpty) {\n      await _box.putAll(mapToUpdate);\n    }\n  }\n\n  Future<void> _refreshData() async {\n    // 1) LOAD DOWNLOADS FROM HIVE\n    downloadsNotifier.value = _box.values.toList().cast<Map>();\n\n    // 2) BUILD PLAYLIST MAP\n    final Map<String, Map<String, dynamic>> playlists = {};\n\n    for (final song in downloadsNotifier.value) {\n      final Map songPlaylists = Map.from(song[\"playlists\"] ?? {});\n\n      for (final entry in songPlaylists.entries) {\n        final String id = entry.key;\n        final value = entry.value;\n\n        if (value is! Map) continue;\n\n        final String title = value[\"title\"] ?? \"Unknown\";\n\n        playlists\n            .putIfAbsent(\n              id,\n              () => {\n                \"id\": id,\n                \"title\": title,\n                \"type\":\n                    id == songsPlaylistId || id == FavouritesManager.playlistId\n                    ? \"PLAYLIST\"\n                    : \"ALBUM\",\n                \"songs\": <Map<String, dynamic>>[],\n              },\n            )[\"songs\"]\n            .add(Map<String, dynamic>.from(song));\n\n        // ALBUM → PLAYLIST upgrade logic (unchanged, but safe)\n        if (playlists[id]![\"type\"] == \"ALBUM\" &&\n            playlists[id]![\"title\"] != song[\"album\"]?[\"name\"]) {\n          playlists[id]![\"type\"] = \"PLAYLIST\";\n        }\n      }\n    }\n\n    // 3) SORT SONGS INSIDE PLAYLISTS\n    for (final playlist in playlists.values) {\n      final String playlistId = playlist[\"id\"];\n\n      (playlist[\"songs\"] as List).sort((a, b) {\n        final aTs = a[\"playlists\"]?[playlistId]?[\"timestamp\"] ?? 0;\n        final bTs = b[\"playlists\"]?[playlistId]?[\"timestamp\"] ?? 0;\n        return aTs.compareTo(bTs);\n      });\n    }\n\n    // 4) UPDATE STATE IF CHANGED\n    if (!const DeepCollectionEquality().equals(\n      playlistsNotifier.value,\n      playlists,\n    )) {\n      playlistsNotifier.value = playlists;\n    }\n  }\n\n  List<Map> getDownloadQueue() {\n    return _downloadQueue.toList();\n  }\n\n  ValueNotifier<double>? getProgressNotifier(String videoId) {\n    return _activeDownloadProgress[videoId];\n  }\n\n  void _startTrackingProgress(String videoId) {\n    _activeDownloadProgress[videoId]?.dispose();\n    _activeDownloadProgress[videoId] = ValueNotifier(0.0);\n  }\n\n  void _updateTrackingProgress(String videoId, double value) {\n    _activeDownloadProgress[videoId]?.value = value;\n  }\n\n  void _stopTrackingProgress(String videoId) {\n    if (_activeDownloadProgress.containsKey(videoId)) {\n      _activeDownloadProgress[videoId]!.dispose();\n      _activeDownloadProgress.remove(videoId);\n    }\n  }\n\n  Future<void> restoreDownloads({List? songs}) async {\n    final songsToRestore = songs ?? downloadsNotifier.value;\n    for (var song in songsToRestore) {\n      final storedSong = _box.get(song['videoId']);\n      if (storedSong != null) {\n        final status = storedSong['status'];\n        final path = storedSong['path'];\n        final isFileMissing =\n            status == 'DOWNLOADED' &&\n            (path == null || !(await File(path).exists()));\n        final isDeleted = status == 'DELETED';\n        if (isDeleted || isFileMissing) {\n          downloadSong(storedSong);\n        }\n      }\n    }\n  }\n\n  Future<void> setDownloads(Map downloads) async {\n    await Future.forEach(downloads.entries, (entry) async {\n      _box.put(entry.key, entry.value);\n    });\n  }\n\n  Future<void> downloadSong(Map songToDownaload) async {\n    // Added \"songs\" playlist if needed\n    final Map song = {\n      ...songToDownaload,\n      'playlists':\n          songToDownaload['playlists'] ??\n          {\n            songsPlaylistId: {\n              'title': 'Songs',\n              'timestamp': DateTime.now().millisecondsSinceEpoch,\n            },\n          },\n    };\n    // Check downloaded songs\n    final Map? downloadSong = _box.get(song['videoId']);\n    if (downloadSong != null) {\n      final queueSong = _downloadQueue.firstWhereOrNull(\n        (item) => item['videoId'] == song['videoId'],\n      );\n      if (_activeDownloads.contains(song['videoId']) || queueSong != null) {\n        // Already downloading, just update metadata\n        await _updateSongMetadata(song['videoId'], {...song});\n        _downloadNext();\n        return;\n      } else {\n        final String? path = downloadSong['path'];\n        if (path != null) {\n          final file = File(path);\n          final exists = await file.exists();\n          if (exists) {\n            // Already downloaded, just update metadata\n            await _updateSongMetadata(song['videoId'], {\n              ...song,\n              'status': 'DOWNLOADED',\n            });\n            _downloadNext();\n            return;\n          }\n        }\n      }\n    }\n    // Execute download process\n    if (!await _downloadStart(song)) return;\n    await _downloadSong(song);\n    _downloadEnd(song);\n    _downloadNext();\n  }\n\n  Future<void> _downloadSong(Map song) async {\n    try {\n      await _updateSongMetadata(song['videoId'], {\n        ...song,\n        'status': 'DOWNLOADING',\n      });\n      _startTrackingProgress(song['videoId']);\n\n      if (!(await GetIt.I<FileStorage>().requestPermissions())) {\n        throw Exception('Storage permissions not granted.');\n      }\n\n      AudioOnlyStreamInfo audioSource = await _getSongInfo(\n        song['videoId'],\n        quality: GetIt.I<SettingsManager>().downloadQuality.name.toLowerCase(),\n      );\n\n      _ensureActive(song);\n\n      int total = audioSource.size.totalBytes;\n      BytesBuilder received = BytesBuilder();\n\n      Stream<List<int>> stream = AudioStreamClient().getAudioStream(\n        audioSource,\n        start: 0,\n        end: total,\n      );\n\n      _ensureActive(song);\n\n      await for (var data in stream) {\n        _ensureActive(song);\n\n        received.add(data);\n        _updateTrackingProgress(song['videoId'], received.length / total);\n      }\n\n      File? file = await GetIt.I<FileStorage>().saveMusic(\n        received.takeBytes(),\n        song,\n      );\n\n      _ensureActive(song);\n\n      if (file != null) {\n        await _updateSongMetadata(song['videoId'], {\n          'status': 'DOWNLOADED',\n          'path': file.path,\n        });\n      } else {\n        throw Exception(\"File saving failed\");\n      }\n    } on DownloadCanceledException {\n      debugPrint(\"Download cancelled by user: ${song['videoId']}\");\n    } catch (e) {\n      debugPrint(\"Error in _downloadSong: $e\");\n      await _updateSongMetadata(song['videoId'], {'status': 'DELETED'});\n    } finally {\n      _stopTrackingProgress(song['videoId']);\n    }\n  }\n\n  Future<void> _updateSongMetadata(String key, Map newMetadata) async {\n    Map? song = _box.get(key);\n    if (song != null) {\n      if (newMetadata.containsKey('playlists')) {\n        Map<String, dynamic> mergedPlaylists = {};\n        if (song['playlists'] != null) {\n          (song['playlists'] as Map).forEach((k, v) {\n            mergedPlaylists[k] = Map<String, dynamic>.from(v);\n          });\n        }\n        (newMetadata['playlists'] as Map).forEach((k, v) {\n          mergedPlaylists[k] = Map<String, dynamic>.from(v);\n        });\n        song['playlists'] = mergedPlaylists;\n        newMetadata.remove('playlists');\n      }\n      await _box.put(key, {...song, ...newMetadata});\n    } else {\n      await _box.put(key, newMetadata);\n    }\n  }\n\n  Future<bool> _downloadStart(Map song) async {\n    if (_activeDownloads.length >= maxConcurrentDownloads) {\n      _downloadQueue.add(song);\n      await _updateSongMetadata(song['videoId'], {...song, 'status': 'QUEUED'});\n      return false;\n    }\n    _activeDownloads.add(song['videoId']);\n    return true;\n  }\n\n  void _ensureActive(Map song) {\n    if (!_activeDownloads.contains(song['videoId'])) {\n      throw DownloadCanceledException();\n    }\n  }\n\n  void _downloadEnd(Map song) {\n    if (_activeDownloads.isNotEmpty) {\n      _activeDownloads.remove(song['videoId']);\n    }\n  }\n\n  void _downloadNext() {\n    if (_downloadQueue.isNotEmpty &&\n        _activeDownloads.length < maxConcurrentDownloads) {\n      downloadSong(_downloadQueue.removeFirst());\n    }\n  }\n\n  Future<void> _deleteSongInstance(Map song) async {\n    // Remove Song from Queue\n    if (song['status'] == \"QUEUED\") {\n      _downloadQueue.removeWhere((item) => item['videoId'] == song['videoId']);\n    }\n    // Stop in-progress download\n    else if (song['status'] == \"DOWNLOADING\") {\n      _downloadEnd(song);\n    }\n    // Delete Song from box\n    await _box.delete(song['videoId']);\n    // Remove file if exists\n    if (song['path'] != null && await File(song['path']).exists()) {\n      await File(song['path']).delete();\n    }\n  }\n\n  Future<String> deleteSong({\n    required String key,\n    String playlistId = songsPlaylistId,\n  }) async {\n    Map? song = _box.get(key);\n    final Map playlists = song?['playlists'];\n    if (song != null && (playlists.keys.contains(playlistId))) {\n      song['playlists'].remove(playlistId);\n      if (song['playlists'].isEmpty) {\n        await _deleteSongInstance(song);\n      } else {\n        if (song['status'] == \"QUEUED\") {\n          for (var item in _downloadQueue) {\n            if (item['videoId'] == song['videoId']) {\n              item['playlists'] = playlists;\n              break;\n            }\n          }\n        }\n        await _box.put(key, song);\n      }\n    }\n    return 'Song deleted successfully.';\n  }\n\n  Future<void> deleteAllSongs() async {\n    List<Map> songs = _box.values.toList().cast<Map>();\n    for (Map song in songs) {\n      await _deleteSongInstance(song);\n    }\n  }\n\n  Future<void> updateStatus(String key, String status) async {\n    Map? song = _box.get(key);\n    if (song != null) {\n      song['status'] = status;\n      await _box.put(key, song);\n    }\n  }\n\n  Future<List> _getSongs({\n    String? playlistId,\n    int maxContinuations = 50, // playlist and albums with up to 24 * 51 songs\n  }) async {\n    final songs = [];\n    if (playlistId != null) {\n      Map result = await GetIt.I<YTMusic>().getNextSongList(\n        playlistId: playlistId,\n      );\n      songs.addAll(result['contents']);\n      String? continuation = result['continuation'];\n      while (maxContinuations > 0 && continuation != null) {\n        result = await GetIt.I<YTMusic>().getNextSongList(\n          continuation: continuation,\n        );\n        songs.addAll(result['contents']);\n        continuation = result['continuation'];\n        maxContinuations -= 1;\n      }\n    }\n    return songs;\n  }\n\n  Future<void> downloadPlaylist(Map playlist) async {\n    List songs = playlist['isPredefined'] == false\n        ? playlist['songs']\n        : await _getSongs(\n            playlistId: playlist['playlistId'],\n            maxContinuations: playlist['type'] == 'ARTIST' ? 0 : 50,\n          );\n    int timestamp = DateTime.now().millisecondsSinceEpoch;\n    for (Map song in songs) {\n      downloadSong({\n        ...song,\n        'playlists': {\n          playlist['playlistId']: {\n            'title': playlist['title'],\n            'timestamp': timestamp++,\n          },\n        },\n      }); // Queue each song download\n    }\n  }\n\n  Future<AudioOnlyStreamInfo> _getSongInfo(\n    String videoId, {\n    String quality = 'high',\n  }) async {\n    try {\n      StreamManifest manifest = await ytExplode.videos.streamsClient\n          .getManifest(\n            videoId,\n            requireWatchPage: true,\n            ytClients: [YoutubeApiClient.androidVr],\n          );\n      List<AudioOnlyStreamInfo> streamInfos = manifest.audioOnly\n          .where((a) => a.container == StreamContainer.mp4)\n          .sortByBitrate()\n          .reversed\n          .toList();\n      return quality == 'low' ? streamInfos.first : streamInfos.last;\n    } catch (e) {\n      rethrow;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/services/favourites_manager.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:hive_flutter/hive_flutter.dart';\n\nclass FavouritesManager {\n  final Box _box;\n  static const playlistId = 'FVRTS';\n\n  Listenable get listenable => _box.listenable();\n\n  Map get songs => _box.toMap();\n  int get songsCount => _box.length;\n\n  Map<String, dynamic> get playlist => {\n    'title': \"Favourites\",\n    'playlistId': playlistId,\n    'type': 'PLAYLIST',\n    'isPredefined': false,\n    'songs': getOrderedSongs(),\n  };\n\n  FavouritesManager._(this._box);\n\n  static Future<FavouritesManager> create() async {\n    final boxName = 'FAVOURITES';\n    await Hive.openBox(boxName);\n    final instance = FavouritesManager._(Hive.box(boxName));\n    return instance;\n  }\n\n  List getOrderedSongs() {\n    final list = _box.values.toList();\n    list.sort((a, b) => (a['createdAt'] ?? 0).compareTo(b['createdAt'] ?? 0));\n    return list;\n  }\n\n  bool isFavourite(Map? song) {\n    if (song == null || song['videoId'] == null) return false;\n    return _box.containsKey(song['videoId']);\n  }\n\n  Future<void> add(Map? song) async {\n    if (song != null) {\n      await _box.put(song['videoId'], {\n        ...song,\n        'createdAt': song['createdAt'] ?? DateTime.now().millisecondsSinceEpoch,\n      });\n    }\n  }\n\n  Future<void> remove(Map? song) async {\n    if (song != null) {\n      await _box.delete(song['videoId']);\n    }\n  }\n\n  Future<void> addOrRemove(Map? song) async {\n    if (song != null) {\n      if (isFavourite(song)) {\n        await remove(song);\n      } else {\n        await add(song);\n      }\n    }\n  }\n\n  Future<void> setFavourites(Map favourites) async {\n    await Future.forEach(favourites.values, (song) async {\n      await add(song);\n    });\n  }\n}\n"
  },
  {
    "path": "lib/services/file_storage.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\nimport 'package:audiotags/audiotags.dart';\nimport 'package:file_picker/file_picker.dart';\nimport 'package:flutter/services.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/download_manager.dart';\nimport 'package:http/http.dart';\nimport 'package:intl/intl.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:permission_handler/permission_handler.dart';\nimport 'package:path/path.dart' as path;\nimport 'package:share_plus/share_plus.dart';\n\nimport '../utils/enhanced_image.dart';\nimport 'history_manager.dart';\nimport 'library.dart';\nimport 'settings_manager.dart';\nimport 'favourites_manager.dart';\n\nclass FileStorage {\n  static final String defaultPath = '/storage/emulated/0/Download/';\n  late StoragePaths _storagePaths;\n  FileStorage._();\n\n  static Future<FileStorage> create() async {\n    final instance = FileStorage._();\n    await instance.setupPaths();\n    return instance;\n  }\n\n  Future<void> setupPaths() async {\n    Directory directory = await _getAppDirectory();\n    _storagePaths = StoragePaths(\n      basePath: directory.path,\n      backupPath: path.join(directory.path, 'Back Up'),\n      musicPath: path.join(directory.path, 'Music'),\n    );\n    await _getDirectory(_storagePaths.backupPath);\n    await _getDirectory(_storagePaths.musicPath);\n  }\n\n  Future<String> saveBackUp(Map data) async {\n    String timestamp = DateFormat('yyyyMMdd_HHmmss').format(DateTime.now());\n    String fileName = '${timestamp}_backup';\n    if (!(await requestPermissions())) return \"\";\n    Directory directory = await _getDirectory(_storagePaths.backupPath);\n\n    String filePath = path.join(directory.path, '$fileName.json');\n    File file = File(filePath);\n    try {\n      if (await file.exists()) {\n        await file.delete();\n      }\n      File result = await file.writeAsString(jsonEncode(data), flush: true);\n      return result.path;\n    } catch (e) {\n      rethrow;\n    }\n  }\n\n  Future<String> shareBackUp(Map data) async {\n    String timestamp = DateFormat('yyyyMMdd_HHmmss').format(DateTime.now());\n    String fileName = '${timestamp}_backup.json';\n    try {\n      final params = ShareParams(\n        files: [\n          XFile.fromData(utf8.encode(jsonEncode(data)), mimeType: 'text/plain'),\n        ],\n        fileNameOverrides: [fileName],\n      );\n      ShareResult result = await SharePlus.instance.share(params);\n      return result.raw;\n    } catch (e) {\n      rethrow;\n    }\n  }\n\n  Future<File?> saveMusic(List<int> data, Map song, {extension = 'm4a'}) async {\n    String fileName = song['title'];\n    final RegExp avoid = RegExp(r'[\\.\\\\\\*\\:\\(\\)\\\"\\?#/;\\|]');\n    fileName = fileName.replaceAll(avoid, '').replaceAll(\"'\", '');\n    //fileName = Uri.decodeFull(fileName);\n    if (!(await requestPermissions())) return null;\n    Directory directory = await _getDirectory(_storagePaths.musicPath);\n\n    File file = File(path.join(directory.path, '$fileName.$extension'));\n    int number = 1;\n    while (file.existsSync()) {\n      file = File((path.join(directory.path, '$fileName($number).$extension')));\n      number++;\n    }\n    try {\n      if (await file.exists()) {\n        await file.delete();\n      }\n      await file.writeAsBytes(data, flush: true);\n\n      try {\n        Response res = await get(\n          Uri.parse(getEnhancedImage(song['thumbnails'].first['url'])),\n        );\n        Tag tag = Tag(\n          title: song['title'],\n          trackArtist: song['artists']\n              ?.map((artist) => artist['name'])\n              .join(','),\n          album: song['album']?['name'],\n          pictures: [\n            Picture(bytes: res.bodyBytes, pictureType: PictureType.coverFront),\n          ],\n        );\n        await AudioTags.write(file.path, tag);\n      } catch (e) {\n        await file.writeAsBytes(data, flush: true);\n      }\n      return file;\n    } catch (e) {\n      return null;\n    }\n  }\n\n  Future<bool> loadBackup() async {\n    if (!(await requestPermissions())) return false;\n    FilePickerResult? picker = await FilePicker.platform.pickFiles(\n      initialDirectory: _storagePaths.backupPath,\n      allowMultiple: false,\n      withData: true,\n      type: FileType.custom,\n      allowedExtensions: ['json'],\n    );\n    if (picker == null) return false;\n    final file = picker.files[0].xFile;\n    String data = await file.readAsString();\n    Map backup = jsonDecode(data);\n    if (backup['name'] != 'Gyawun' && backup['type'] != 'backup') {\n      return false;\n    }\n    Map? settings = backup['data']?['settings'];\n    Map? favourites = backup['data']?['favourites'];\n    Map? playlists = backup['data']?['playlists'];\n    Map? history = backup['data']?['song_history'];\n    Map? downloads = backup['data']?['downloads'];\n    if (settings != null) {\n      await GetIt.I<SettingsManager>().setSettings(settings);\n      // await GetIt.I<YTMusic>().refreshHeaders();\n    }\n    if (favourites != null) {\n      await GetIt.I<FavouritesManager>().setFavourites(favourites);\n    }\n    if (playlists != null) {\n      await GetIt.I<LibraryService>().setPlaylists(playlists);\n    }\n    if (history != null) {\n      await GetIt.I<HistoryManager>().songs.setHistory(history);\n    }\n    if (downloads != null) {\n      await GetIt.I<DownloadManager>().setDownloads(downloads);\n    }\n    return true;\n  }\n\n  Future<Directory> _getAppDirectory() async {\n    if (Platform.isAndroid) {\n      return Directory(\n        path.join(GetIt.I<SettingsManager>().appFolder, 'Gyawun'),\n      );\n    }\n    if (Platform.isWindows) {\n      return Directory(\n        path.join((await getDownloadsDirectory())!.path, 'Gyawun'),\n      );\n    }\n    return await getApplicationDocumentsDirectory();\n  }\n\n  Future<Directory> _getDirectory(String pathString) async {\n    Directory dir = Directory(pathString);\n    if (!(await dir.exists())) {\n      await dir.create(recursive: true);\n    }\n    return dir;\n  }\n\n  Future<bool> requestPermissions() async {\n    if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {\n      // Desktop platforms do not need permission\n      return true;\n    }\n\n    if (Platform.isIOS) {\n      // iOS app-specific storage doesn't require permission\n      return true;\n    }\n\n    bool isGranted = false;\n\n    if (Platform.isAndroid) {\n      int sdkInt = (await _getAndroidSdkInt()) ?? 30;\n\n      if (sdkInt >= 30) {\n        // Android 11+ requires MANAGE_EXTERNAL_STORAGE for arbitrary access\n        isGranted = await Permission.manageExternalStorage.isGranted;\n        if (!isGranted) {\n          await Permission.manageExternalStorage.request();\n          isGranted = await Permission.manageExternalStorage.isGranted;\n        }\n      } else {\n        // Android < 11 uses standard storage permission\n        isGranted = await Permission.storage.isGranted;\n        if (!isGranted) {\n          await Permission.storage.request();\n          isGranted = await Permission.storage.isGranted;\n        }\n      }\n\n      if (!isGranted) {\n        // Open settings if permission denied\n        await openAppSettings();\n      }\n    }\n\n    return isGranted;\n  }\n\n  // Helper function to get Android SDK version\n  Future<int?> _getAndroidSdkInt() async {\n    try {\n      final String? sdkString = await MethodChannel(\n        'flutter/platform',\n      ).invokeMethod<String>('SystemNavigator.getPlatformVersion');\n      if (sdkString != null) {\n        final match = RegExp(r'Android (\\d+)').firstMatch(sdkString);\n        if (match != null) return int.tryParse(match.group(1)!);\n      }\n    } catch (_) {}\n    return null;\n  }\n}\n\nclass StoragePaths {\n  String basePath;\n  String backupPath;\n  String musicPath;\n  StoragePaths({\n    required this.basePath,\n    required this.backupPath,\n    required this.musicPath,\n  });\n}\n"
  },
  {
    "path": "lib/services/history_manager.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:hive_flutter/hive_flutter.dart';\nimport 'package:gyawun/services/settings_manager.dart';\n\nclass HistoryManager {\n  final SearchHistory searches;\n  final SongHistory songs;\n\n  HistoryManager._(this.searches, this.songs);\n\n  static Future<HistoryManager> create() async {\n    final searches = await SearchHistory.create();\n    final songs = await SongHistory.create();\n    final instance = HistoryManager._(searches, songs);\n    return instance;\n  }\n}\n\nclass SearchHistory {\n  final Box _box;\n\n  SearchHistory._(this._box);\n\n  static Future<SearchHistory> create() async {\n    final boxName = 'SEARCH_HISTORY';\n    await Hive.openBox(boxName);\n    final instance = SearchHistory._(Hive.box(boxName));\n    return instance;\n  }\n\n  Map get all => _box.toMap();\n\n  List<Map<String, dynamic>> getList({String? filter}) {\n    final searchHistory = filter == null\n        ? _box.values\n        : _box.values.where(\n            (el) => el.toLowerCase().contains(filter.toLowerCase()),\n          );\n    return searchHistory\n        .toList()\n        .map((el) => {'type': 'TEXT', 'query': el, 'isHistory': true})\n        .toList();\n  }\n\n  Future<void> add(String value) async {\n    if (GetIt.I<SettingsManager>().searchHistory) {\n      await _box.delete(value.toLowerCase());\n      await _box.put(value.toLowerCase(), value);\n    }\n  }\n\n  Future<void> clear() async {\n    await _box.clear();\n  }\n}\n\nclass SongHistory {\n  final Box _box;\n\n  SongHistory._(this._box);\n\n  static Future<SongHistory> create() async {\n    final boxName = 'SONG_HISTORY';\n    await Hive.openBox(boxName);\n    final instance = SongHistory._(Hive.box(boxName));\n    return instance;\n  }\n\n  Listenable get listenable => _box.listenable();\n  int get count => _box.length;\n  Map get all => _box.toMap();\n\n  List getList() {\n    final list = _box.values.toList();\n    list.sort((a, b) => (b['updatedAt'] ?? 0).compareTo(a['updatedAt'] ?? 0));\n    return list;\n  }\n\n  Future<void> add(Map song) async {\n    if (GetIt.I<SettingsManager>().playbackHistory) {\n      Map? oldState = _box.get(song['videoId']);\n      int timestamp = DateTime.now().millisecondsSinceEpoch;\n      if (oldState != null) {\n        await _box.put(song['videoId'], {\n          ...oldState,\n          'plays': oldState['plays'] + 1,\n          'updatedAt': timestamp,\n        });\n      } else {\n        await _box.put(song['videoId'], {\n          ...song,\n          'plays': 1,\n          'createdAt': timestamp,\n          'updatedAt': timestamp,\n        });\n      }\n    }\n  }\n\n  Future<void> remove(Map song) async {\n    await _box.delete(song['videoId']);\n  }\n\n  Future<void> clear() async {\n    await _box.clear();\n  }\n\n  Future<void> setHistory(Map history) async {\n    await Future.forEach(history.entries, (entry) async {\n      _box.put(entry.key, entry.value);\n    });\n  }\n}\n"
  },
  {
    "path": "lib/services/library.dart",
    "content": "import 'package:flutter/cupertino.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:hive_flutter/hive_flutter.dart';\nimport 'package:yt_music/ytmusic.dart';\n\nclass LibraryService extends ChangeNotifier {\n  final Box _box;\n  late Map _playlists;\n\n  LibraryService._(this._box) {\n    _init();\n  }\n\n  static Future<LibraryService> create() async {\n    final boxName = 'LIBRARY';\n    await Hive.openBox(boxName);\n    final instance = LibraryService._(Hive.box(boxName));\n    await instance._migrateLibIcons();\n    return instance;\n  }\n\n  void _init() {\n    _playlists = _box.toMap();\n    _box.listenable().addListener(() {\n      _playlists = _box.toMap();\n      notifyListeners();\n    });\n  }\n\n  Map get playlists => _playlists;\n  Map get userPlaylists => Map.fromEntries(\n    _playlists.entries.where((item) => item.value['isPredefined'] == false),\n  );\n  Map? getPlaylist(String playlistId) => _box.get(playlistId);\n\n  Future<void> reInit() async {\n    await _migrateLibIcons();\n    _playlists = _box.toMap();\n    notifyListeners();\n  }\n\n  Future<void> _migrateLibIcons() async {\n    for (final entry in userPlaylists.entries) {\n      final id = entry.key;\n      final item = entry.value;\n      if (!item.keys.contains(\"iconId\")) {\n        await _box.put(id, {...item, 'iconId': 'musicNoteList'});\n      }\n    }\n  }\n\n  Future<String> createPlaylist(\n    String title,\n    String iconId, {\n    Map? item,\n  }) async {\n    if (title.trim().isEmpty) {\n      return \"Playlist name can't be empty\";\n    }\n    await _box.put(\"CSTMPL${DateTime.now().millisecondsSinceEpoch}\", {\n      'title': title,\n      'iconId': iconId,\n      'isPredefined': false,\n      'songs': item != null ? [item] : [],\n      'createdAt': DateTime.now().millisecondsSinceEpoch,\n    });\n    notifyListeners();\n    if (item != null) {\n      return '${item['title']} added to $title';\n    } else {\n      return 'Playlist $title created';\n    }\n  }\n\n  Future<String> importPlaylist(String playlistUrl) async {\n    try {\n      Uri uri = Uri.parse(playlistUrl);\n      String? playlistId = uri.queryParameters['list'];\n      if (!uri.host.contains('youtube.com') || playlistId == null) {\n        return 'Invalid Url';\n      }\n      String browseId = playlistId.startsWith(\"VL\")\n          ? playlistId\n          : \"VL$playlistId\";\n      Map<String, dynamic> playlist = await GetIt.I<YTMusic>().importPlaylist(\n        browseId,\n      );\n      String id = playlistId;\n      if (_playlists[id] != null) {\n        await _box.delete(id);\n        notifyListeners();\n        return 'Playlist is already added';\n      } else {\n        await _box.put(id, {\n          ...playlist,\n          'iconId': 'musicNoteList',\n          'isPredefined': true,\n          'createdAt': DateTime.now().millisecondsSinceEpoch,\n        });\n        notifyListeners();\n        return 'Added to Library';\n      }\n    } catch (e) {\n      return \"Error importing playlist\";\n    }\n  }\n\n  Future<String> addToOrRemoveFromLibrary(Map item) async {\n    String id = item['playlistId'];\n    if (_playlists[id] != null) {\n      await _box.delete(id);\n      notifyListeners();\n      return 'Removed from Library';\n    } else {\n      await _box.put(id, {\n        ...item,\n        'isPredefined': true,\n        'createdAt': DateTime.now().millisecondsSinceEpoch,\n      });\n      notifyListeners();\n      return 'Added to Library';\n    }\n  }\n\n  Future<String> editPlaylist({\n    String? title,\n    required String playlistId,\n    required String iconId,\n  }) async {\n    if (_playlists[playlistId] == null) {\n      return 'Playlist does not exist';\n    }\n    if (title == null || title.trim().isEmpty) {\n      return \"Playlist name can't be empty\";\n    }\n    Map playlist = await _box.get(playlistId);\n    playlist['title'] = title;\n    playlist['iconId'] = iconId;\n    await _box.put(playlistId, playlist);\n    notifyListeners();\n    return 'Playlist updated';\n  }\n\n  Future<String> removeFromLibrary(String key) async {\n    if (_playlists.containsKey(key)) {\n      await _box.delete(key);\n      notifyListeners();\n      return 'Removed from Library';\n    } else {\n      return 'Does not exist in Library';\n    }\n  }\n\n  Future<String> addToPlaylist({required Map item, required String key}) async {\n    Map? playlist = await _box.get(key);\n    if (playlist == null) return 'Playlist does not exist';\n    List songs = playlist['songs'] ?? [];\n    if (songs.contains(item)) {\n      return 'Already present in Playlist';\n    }\n    songs.add(item);\n    playlist['songs'] = songs;\n    await _box.put(key, playlist);\n    notifyListeners();\n    return 'Added to Playlist';\n  }\n\n  Future<String> removeFromPlaylist({\n    required Map item,\n    required String playlistId,\n  }) async {\n    Map? playlist = await _box.get(playlistId);\n    if (playlist == null) return 'Playlist does not exist';\n    List songs = playlist['songs'] ?? [];\n    if (songs.remove(item)) {\n      playlist['songs'] = songs;\n      await _box.put(playlistId, playlist);\n      notifyListeners();\n      return 'Removed from Playlist';\n    } else {\n      return 'An error occured';\n    }\n  }\n\n  Future setPlaylists(Map value) async {\n    await Future.forEach(value.entries, (entry) async {\n      await _box.put(entry.key, entry.value);\n    });\n    notifyListeners();\n  }\n}\n"
  },
  {
    "path": "lib/services/lyrics.dart",
    "content": "import 'dart:convert';\nimport 'package:flutter/widgets.dart';\nimport 'package:http/http.dart';\nimport 'package:translator/translator.dart';\nimport 'package:language_detector/language_detector.dart';\n\nclass Lyrics {\n  Map lyricsList = {};\n  Future<Map<String, dynamic>> getLyrics({\n    required String videoId,\n    required String title,\n    required int durationInSeconds,\n    String? artist,\n    String? album,\n    String? translation,\n  }) async {\n    if (lyricsList[videoId] != null) {\n      return lyricsList[videoId];\n    }\n    Map response = await fetchLyrics(\n      videoId: videoId,\n      title: title,\n      durationInSeconds: durationInSeconds,\n      album: album,\n      artist: artist,\n    );\n    if (response[\"syncedLyrics\"] != null || response['plainLyrics'] != null) {\n      final lyricsData = {\n        \"syncedLyrics\": response[\"syncedLyrics\"].toString(),\n        \"plainLyrics\": response[\"plainLyrics\"],\n        \"transLyrics\": \"\",\n      };\n      if (translation != null) {\n        String lyricsLang = await LanguageDetector.getLanguageCode(\n            content: response[\"plainLyrics\"]);\n        if (lyricsLang != translation) {\n          if (response[\"syncedLyrics\"] != null) {\n            lyricsData[\"transLyrics\"] = await translateSyncLyrics(\n              response[\"syncedLyrics\"].toString(),\n              lyricsLang,\n              translation,\n            );\n          } else {\n            lyricsData[\"plainLyrics\"] = await translatePlainLyrics(\n              response['plainLyrics'],\n              lyricsLang,\n              translation,\n            );\n          }\n        }\n      }\n      lyricsList[videoId] = lyricsData;\n      return lyricsData;\n    }\n    return {'success': false};\n  }\n\n  void fixLrcFormat(Map lrc) {\n    if (lrc.containsKey('syncedLyrics') && lrc['syncedLyrics'] != null) {\n      lrc['syncedLyrics'] = (lrc['syncedLyrics'] as String)\n          .replaceAllMapped(RegExp(r'\\[(\\d{2}):(\\d{2}):(\\d{2,3})\\]'), (match) {\n        return '[${match.group(1)}:${match.group(2)}.${match.group(3)}]';\n      });\n    }\n  }\n\n  Future<Map> fetchLyrics({\n    required String videoId,\n    required String title,\n    required int durationInSeconds,\n    String? artist,\n    String? album,\n  }) async {\n    try {\n      Uri uri;\n      bool isSpecificSearch = false;\n      if (artist != null && album != null) {\n        uri = Uri.https('lrclib.net', '/api/get', {\n          'artist_name': artist,\n          'track_name': title,\n          'album_name': album,\n          'duration': durationInSeconds.toString(),\n        });\n        isSpecificSearch = true;\n      } else {\n        final params = {'track_name': title};\n        if (artist != null) params['artist_name'] = artist;\n        if (album != null) params['album_name'] = album;\n        uri = Uri.https('lrclib.net', '/api/search', params);\n      }\n      final response = await get(uri).timeout(const Duration(seconds: 20));\n      if (response.statusCode != 200) {\n        debugPrint(\"Error in lrclib get : ${response.statusCode}\");\n        return {};\n      }\n      final decoded = jsonDecode(utf8.decode(response.bodyBytes));\n      Map lyric = {};\n      if (isSpecificSearch) {\n        if (decoded is Map) lyric = decoded;\n      } else {\n        if (decoded is List && decoded.isNotEmpty) {\n          decoded.sort((a, b) {\n            final durA = (a['duration'] as num).toDouble();\n            final durB = (b['duration'] as num).toDouble();\n            final target = durationInSeconds.toDouble();\n            return (durA - target).abs().compareTo((durB - target).abs());\n          });\n          lyric = decoded.first;\n        }\n      }\n      fixLrcFormat(lyric);\n      return lyric;\n    } catch (e) {\n      debugPrint(\"Error in fetchLyrics: $e\");\n      return {};\n    }\n  }\n\n  Future<String?> translateSyncLyrics(\n      String lyric, String from, String to) async {\n    try {\n      Translation trans = await lyric.translate(from: from, to: to);\n      final transLines = trans.text.split(\"\\n\");\n      final lyricLines = lyric.split(\"\\n\");\n      if (lyricLines.length != transLines.length) {\n        throw Exception(\"Translation lines do not match original lines\");\n      }\n      String transLyric = \"\";\n      for (int i = 0; i < transLines.length; i++) {\n        transLyric +=\n            \"${lyricLines[i].split(\"]\")[0]}]${transLines[i].split(\"]\")[1]}\\n\";\n      }\n      return transLyric;\n    } catch (e) {\n      return \"\";\n    }\n  }\n\n  Future<String?> translatePlainLyrics(\n      String lyric, String from, String to) async {\n    try {\n      Translation trans = await lyric.translate(from: from, to: to);\n      final lines = lyric.split(\"\\n\");\n      final transLines = trans.text.split(\"\\n\");\n      if (lines.length != transLines.length) {\n        throw Exception(\"Translation lines do not match original lines\");\n      }\n      String transLyric = '';\n      for (int i = 0; i < lines.length; i++) {\n        transLyric += \"${lines[i]}\\n[${transLines[i]}]\\n\\n\";\n      }\n      return transLyric;\n    } catch (e) {\n      return \"\";\n    }\n  }\n}\n"
  },
  {
    "path": "lib/services/media_player.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/services/download_manager.dart';\nimport 'package:gyawun/services/yt_audio_stream.dart';\nimport 'package:just_audio/just_audio.dart';\nimport 'package:just_audio_background/just_audio_background.dart';\nimport 'package:rxdart/rxdart.dart';\nimport 'package:yt_music/ytmusic.dart';\n\nimport '../utils/add_history.dart';\nimport 'settings_manager.dart';\n\nclass MediaPlayer extends ChangeNotifier {\n  late final AudioPlayer _player;\n\n  final _loudnessEnhancer = AndroidLoudnessEnhancer();\n  AndroidEqualizer? _equalizer;\n  AndroidEqualizerParameters? _equalizerParams;\n\n  List<IndexedAudioSource> _songList = [];\n  final ValueNotifier<MediaItem?> _currentSongNotifier = ValueNotifier(null);\n  final ValueNotifier<int?> _currentIndex = ValueNotifier(null);\n  final ValueNotifier<ButtonState> _buttonState = ValueNotifier(\n    ButtonState.loading,\n  );\n  Timer? _timer;\n  final ValueNotifier<Duration?> _timerDuration = ValueNotifier(null);\n\n  final ValueNotifier<LoopMode> _loopMode = ValueNotifier(LoopMode.off);\n\n  final ValueNotifier<ProgressBarState> _progressBarState = ValueNotifier(\n    ProgressBarState(),\n  );\n\n  bool _shuffleModeEnabled = false;\n\n  Object? _activeSession;\n\n  MediaPlayer() {\n    if (Platform.isAndroid) {\n      _equalizer = AndroidEqualizer();\n    }\n    final AudioPipeline pipeline = AudioPipeline(\n      androidAudioEffects: [\n        if (Platform.isAndroid && _equalizer != null) _equalizer!,\n        _loudnessEnhancer,\n      ],\n    );\n    _player = AudioPlayer(audioPipeline: pipeline);\n\n    GetIt.I.registerSingleton<AndroidLoudnessEnhancer>(_loudnessEnhancer);\n    if (Platform.isAndroid && _equalizer != null) {\n      GetIt.I.registerSingleton<AndroidEqualizer>(_equalizer!);\n    }\n\n    _init();\n  }\n\n  AudioPlayer get player => _player;\n  List<IndexedAudioSource> get songList => List.unmodifiable(_songList);\n  ValueNotifier<MediaItem?> get currentSongNotifier => _currentSongNotifier;\n  ValueNotifier<int?> get currentIndex => _currentIndex;\n  ValueNotifier<ButtonState> get buttonState => _buttonState;\n  ValueNotifier<ProgressBarState> get progressBarState => _progressBarState;\n  bool get shuffleModeEnabled => _shuffleModeEnabled;\n  ValueNotifier<LoopMode> get loopMode => _loopMode;\n  ValueNotifier<Duration?> get timerDuration => _timerDuration;\n  Object _startSession() => _activeSession = Object();\n  bool _isSessionValid(Object? session) => _activeSession == session;\n\n  Stream<\n    ({\n      List<IndexedAudioSource>? sequence,\n      int? currentIndex,\n      MediaItem? currentItem,\n    })\n  >\n  get currentTrackStream =>\n      Rx.combineLatest2<\n        List<IndexedAudioSource>?,\n        int?,\n        ({\n          List<IndexedAudioSource>? sequence,\n          int? currentIndex,\n          MediaItem? currentItem,\n        })\n      >(_player.sequenceStream, _player.currentIndexStream, (\n        sequence,\n        currentIndex,\n      ) {\n        MediaItem? currentItem;\n        if (sequence != null &&\n            currentIndex != null &&\n            currentIndex >= 0 &&\n            currentIndex < sequence.length) {\n          final tag = sequence[currentIndex].tag;\n          if (tag is MediaItem) currentItem = tag;\n        }\n        return (\n          sequence: sequence,\n          currentIndex: currentIndex,\n          currentItem: currentItem,\n        );\n      });\n\n  Future<void> _init() async {\n    await _loadLoudnessEnhancer();\n    await _loadEqualizer();\n\n    // Start with an empty queue\n    await _player.setAudioSources([]);\n\n    _listenToChangesInPlaylist();\n    _listenToPlaybackState();\n    _listenToCurrentPosition();\n    _listenToBufferedPosition();\n    _listenToTotalDuration();\n    _listenToChangesInSong();\n    _listenToShuffle();\n    _listenToAutofetch();\n\n    Timer.periodic(const Duration(seconds: 10), (timer) {\n      if (currentSongNotifier.value != null && _player.playing) {\n        GetIt.I<YTMusic>().addPlayingStats(\n          currentSongNotifier.value!.id,\n          _player.position,\n        );\n      }\n    });\n  }\n\n  Future<void> _loadLoudnessEnhancer() async {\n    await _loudnessEnhancer.setEnabled(\n      GetIt.I<SettingsManager>().loudnessEnabled,\n    );\n\n    await _loudnessEnhancer.setTargetGain(\n      GetIt.I<SettingsManager>().loudnessTargetGain,\n    );\n  }\n\n  Future<Map> getEqualizerParameters() async {\n    Map storedParams = GetIt.I<SettingsManager>().equalizerParameters;\n    if (storedParams.isNotEmpty) return storedParams;\n    _equalizerParams = await _equalizer!.parameters;\n    await GetIt.I<SettingsManager>().setEqualizerParameters(\n      _equalizerParams!.toMap(),\n    );\n    return GetIt.I<SettingsManager>().equalizerParameters;\n  }\n\n  Future<void> _loadEqualizer() async {\n    if (!Platform.isAndroid || _equalizer == null) return;\n    await _equalizer!.setEnabled(GetIt.I<SettingsManager>().equalizerEnabled);\n    _equalizer!.parameters.then((value) async {\n      _equalizerParams ??= value;\n      if (GetIt.I<SettingsManager>().equalizerParameters.isEmpty) {\n        GetIt.I<SettingsManager>().setEqualizerParameters(\n          _equalizerParams!.toMap(),\n        );\n      } else {\n        List<double> storedBandsGain =\n            GetIt.I<SettingsManager>().equalizerBandsGain;\n        final List<AndroidEqualizerBand> bands = _equalizerParams!.bands;\n        for (var e in bands) {\n          final gain = storedBandsGain.isNotEmpty\n              ? storedBandsGain[e.index]\n              : 0.0;\n          _equalizerParams!.bands[e.index].setGain(gain);\n        }\n      }\n    });\n  }\n\n  Future<void> setLoudnessEnabled(bool value) async {\n    await _loudnessEnhancer.setEnabled(value);\n    GetIt.I<SettingsManager>().loudnessEnabled = value;\n  }\n\n  Future<void> setEqualizerEnabled(bool value) async {\n    await _equalizer?.setEnabled(value);\n    GetIt.I<SettingsManager>().equalizerEnabled = value;\n  }\n\n  Future<void> setLoudnessTargetGain(double value) async {\n    await _loudnessEnhancer.setTargetGain(value);\n    GetIt.I<SettingsManager>().loudnessTargetGain = value;\n  }\n\n  Future<void> setEqualizerBandGain(int bandIndex, double gain) async {\n    await GetIt.I<SettingsManager>().setEqualizerBandsGain(bandIndex, gain);\n    _equalizerParams = await _equalizer!.parameters;\n    await _equalizerParams!.bands[bandIndex].setGain(gain);\n  }\n\n  void _listenToChangesInPlaylist() {\n    _player.sequenceStream.listen((playlist) {\n      final List<IndexedAudioSource> newList = (playlist)\n          .cast<IndexedAudioSource>();\n\n      if (listEquals(newList, _songList)) return;\n\n      final bool shouldAdd = (_songList.isEmpty && newList.isNotEmpty);\n\n      if (newList.isEmpty) {\n        _currentSongNotifier.value = null;\n        _currentIndex.value = null;\n        _songList = [];\n      } else {\n        _songList = newList;\n\n        final currentIndex = _currentIndex.value ??= 0;\n        _currentSongNotifier.value = (_songList.length > currentIndex)\n            ? _songList[currentIndex].tag\n            : null;\n      }\n\n      if (shouldAdd == true && _currentSongNotifier.value != null) {\n        addHistory(_currentSongNotifier.value!.extras!);\n      }\n\n      notifyListeners();\n    });\n  }\n\n  void _listenToPlaybackState() {\n    _player.playerStateStream.listen((event) {\n      final isPlaying = event.playing;\n      final processingState = event.processingState;\n      if (processingState == ProcessingState.loading ||\n          processingState == ProcessingState.buffering) {\n        _buttonState.value = ButtonState.loading;\n      } else if (!isPlaying || processingState == ProcessingState.idle) {\n        _buttonState.value = ButtonState.paused;\n      } else if (processingState != ProcessingState.completed) {\n        _buttonState.value = ButtonState.playing;\n      } else {\n        _player.seek(Duration.zero);\n        _player.pause();\n      }\n    });\n  }\n\n  void _listenToCurrentPosition() {\n    _player.positionStream.listen((position) {\n      final oldState = _progressBarState.value;\n      if (oldState.current != position) {\n        _progressBarState.value = ProgressBarState(\n          current: position,\n          buffered: oldState.buffered,\n          total: oldState.total,\n        );\n      }\n    });\n  }\n\n  void _listenToBufferedPosition() {\n    _player.bufferedPositionStream.listen((position) {\n      final oldState = _progressBarState.value;\n      if (oldState.buffered != position) {\n        _progressBarState.value = ProgressBarState(\n          current: oldState.current,\n          buffered: position,\n          total: oldState.total,\n        );\n      }\n    });\n  }\n\n  void _listenToTotalDuration() {\n    _player.durationStream.listen((position) {\n      final oldState = _progressBarState.value;\n      if (oldState.total != position) {\n        _progressBarState.value = ProgressBarState(\n          current: oldState.current,\n          buffered: oldState.buffered,\n          total: position ?? Duration.zero,\n        );\n      }\n    });\n  }\n\n  void _listenToShuffle() {\n    _player.shuffleModeEnabledStream.listen((data) {\n      _shuffleModeEnabled = data;\n      notifyListeners();\n    });\n  }\n\n  void _listenToChangesInSong() {\n    _player.currentIndexStream.listen((index) {\n      if (_songList.isNotEmpty && _currentIndex.value != index) {\n        _currentIndex.value = index;\n        _currentSongNotifier.value =\n            index != null && _songList.isNotEmpty && index < _songList.length\n            ? _songList[index].tag\n            : null;\n        if (_songList.isNotEmpty && _currentIndex.value != null) {\n          final MediaItem item = _songList[_currentIndex.value!].tag;\n          addHistory(item.extras!);\n        }\n        notifyListeners();\n      }\n    });\n  }\n\n  Future<List> _fetchAndQueueSongs({\n    String? videoId,\n    String? playlistId,\n    String continuation = '',\n    String? params,\n    bool radio = false,\n    bool shuffle = false,\n    bool isNext = false,\n    int offset = 0,\n    int maxContinuations = 50, // playlist and albums with up to 24 * 51 songs\n    Object? session,\n  }) async {\n    Map songs = await GetIt.I<YTMusic>().getNextSongList(\n      videoId: videoId,\n      playlistId: playlistId,\n      continuation: continuation,\n      params: params,\n      radio: radio,\n      shuffle: shuffle,\n    );\n    if (!_isSessionValid(session)) return [];\n    if (songs[\"continuation\"] != null && maxContinuations > 0) {\n      final newOffset = offset + songs[\"contents\"].length as int;\n      _fetchAndQueueSongs(\n        continuation: songs[\"continuation\"],\n        isNext: isNext,\n        offset: newOffset,\n        maxContinuations: maxContinuations - 1,\n        session: session,\n      ).then((s) async {\n        if (!_isSessionValid(session)) return;\n        await _addSongListToQueue(s, isNext: isNext, offset: newOffset);\n      });\n    }\n    return songs[\"contents\"];\n  }\n\n  void changeLoopMode() {\n    switch (_loopMode.value) {\n      case LoopMode.off:\n        _loopMode.value = LoopMode.all;\n        break;\n      case LoopMode.all:\n        _loopMode.value = LoopMode.one;\n        break;\n      default:\n        _loopMode.value = LoopMode.off;\n        break;\n    }\n    _player.setLoopMode(_loopMode.value);\n  }\n\n  Future<void> skipSilence(bool value) async {\n    await _player.setSkipSilenceEnabled(value);\n    GetIt.I<SettingsManager>().skipSilence = value;\n  }\n\n  Future<AudioSource> _getAudioSource(Map<String, dynamic> song) async {\n    MediaItem tag = MediaItem(\n      id: song['videoId'],\n      title: song['title'] ?? 'Title',\n      album: song['album']?['name'],\n      artUri: Uri.parse(\n        song['thumbnails']?.first['url'].replaceAll('w60-h60', 'w225-h225'),\n      ),\n      artist: song['artists']?.map((artist) => artist['name']).join(','),\n      extras: song,\n    );\n\n    final downloadSong = GetIt.I<DownloadManager>().getDownload(\n      song['videoId'],\n    );\n    final bool isDownloaded =\n        downloadSong != null &&\n        downloadSong['status'] == 'DOWNLOADED' &&\n        downloadSong['path'] != null &&\n        (await File(downloadSong['path']).exists());\n    if (isDownloaded) {\n      return AudioSource.file(downloadSong['path'], tag: tag);\n    } else {\n      return YouTubeAudioSource(\n        videoId: song['videoId'],\n        quality: GetIt.I<SettingsManager>().streamingQuality.name.toLowerCase(),\n        tag: tag,\n      );\n    }\n  }\n\n  Future<List<AudioSource>> _getAudioSources(List songs) async {\n    return await Future.wait(\n      songs.map((song) async {\n        final mapSong = Map<String, dynamic>.from(song);\n        return await _getAudioSource(mapSong);\n      }),\n    );\n  }\n\n  Future<List> _getPlaylistSongs({\n    required Map<String, dynamic> mediaItem,\n    required Object? session,\n    bool isNext = false,\n  }) async {\n    if (mediaItem['songs'] != null) {\n      // Get Custom or Downloaded Playlist songs\n      return mediaItem['songs'];\n    } else {\n      // Get Online Playlist songs\n      return await _fetchAndQueueSongs(\n        playlistId: mediaItem['playlistId'],\n        isNext: isNext,\n        maxContinuations: mediaItem['type'] == 'ARTIST' ? 0 : 50,\n        session: session,\n      );\n    }\n  }\n\n  Future<void> playSong(Map<String, dynamic> song) async {\n    final session = _startSession();\n    if (song['videoId'] == null) return;\n\n    // clear sources and set the tapped song as the single source so it plays immediately\n    await _player.clearAudioSources();\n\n    final source = await _getAudioSource(song);\n    if (!_isSessionValid(session)) return;\n    await _player.setAudioSources([source]);\n    await _player.play();\n  }\n\n  Future<void> playNext(Map<String, dynamic> mediaItem) async {\n    final session = _startSession();\n    // Case 1: A single video/song\n    if (mediaItem['videoId'] != null) {\n      final audioSource = await _getAudioSource(mediaItem);\n\n      // Determine insertion position\n      final currentIndex = _player.currentIndex ?? -1;\n      final sequenceLength = _player.sequence.length;\n      final insertIndex = (currentIndex + 1).clamp(0, sequenceLength);\n\n      if (!_isSessionValid(session)) return;\n      // If player already has something in the queue\n      if (sequenceLength > 0) {\n        await _player.insertAudioSource(insertIndex, audioSource);\n      } else {\n        // If queue is empty, just set audio source\n        await _player.setAudioSource(audioSource);\n      }\n    } else {\n      // Case 2: Playlist\n      List songs = await _getPlaylistSongs(\n        mediaItem: mediaItem,\n        session: _activeSession,\n        isNext: true,\n      );\n      if (!_isSessionValid(session)) return;\n      await _addSongListToQueue(songs, isNext: true);\n    }\n  }\n\n  Future<void> playAll(List songs, {int index = 0}) async {\n    final session = _startSession();\n    await _player.clearAudioSources();\n\n    // Build full list and set atomically\n    final List<AudioSource> sources = await _getAudioSources(songs);\n\n    if (!_isSessionValid(session)) return;\n    await _player.setAudioSources(sources);\n    await _player.seek(Duration.zero, index: index);\n    if (!_player.playing) await _player.play();\n  }\n\n  Future<void> addToQueue(Map<String, dynamic> mediaItem) async {\n    final session = _startSession();\n    // Case 1: A single video/song\n    if (mediaItem['videoId'] != null) {\n      final audioSource = await _getAudioSource(mediaItem);\n      if (!_isSessionValid(session)) return;\n      if (_player.sequence.isEmpty) {\n        // If queue is empty, just set audio source\n        await _player.setAudioSource(audioSource);\n      } else {\n        // If player already has something in the queue\n        await _player.addAudioSource(audioSource);\n      }\n      // Case 2: Playlist\n    } else {\n      List songs = await _getPlaylistSongs(\n        mediaItem: mediaItem,\n        session: _activeSession,\n      );\n      if (!_isSessionValid(session)) return;\n      await _addSongListToQueue(songs, isNext: false);\n    }\n  }\n\n  Future<void> startRelated(\n    Map<String, dynamic> song, {\n    bool radio = false,\n    bool shuffle = false,\n    bool isArtist = false,\n  }) async {\n    final session = _startSession();\n    await _player.clearAudioSources();\n    if (!isArtist) {\n      await addToQueue(song);\n    }\n    List songs = await _fetchAndQueueSongs(\n      videoId: song['videoId'],\n      playlistId: song['playlistRadioId'],\n      radio: radio,\n      shuffle: shuffle,\n      maxContinuations: 0,\n      session: session,\n    );\n    if (!_isSessionValid(session)) return;\n    if (songs.isNotEmpty) songs.removeAt(0);\n    await _addSongListToQueue(songs);\n    await _player.play();\n  }\n\n  Future<void> startPlaylistSongs(Map endpoint) async {\n    final session = _startSession();\n    await _player.clearAudioSources();\n    List songs = await _fetchAndQueueSongs(\n      playlistId: endpoint['playlistId'],\n      params: endpoint['params'],\n      maxContinuations: endpoint['type'] == 'ARTIST' ? 0 : 50,\n      session: session,\n    );\n    if (songs.isNotEmpty && songs.first['videoId'] == null) {\n      // if API returned a placeholder, convert or handle accordingly\n    }\n    if (!_isSessionValid(session)) return;\n    await _addSongListToQueue(songs);\n    await _player.play();\n  }\n\n  Future<void> stop() async {\n    _activeSession = null;\n    await _player.stop();\n    await _player.clearAudioSources();\n    await _player.seek(Duration.zero, index: 0);\n    _currentIndex.value = null;\n    _currentSongNotifier.value = null;\n    notifyListeners();\n  }\n\n  Future<void> _addSongListToQueue(\n    List songs, {\n    bool isNext = false,\n    int offset = 0,\n  }) async {\n    if (songs.isEmpty) return;\n\n    // Convert your song objects into AudioSources\n    final newSources = await _getAudioSources(songs);\n\n    // Current queue length\n    final queueLength = _player.sequence.length;\n    if (queueLength > 0) {\n      if (isNext) {\n        // Insert immediately after the current index\n        final currentIndex = _player.currentIndex ?? -1;\n        int insertIndex = (currentIndex + offset + 1).clamp(0, queueLength);\n        await _player.insertAudioSources(insertIndex, newSources);\n      } else {\n        // Append to the end\n        await _player.addAudioSources(newSources);\n      }\n    } else {\n      // If queue is empty, just set audio sources\n      await _player.setAudioSources(newSources);\n    }\n  }\n\n  void _listenToAutofetch() {\n    player.playerStateStream.listen((state) async {\n      if (state.processingState == ProcessingState.completed &&\n          _songList.isNotEmpty &&\n          GetIt.I<SettingsManager>().autofetchSongs) {\n        final session = _startSession();\n        List songs = await _fetchAndQueueSongs(\n          videoId: _songList[_currentIndex.value ?? 0].tag.id,\n          maxContinuations: 0,\n          session: session,\n        );\n        if (!_isSessionValid(session)) return;\n        if (songs.isNotEmpty) songs.removeAt(0);\n        await _player.clearAudioSources();\n        await _addSongListToQueue(songs);\n        await _player.play();\n      }\n    });\n  }\n\n  void setTimer(Duration duration) {\n    int seconds = duration.inSeconds;\n    _timer?.cancel();\n    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {\n      seconds--;\n      _timerDuration.value = Duration(seconds: seconds);\n      if (seconds == 0) {\n        cancelTimer();\n        _player.pause();\n      }\n      notifyListeners();\n    });\n  }\n\n  void cancelTimer() {\n    _timerDuration.value = null;\n    _timer?.cancel();\n    notifyListeners();\n  }\n}\n\nenum ButtonState { loading, paused, playing }\n\nenum LoopState { off, all, one }\n\nclass ProgressBarState {\n  Duration current;\n  Duration buffered;\n  Duration total;\n  ProgressBarState({\n    this.current = Duration.zero,\n    this.buffered = Duration.zero,\n    this.total = Duration.zero,\n  });\n}\n\nextension on AndroidEqualizerParameters {\n  Map<String, dynamic> toMap() {\n    return {\n      'maxDecibels': maxDecibels,\n      'minDecibels': minDecibels,\n      'bands': bands\n          .map(\n            (e) => {\n              'centerFrequency': e.centerFrequency,\n              'gain': e.gain,\n              'index': e.index,\n            },\n          )\n          .toList(),\n    };\n  }\n}\n"
  },
  {
    "path": "lib/services/settings_manager.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:gyawun/services/file_storage.dart';\nimport 'package:hive_flutter/hive_flutter.dart';\n\nclass SettingsManager extends ChangeNotifier {\n  final Box _box;\n\n  ThemeMode _themeMode = ThemeMode.system;\n  final List<ThemeMode> _themeModes = [\n    ThemeMode.system,\n    ThemeMode.light,\n    ThemeMode.dark,\n  ];\n  late Map<String, String> _location;\n  late Map<String, String> _language;\n  bool _autofetchSongs = true;\n  final List<AudioQuality> _audioQualities = [\n    AudioQuality.high,\n    AudioQuality.low,\n  ];\n\n  AudioQuality _streamingQuality = AudioQuality.high;\n  AudioQuality _downloadQuality = AudioQuality.high;\n  bool _skipSilence = false;\n  Color? _accentColor;\n  bool _amoledBlack = true;\n  bool _dynamicColors = false;\n  bool _equalizerEnabled = false;\n  Map _equalizerParameters = {};\n  bool _loudnessEnabled = false;\n  double _loudnessTargetGain = 0.0;\n  bool _searchHistory = true;\n  bool _translateLyrics = false;\n  bool _playbackHistory = true;\n  bool _personalisedContent = true;\n  String? _visitorId;\n  String? _apiKey;\n  String? _clientName;\n  String? _clientVersion;\n  String _appFolder = FileStorage.defaultPath;\n\n  ThemeMode get themeMode => _themeMode;\n  List<ThemeMode> get themeModes => _themeModes;\n  Map<String, String> get location => _location;\n  List<Map<String, String>> get locations => _countries;\n  Map<String, String> get language => _language;\n  bool get autofetchSongs => _autofetchSongs;\n  List<Map<String, String>> get languages => _languages;\n  List<AudioQuality> get audioQualities => _audioQualities;\n  AudioQuality get streamingQuality => _streamingQuality;\n  AudioQuality get downloadQuality => _downloadQuality;\n  bool get skipSilence => _skipSilence;\n\n  Color? get accentColor => _accentColor;\n  bool get amoledBlack => _amoledBlack;\n  bool get dynamicColors => _dynamicColors;\n  bool get loudnessEnabled => _loudnessEnabled;\n  double get loudnessTargetGain => _loudnessTargetGain;\n  bool get equalizerEnabled => _equalizerEnabled;\n  Map get equalizerParameters => _equalizerParameters;\n  List<double> get equalizerBandsGain =>\n      (equalizerParameters['bands'] as List?)\n          ?.map<double>((e) => (e['gain'] as num).toDouble())\n          .toList() ??\n      [];\n\n  bool get searchHistory => _searchHistory;\n  bool get translateLyrics => _translateLyrics;\n  bool get playbackHistory => _playbackHistory;\n  bool get personalisedContent => _personalisedContent;\n  String? get visitorId => _visitorId;\n  String? get apiKey => _apiKey;\n  String? get clientName => _clientName;\n  String? get clientVersion => _clientVersion;\n  String get appFolder => _appFolder;\n\n  Map get settings => _box.toMap();\n\n  SettingsManager._(this._box) {\n    _init();\n  }\n\n  static Future<SettingsManager> create() async {\n    final boxName = 'SETTINGS';\n    await Hive.openBox(boxName);\n    final instance = SettingsManager._(Hive.box(boxName));\n    return instance;\n  }\n\n  void _init() {\n    _themeMode = _themeModes[_box.get('THEME_MODE', defaultValue: 0)];\n    _language = _languages.firstWhere(\n      (language) =>\n          language['value'] == _box.get('LANGUAGE', defaultValue: 'en-IN'),\n    );\n    _autofetchSongs = _box.get('AUTOFETCH_SONGS', defaultValue: true);\n    _accentColor = _box.get('ACCENT_COLOR') != null\n        ? Color(_box.get('ACCENT_COLOR'))\n        : null;\n    _amoledBlack = _box.get('AMOLED_BLACK', defaultValue: true);\n    _dynamicColors = _box.get('DYNAMIC_COLORS', defaultValue: false);\n\n    _location = _countries.firstWhere(\n      (country) => country['value'] == _box.get('LOCATION', defaultValue: 'IN'),\n    );\n\n    _streamingQuality =\n        _audioQualities[_box.get('STREAMING_QUALITY', defaultValue: 0)];\n    _downloadQuality =\n        _audioQualities[_box.get('DOWNLOAD_QUALITY', defaultValue: 0)];\n    _skipSilence = _box.get('SKIP_SILENCE', defaultValue: false);\n    _equalizerEnabled = _box.get('EQUALIZER_ENABLED', defaultValue: false);\n    _loudnessEnabled = _box.get('LOUDNESS_ENABLED', defaultValue: false);\n    _loudnessTargetGain = _box.get('LOUDNESS_TARGET_GAIN', defaultValue: 0.0);\n    _equalizerParameters = _box.get('EQUALIZER_PARAMETERS', defaultValue: {});\n    _searchHistory = _box.get('SEARCH_HISTORY', defaultValue: true);\n    _translateLyrics = _box.get('TRANSLATE_LYRICS', defaultValue: false);\n    _playbackHistory = _box.get('PLAYBACK_HISTORY', defaultValue: true);\n    _personalisedContent = _box.get('PERSONALISED_CONTENT', defaultValue: true);\n    _visitorId = _box.get('YT_VISITOR_ID');\n    _apiKey = _box.get('YT_API_KEY');\n    _clientName = _box.get('YT_CLIENT_NAME');\n    _clientVersion = _box.get('YT_CLIENT_VERSION');\n    _appFolder = _box.get('APP_FOLDER', defaultValue: FileStorage.defaultPath);\n  }\n\n  Future<void> setThemeMode(ThemeMode mode) async {\n    _box.put('THEME_MODE', _themeModes.indexOf(mode));\n    _themeMode = mode;\n\n    notifyListeners();\n  }\n\n  set searchHistory(bool value) {\n    _box.put('SEARCH_HISTORY', value);\n    _searchHistory = value;\n    notifyListeners();\n  }\n\n  set translateLyrics(bool value) {\n    _box.put('TRANSLATE_LYRICS', value);\n    _translateLyrics = value;\n    notifyListeners();\n  }\n\n  set playbackHistory(bool value) {\n    _box.put('PLAYBACK_HISTORY', value);\n    _playbackHistory = value;\n    notifyListeners();\n  }\n\n  set personalisedContent(bool value) {\n    _box.put('PERSONALISED_CONTENT', value);\n    _personalisedContent = value;\n    notifyListeners();\n  }\n\n  set visitorId(String? value) {\n    _box.put('YT_VISITOR_ID', value);\n    _visitorId = value;\n    notifyListeners();\n  }\n\n  set apiKey(String? value) {\n    _box.put('YT_API_KEY', value);\n    _apiKey = value;\n    notifyListeners();\n  }\n\n  set clientName(String? value) {\n    _box.put('YT_CLIENT_NAME', value);\n    _clientName = value;\n    notifyListeners();\n  }\n\n  set clientVersion(String? value) {\n    _box.put('YT_CLIENT_VERSION', value);\n    _clientVersion = value;\n    notifyListeners();\n  }\n\n  set appFolder(String value) {\n    _box.put('APP_FOLDER', value);\n    _appFolder = value;\n    notifyListeners();\n  }\n\n  set location(Map<String, String> value) {\n    _box.put('LOCATION', value['value']);\n    _location = value;\n    notifyListeners();\n  }\n\n  set language(Map<String, String> value) {\n    _box.put('LANGUAGE', value['value']);\n    _language = value;\n    notifyListeners();\n  }\n\n  set autofetchSongs(bool value) {\n    _box.put('AUTOFETCH_SONGS', value);\n    _autofetchSongs = value;\n    notifyListeners();\n  }\n\n  set streamingQuality(AudioQuality value) {\n    _box.put('STREAMING_QUALITY', _audioQualities.indexOf(value));\n    _streamingQuality = value;\n    notifyListeners();\n  }\n\n  set downloadQuality(AudioQuality value) {\n    _box.put('DOWNLOAD_QUALITY', _audioQualities.indexOf(value));\n    _downloadQuality = value;\n    notifyListeners();\n  }\n\n  set skipSilence(bool value) {\n    _box.put('SKIP_SILENCE', value);\n    _skipSilence = value;\n    notifyListeners();\n  }\n\n  set accentColor(Color? color) {\n    int? c = color?.toARGB32();\n    _box.put('ACCENT_COLOR', c);\n    _accentColor = color;\n    notifyListeners();\n  }\n\n  set amoledBlack(bool val) {\n    _box.put('AMOLED_BLACK', val);\n    _amoledBlack = val;\n    notifyListeners();\n  }\n\n  set dynamicColors(bool isMaterial) {\n    _box.put('DYNAMIC_COLORS', isMaterial);\n    _dynamicColors = isMaterial;\n    notifyListeners();\n  }\n\n  set equalizerEnabled(bool enabled) {\n    _box.put('EQUALIZER_ENABLED', enabled);\n    _equalizerEnabled = enabled;\n    notifyListeners();\n  }\n\n  Future<void> setEqualizerParameters(Map value) async {\n    await _box.put('EQUALIZER_PARAMETERS', value);\n    _equalizerParameters = value;\n    notifyListeners();\n  }\n\n  Future<void> setEqualizerBandsGain(int index, double value) async {\n    _equalizerParameters['bands'][index]['gain'] = value;\n    await _box.put('EQUALIZER_PARAMETERS', _equalizerParameters);\n    notifyListeners();\n  }\n\n  // ignore: strict_top_level_inference\n  set loudnessEnabled(enabled) {\n    _box.put('LOUDNESS_ENABLED', enabled);\n    _loudnessEnabled = enabled;\n    notifyListeners();\n  }\n\n  set loudnessTargetGain(double value) {\n    _box.put('LOUDNESS_TARGET_GAIN', value);\n    _loudnessTargetGain = value;\n    notifyListeners();\n  }\n\n  Future<void> setSettings(Map value) async {\n    await Future.forEach(value.entries, (entry) async {\n      await _box.put(entry.key, entry.value);\n    });\n    notifyListeners();\n    _init();\n  }\n}\n\nbool getDarkness(int themeMode) {\n  if (themeMode == 0) {\n    return MediaQueryData.fromView(\n              WidgetsBinding.instance.platformDispatcher.views.first,\n            ).platformBrightness ==\n            Brightness.dark\n        ? true\n        : false;\n  } else if (themeMode == 2) {\n    return true;\n  }\n  return false;\n}\n\nenum AudioQuality { high, low }\n\nList<Map<String, String>> _countries = [\n  {\"name\": \"Algeria\", \"value\": \"DZ\"},\n  {\"name\": \"Argentina\", \"value\": \"AR\"},\n  {\"name\": \"Australia\", \"value\": \"AU\"},\n  {\"name\": \"Austria\", \"value\": \"AT\"},\n  {\"name\": \"Azerbaijan\", \"value\": \"AZ\"},\n  {\"name\": \"Bahrain\", \"value\": \"BH\"},\n  {\"name\": \"Bangladesh\", \"value\": \"BD\"},\n  {\"name\": \"Belarus\", \"value\": \"BY\"},\n  {\"name\": \"Belgium\", \"value\": \"BE\"},\n  {\"name\": \"Bolivia\", \"value\": \"BO\"},\n  {\"name\": \"Bosnia and Herzegovina\", \"value\": \"BA\"},\n  {\"name\": \"Brazil\", \"value\": \"BR\"},\n  {\"name\": \"Bulgaria\", \"value\": \"BG\"},\n  {\"name\": \"Cambodia\", \"value\": \"KH\"},\n  {\"name\": \"Canada\", \"value\": \"CA\"},\n  {\"name\": \"Chile\", \"value\": \"CL\"},\n  {\"name\": \"Colombia\", \"value\": \"CO\"},\n  {\"name\": \"Costa Rica\", \"value\": \"CR\"},\n  {\"name\": \"Croatia\", \"value\": \"HR\"},\n  {\"name\": \"Cyprus\", \"value\": \"CY\"},\n  {\"name\": \"Czechia\", \"value\": \"CZ\"},\n  {\"name\": \"Denmark\", \"value\": \"DK\"},\n  {\"name\": \"Dominican Republic\", \"value\": \"DO\"},\n  {\"name\": \"Ecuador\", \"value\": \"EC\"},\n  {\"name\": \"Egypt\", \"value\": \"EG\"},\n  {\"name\": \"El Salvador\", \"value\": \"SV\"},\n  {\"name\": \"Estonia\", \"value\": \"EE\"},\n  {\"name\": \"Finland\", \"value\": \"FI\"},\n  {\"name\": \"France\", \"value\": \"FR\"},\n  {\"name\": \"Georgia\", \"value\": \"GE\"},\n  {\"name\": \"Germany\", \"value\": \"DE\"},\n  {\"name\": \"Ghana\", \"value\": \"GH\"},\n  {\"name\": \"Greece\", \"value\": \"GR\"},\n  {\"name\": \"Guatemala\", \"value\": \"GT\"},\n  {\"name\": \"Honduras\", \"value\": \"HN\"},\n  {\"name\": \"Hong Kong\", \"value\": \"HK\"},\n  {\"name\": \"Hungary\", \"value\": \"HU\"},\n  {\"name\": \"Iceland\", \"value\": \"IS\"},\n  {\"name\": \"India\", \"value\": \"IN\"},\n  {\"name\": \"Indonesia\", \"value\": \"ID\"},\n  {\"name\": \"Iraq\", \"value\": \"IQ\"},\n  {\"name\": \"Ireland\", \"value\": \"IE\"},\n  {\"name\": \"Israel\", \"value\": \"IL\"},\n  {\"name\": \"Italy\", \"value\": \"IT\"},\n  {\"name\": \"Jamaica\", \"value\": \"JM\"},\n  {\"name\": \"Japan\", \"value\": \"JP\"},\n  {\"name\": \"Jordan\", \"value\": \"JO\"},\n  {\"name\": \"Kazakhstan\", \"value\": \"KZ\"},\n  {\"name\": \"Kenya\", \"value\": \"KE\"},\n  {\"name\": \"Kuwait\", \"value\": \"KW\"},\n  {\"name\": \"Laos\", \"value\": \"LA\"},\n  {\"name\": \"Latvia\", \"value\": \"LV\"},\n  {\"name\": \"Lebanon\", \"value\": \"LB\"},\n  {\"name\": \"Libya\", \"value\": \"LY\"},\n  {\"name\": \"Liechtenstein\", \"value\": \"LI\"},\n  {\"name\": \"Lithuania\", \"value\": \"LT\"},\n  {\"name\": \"Luxembourg\", \"value\": \"LU\"},\n  {\"name\": \"Malaysia\", \"value\": \"MY\"},\n  {\"name\": \"Malta\", \"value\": \"MT\"},\n  {\"name\": \"Mexico\", \"value\": \"MX\"},\n  {\"name\": \"Moldova\", \"value\": \"MD\"},\n  {\"name\": \"Montenegro\", \"value\": \"ME\"},\n  {\"name\": \"Morocco\", \"value\": \"MA\"},\n  {\"name\": \"Nepal\", \"value\": \"NP\"},\n  {\"name\": \"Netherlands\", \"value\": \"NL\"},\n  {\"name\": \"New Zealand\", \"value\": \"NZ\"},\n  {\"name\": \"Nicaragua\", \"value\": \"NI\"},\n  {\"name\": \"Nigeria\", \"value\": \"NG\"},\n  {\"name\": \"North Macedonia\", \"value\": \"MK\"},\n  {\"name\": \"Norway\", \"value\": \"NO\"},\n  {\"name\": \"Oman\", \"value\": \"OM\"},\n  {\"name\": \"Pakistan\", \"value\": \"PK\"},\n  {\"name\": \"Panama\", \"value\": \"PA\"},\n  {\"name\": \"Papua New Guinea\", \"value\": \"PG\"},\n  {\"name\": \"Paraguay\", \"value\": \"PY\"},\n  {\"name\": \"Peru\", \"value\": \"PE\"},\n  {\"name\": \"Philippines\", \"value\": \"PH\"},\n  {\"name\": \"Poland\", \"value\": \"PL\"},\n  {\"name\": \"Portugal\", \"value\": \"PT\"},\n  {\"name\": \"Puerto Rico\", \"value\": \"PR\"},\n  {\"name\": \"Qatar\", \"value\": \"QA\"},\n  {\"name\": \"Romania\", \"value\": \"RO\"},\n  {\"name\": \"Russia\", \"value\": \"RU\"},\n  {\"name\": \"Saudi Arabia\", \"value\": \"SA\"},\n  {\"name\": \"Senegal\", \"value\": \"SN\"},\n  {\"name\": \"Serbia\", \"value\": \"RS\"},\n  {\"name\": \"Singapore\", \"value\": \"SG\"},\n  {\"name\": \"Slovakia\", \"value\": \"SK\"},\n  {\"name\": \"Slovenia\", \"value\": \"SI\"},\n  {\"name\": \"South Africa\", \"value\": \"ZA\"},\n  {\"name\": \"South Korea\", \"value\": \"KR\"},\n  {\"name\": \"Spain\", \"value\": \"ES\"},\n  {\"name\": \"Sri Lanka\", \"value\": \"LK\"},\n  {\"name\": \"Sweden\", \"value\": \"SE\"},\n  {\"name\": \"Switzerland\", \"value\": \"CH\"},\n  {\"name\": \"Taiwan\", \"value\": \"TW\"},\n  {\"name\": \"Tanzania\", \"value\": \"TZ\"},\n  {\"name\": \"Thailand\", \"value\": \"TH\"},\n  {\"name\": \"Tunisia\", \"value\": \"TN\"},\n  {\"name\": \"Turkey\", \"value\": \"TR\"},\n  {\"name\": \"Uganda\", \"value\": \"UG\"},\n  {\"name\": \"Ukraine\", \"value\": \"UA\"},\n  {\"name\": \"United Arab Emirates\", \"value\": \"AE\"},\n  {\"name\": \"United Kingdom\", \"value\": \"GB\"},\n  {\"name\": \"United States\", \"value\": \"US\"},\n  {\"name\": \"Uruguay\", \"value\": \"UY\"},\n  {\"name\": \"Venezuela\", \"value\": \"VE\"},\n  {\"name\": \"Vietnam\", \"value\": \"VN\"},\n  {\"name\": \"Yemen\", \"value\": \"YE\"},\n  {\"name\": \"Zimbabwe\", \"value\": \"ZW\"},\n];\n\nList<Map<String, String>> _languages = [\n  {\"name\": \"Afrikaans\", \"value\": \"af\"},\n  {\"name\": \"Azərbaycan\", \"value\": \"az\"},\n  {\"name\": \"Bahasa Indonesia\", \"value\": \"id\"},\n  {\"name\": \"Bahasa Malaysia\", \"value\": \"ms\"},\n  {\"name\": \"Bosanski\", \"value\": \"bs\"},\n  {\"name\": \"Català\", \"value\": \"ca\"},\n  {\"name\": \"Čeština\", \"value\": \"cs\"},\n  {\"name\": \"Dansk\", \"value\": \"da\"},\n  {\"name\": \"Deutsch\", \"value\": \"de\"},\n  {\"name\": \"Eesti\", \"value\": \"et\"},\n  {\"name\": \"English (India)\", \"value\": \"en-IN\"},\n  {\"name\": \"English (UK)\", \"value\": \"en-GB\"},\n  {\"name\": \"English (US)\", \"value\": \"en\"},\n  {\"name\": \"Español (España)\", \"value\": \"es\"},\n  {\"name\": \"Español (Latinoamérica)\", \"value\": \"es-419\"},\n  {\"name\": \"Español (US)\", \"value\": \"es-US\"},\n  {\"name\": \"Euskara\", \"value\": \"eu\"},\n  {\"name\": \"Filipino\", \"value\": \"fil\"},\n  {\"name\": \"Français\", \"value\": \"fr\"},\n  {\"name\": \"Français (Canada)\", \"value\": \"fr-CA\"},\n  {\"name\": \"Galego\", \"value\": \"gl\"},\n  {\"name\": \"Hrvatski\", \"value\": \"hr\"},\n  {\"name\": \"IsiZulu\", \"value\": \"zu\"},\n  {\"name\": \"Íslenska\", \"value\": \"is\"},\n  {\"name\": \"Italiano\", \"value\": \"it\"},\n  {\"name\": \"Kiswahili\", \"value\": \"sw\"},\n  {\"name\": \"Latviešu valoda\", \"value\": \"lv\"},\n  {\"name\": \"Lietuvių\", \"value\": \"lt\"},\n  {\"name\": \"Magyar\", \"value\": \"hu\"},\n  {\"name\": \"Nederlands\", \"value\": \"nl\"},\n  {\"name\": \"Norsk\", \"value\": \"no\"},\n  {\"name\": \"O‘zbek\", \"value\": \"uz\"},\n  {\"name\": \"Polski\", \"value\": \"pl\"},\n  {\"name\": \"Português\", \"value\": \"pt-PT\"},\n  {\"name\": \"Português (Brasil)\", \"value\": \"pt\"},\n  {\"name\": \"Română\", \"value\": \"ro\"},\n  {\"name\": \"Shqip\", \"value\": \"sq\"},\n  {\"name\": \"Slovenčina\", \"value\": \"sk\"},\n  {\"name\": \"Slovenščina\", \"value\": \"sl\"},\n  {\"name\": \"Srpski\", \"value\": \"sr-Latn\"},\n  {\"name\": \"Suomi\", \"value\": \"fi\"},\n  {\"name\": \"Svenska\", \"value\": \"sv\"},\n  {\"name\": \"Tiếng Việt\", \"value\": \"vi\"},\n  {\"name\": \"Türkçe\", \"value\": \"tr\"},\n  {\"name\": \"Беларуская\", \"value\": \"be\"},\n  {\"name\": \"Български\", \"value\": \"bg\"},\n  {\"name\": \"Кыргызча\", \"value\": \"ky\"},\n  {\"name\": \"Қазақ Тілі\", \"value\": \"kk\"},\n  {\"name\": \"Македонски\", \"value\": \"mk\"},\n  {\"name\": \"Монгол\", \"value\": \"mn\"},\n  {\"name\": \"Русский\", \"value\": \"ru\"},\n  {\"name\": \"Српски\", \"value\": \"sr\"},\n  {\"name\": \"Українська\", \"value\": \"uk\"},\n  {\"name\": \"Ελληνικά\", \"value\": \"el\"},\n  {\"name\": \"Հայերեն\", \"value\": \"hy\"},\n  {\"name\": \"עברית\", \"value\": \"iw\"},\n  {\"name\": \"اردو\", \"value\": \"ur\"},\n  {\"name\": \"العربية\", \"value\": \"ar\"},\n  {\"name\": \"فارسی\", \"value\": \"fa\"},\n  {\"name\": \"नेपाली\", \"value\": \"ne\"},\n  {\"name\": \"मराठी\", \"value\": \"mr\"},\n  {\"name\": \"हिन्दी\", \"value\": \"hi\"},\n  {\"name\": \"অসমীয়া\", \"value\": \"as\"},\n  {\"name\": \"বাংলা\", \"value\": \"bn\"},\n  {\"name\": \"ਪੰਜਾਬੀ\", \"value\": \"pa\"},\n  {\"name\": \"ગુજરાતી\", \"value\": \"gu\"},\n  {\"name\": \"ଓଡ଼ିଆ\", \"value\": \"or\"},\n  {\"name\": \"தமிழ்\", \"value\": \"ta\"},\n  {\"name\": \"తెలుగు\", \"value\": \"te\"},\n  {\"name\": \"ಕನ್ನಡ\", \"value\": \"kn\"},\n  {\"name\": \"മലയാളം\", \"value\": \"ml\"},\n  {\"name\": \"සිංහල\", \"value\": \"si\"},\n  {\"name\": \"ภาษาไทย\", \"value\": \"th\"},\n  {\"name\": \"ລາວ\", \"value\": \"lo\"},\n  {\"name\": \"ဗမာ\", \"value\": \"my\"},\n  {\"name\": \"ქართული\", \"value\": \"ka\"},\n  {\"name\": \"አማርኛ\", \"value\": \"am\"},\n  {\"name\": \"ខ្មែរ\", \"value\": \"km\"},\n  {\"name\": \"中文 (简体)\", \"value\": \"zh-CN\"},\n  {\"name\": \"中文 (繁體)\", \"value\": \"zh-TW\"},\n  {\"name\": \"中文 (香港)\", \"value\": \"zh-HK\"},\n  {\"name\": \"日本語\", \"value\": \"ja\"},\n  {\"name\": \"한국어\", \"value\": \"ko\"},\n];\n"
  },
  {
    "path": "lib/services/stream_client.dart",
    "content": "import 'dart:async';\n\nimport 'package:http/http.dart' as http;\nimport 'package:youtube_explode_dart/youtube_explode_dart.dart';\n\nclass AudioStreamClient {\n  final http.Client _httpClient = http.Client();\n\n  AudioStreamClient();\n  static const Map<String, String> _defaultHeaders = {\n    'user-agent':\n        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36',\n    'cookie': 'CONSENT=YES+cb',\n    'accept':\n        'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',\n    'accept-language': 'en-US,en;q=0.9',\n    'sec-fetch-dest': 'document',\n    'sec-fetch-mode': 'navigate',\n    'sec-fetch-site': 'none',\n    'sec-fetch-user': '?1',\n    'sec-gpc': '1',\n    'upgrade-insecure-requests': '1',\n  };\n\n  Stream<List<int>> getAudioStream(StreamInfo streamInfo,\n          {required int start, required int end}) =>\n      _getStream(streamInfo, streamClient: this, start: start, end: end);\n\n  Stream<List<int>> _getStream(\n    StreamInfo streamInfo, {\n    Map<String, String> headers = const {},\n    bool validate = true,\n    required int start,\n    required int end,\n    int errorCount = 0,\n    required AudioStreamClient streamClient,\n  }) async* {\n    var url = streamInfo.url;\n    var bytesCount = start;\n    while (bytesCount != end) {\n      try {\n        final response = await retry(this, () async {\n          final from = bytesCount;\n          final to = end - 1;\n\n          late final http.Request request;\n          if (url.queryParameters['c'] == 'ANDROID') {\n            request = http.Request('get', url);\n            request.headers['Range'] = 'bytes=$from-$to';\n          } else {\n            url = url.replace(queryParameters: {\n              ...url.queryParameters,\n              'range': '$from-$to'\n            });\n            request = http.Request('get', url);\n          }\n          return send(request);\n        });\n        if (validate) {\n          try {\n            _validateResponse(response, response.statusCode);\n          } on FatalFailureException {\n            continue;\n          }\n        }\n        final stream = StreamController<List<int>>();\n        response.stream.listen(\n          (data) {\n            bytesCount += data.length;\n            stream.add(data);\n          },\n          onError: (_) => null,\n          onDone: stream.close,\n          cancelOnError: false,\n        );\n        errorCount = 0;\n        yield* stream.stream;\n      } on HttpClientClosedException {\n        break;\n      } on Exception {\n        if (errorCount == 5) {\n          rethrow;\n        }\n        await Future.delayed(const Duration(milliseconds: 500));\n        yield* _getStream(\n          streamInfo,\n          streamClient: streamClient,\n          headers: headers,\n          validate: validate,\n          start: bytesCount,\n          end: end,\n          errorCount: errorCount + 1,\n        );\n        break;\n      }\n    }\n  }\n\n  void _validateResponse(http.BaseResponse response, int statusCode) {\n    final request = response.request!;\n\n    if (request.url.host.endsWith('.google.com') &&\n        request.url.path.startsWith('/sorry/')) {\n      throw RequestLimitExceededException.httpRequest(response);\n    }\n\n    if (statusCode >= 500) {\n      throw TransientFailureException.httpRequest(response);\n    }\n\n    if (statusCode == 429) {\n      throw RequestLimitExceededException.httpRequest(response);\n    }\n\n    if (statusCode >= 400) {\n      throw FatalFailureException.httpRequest(response);\n    }\n  }\n\n  Future<T> retry<T>(\n    AudioStreamClient? client,\n    FutureOr<T> Function() function,\n  ) async {\n    var retryCount = 5;\n\n    // ignore: literal_only_boolean_expressions\n    while (true) {\n      try {\n        return await function();\n        // ignore: avoid_catches_without_on_clauses\n      } on Exception catch (e) {\n        retryCount -= getExceptionCost(e);\n        if (retryCount <= 0) {\n          rethrow;\n        }\n        await Future.delayed(const Duration(milliseconds: 500));\n      }\n    }\n  }\n\n  int getExceptionCost(Exception e) {\n    if (e is RequestLimitExceededException) {\n      return 2;\n    }\n    if (e is FatalFailureException) {\n      return 3;\n    }\n    return 1;\n  }\n\n  Future<http.StreamedResponse> send(http.BaseRequest request) async {\n    // Apply default headers if they are not already present\n    _defaultHeaders.forEach((key, value) {\n      if (request.headers[key] == null) {\n        request.headers[key] = _defaultHeaders[key]!;\n      }\n    });\n\n    // print(request);\n    // print(StackTrace.current);\n    return _httpClient.send(request);\n  }\n}\n"
  },
  {
    "path": "lib/services/update_service/models/update_info.dart",
    "content": "import 'package:pub_semver/pub_semver.dart';\n\nclass UpdateInfo {\n  final Version version;\n  final String name;\n  final String body;\n  final String publishedAt;\n  final String downloadUrl;\n\n  UpdateInfo({\n    required this.version,\n    required this.name,\n    required this.body,\n    required this.publishedAt,\n    required this.downloadUrl,\n  });\n}\n"
  },
  {
    "path": "lib/services/update_service/update_service.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:device_info_plus/device_info_plus.dart';\nimport 'package:flutter/material.dart';\nimport 'package:gyawun/services/update_service/models/update_info.dart';\nimport 'package:gyawun/services/update_service/widgets/update_checking.dart';\nimport 'package:gyawun/services/update_service/widgets/update_dialog.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:package_info_plus/package_info_plus.dart';\nimport 'package:pub_semver/pub_semver.dart';\n\nclass UpdateService {\n  static const String owner = 'sheikhhaziq';\n  static const String repo = 'gyawun_music';\n\n  /* ─────────────────────────────────────────────\n   * UPDATE CHECK (CHANNEL AWARE)\n   * ───────────────────────────────────────────── */\n  static Future<UpdateInfo?> checkForUpdate() async {\n    try {\n      final package = await PackageInfo.fromPlatform();\n      final currentVersion = Version.parse(package.version);\n\n      final bool isAlpha = package.version.contains('-alpha.');\n      final bool isBeta = package.version.contains('-beta.');\n\n      final uri = Uri.parse(\n        'https://api.github.com/repos/$owner/$repo/releases',\n      );\n\n      final response = await http.get(\n        uri,\n        headers: {'Accept': 'application/vnd.github+json'},\n      );\n\n      if (response.statusCode != 200) return null;\n\n      final List releases = jsonDecode(response.body);\n\n      Iterable<Map<String, dynamic>> channelReleases;\n\n      if (isAlpha) {\n        channelReleases = releases\n            .where(\n              (r) =>\n                  r['prerelease'] == true &&\n                  r['tag_name'].toString().contains('-alpha.'),\n            )\n            .whereType<Map<String, dynamic>>();\n      } else if (isBeta) {\n        channelReleases = releases\n            .where(\n              (r) =>\n                  r['prerelease'] == true &&\n                  r['tag_name'].toString().contains('-beta.'),\n            )\n            .whereType<Map<String, dynamic>>();\n      } else {\n        channelReleases = releases\n            .where((r) => r['prerelease'] == false)\n            .whereType<Map<String, dynamic>>();\n      }\n\n      if (channelReleases.isEmpty) return null;\n\n      final sorted = channelReleases.toList()\n        ..sort((a, b) {\n          final va = Version.parse(\n            a['tag_name'].toString().replaceFirst('v', ''),\n          );\n          final vb = Version.parse(\n            b['tag_name'].toString().replaceFirst('v', ''),\n          );\n          return vb.compareTo(va);\n        });\n\n      final release = sorted.first;\n\n      final remoteVersion = Version.parse(\n        release['tag_name'].toString().replaceFirst('v', ''),\n      );\n\n      if (remoteVersion <= currentVersion) return null;\n\n      final asset = await _selectAsset(release['assets']);\n      if (asset == null) return null;\n\n      return UpdateInfo(\n        version: remoteVersion,\n        name: release['name'] ?? '',\n        body: release['body'] ?? '',\n        publishedAt: release['published_at'] ?? '',\n        downloadUrl: asset['browser_download_url'],\n      );\n    } catch (_) {\n      return null;\n    }\n  }\n\n  /* ───────────────────────────────────────────── */\n  static Future<void> autoCheck(BuildContext context) async {\n    final update = await checkForUpdate();\n    if (update == null || !context.mounted) return;\n    await showUpdateDialog(context, update);\n  }\n\n  static Future<void> manualCheck(BuildContext context) async {\n    showDialog(\n      context: context,\n      barrierDismissible: false,\n      useRootNavigator: false,\n      builder: (_) => const UpdateCheckingDialog(),\n    );\n\n    final update = await checkForUpdate();\n\n    if (!context.mounted) return;\n    Navigator.pop(context);\n\n    if (update != null) {\n      await showUpdateDialog(context, update);\n    } else {\n      ScaffoldMessenger.of(context).showSnackBar(\n        const SnackBar(content: Text('You are already on the latest version')),\n      );\n    }\n  }\n\n  static Future<void> showUpdateDialog(BuildContext context, UpdateInfo info) {\n    return showDialog(\n      context: context,\n      useRootNavigator: false,\n      builder: (_) => UpdateDialog(info),\n    );\n  }\n\n  /* ─────────────────────────────────────────────\n   * ASSET SELECTION\n   * ───────────────────────────────────────────── */\n  static Future<Map?> _selectAsset(List assets) async {\n    final deviceInfo = DeviceInfoPlugin();\n\n    if (Platform.isAndroid) {\n      final android = await deviceInfo.androidInfo;\n      for (final abi in android.supportedAbis) {\n        final match = assets.where((a) => a['name'].contains(abi)).toList();\n        if (match.isNotEmpty) return match.first;\n      }\n    }\n\n    // if (Platform.isWindows) {\n    //   return assets.firstWhere(\n    //     (a) => a['name'].toString().endsWith('.exe'),\n    //     orElse: () => null,\n    //   );\n    // }\n\n    return null;\n  }\n}\n"
  },
  {
    "path": "lib/services/update_service/widgets/update_checking.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass UpdateCheckingDialog extends StatelessWidget {\n  const UpdateCheckingDialog({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return const Dialog(\n      insetPadding: EdgeInsets.all(48),\n      child: Padding(\n        padding: EdgeInsets.all(24),\n        child: Row(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            CircularProgressIndicator(),\n            SizedBox(width: 16),\n            Text('Checking for updates…'),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/services/update_service/widgets/update_dialog.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_markdown_plus/flutter_markdown_plus.dart';\nimport 'package:gyawun/services/update_service/models/update_info.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass UpdateDialog extends StatelessWidget {\n  final UpdateInfo info;\n\n  const UpdateDialog(this.info, {super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n\n    return AlertDialog(\n      title: const Text('Update Available'),\n      content: ConstrainedBox(\n        constraints: const BoxConstraints(maxHeight: 400),\n        child: SingleChildScrollView(\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: [\n              Text(\n                'Version ${info.version}',\n                style: theme.textTheme.titleMedium,\n              ),\n              const SizedBox(height: 12),\n              MarkdownBody(\n                data: info.body.isNotEmpty\n                    ? info.body\n                    : '_No changelog provided._',\n                selectable: true,\n                styleSheet: MarkdownStyleSheet.fromTheme(theme).copyWith(\n                  p: theme.textTheme.bodyMedium,\n                  h1: theme.textTheme.titleLarge,\n                  h2: theme.textTheme.titleMedium,\n                  h3: theme.textTheme.titleSmall,\n                  blockquoteDecoration: BoxDecoration(\n                    color: theme.colorScheme.surfaceContainerHighest,\n                    borderRadius: BorderRadius.circular(8),\n                  ),\n                ),\n                onTapLink: (text, href, title) async {\n                  if (href == null) return;\n                  final uri = Uri.parse(href);\n                  if (await canLaunchUrl(uri)) {\n                    await launchUrl(\n                      uri,\n                      mode: LaunchMode.externalApplication,\n                    );\n                  }\n                },\n              ),\n            ],\n          ),\n        ),\n      ),\n      actions: [\n        TextButton(\n          onPressed: () => Navigator.pop(context),\n          child: const Text('Later'),\n        ),\n        FilledButton(\n          onPressed: () async {\n            final uri = Uri.parse(info.downloadUrl);\n\n            await launchUrl(\n                uri,\n                mode: LaunchMode.externalApplication,\n              );\n          },\n          child: const Text('Update'),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/services/yt_audio_stream.dart",
    "content": "// ignore_for_file: experimental_member_use\n\nimport 'dart:async';\nimport 'dart:io';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:just_audio/just_audio.dart';\nimport 'package:youtube_explode_dart/youtube_explode_dart.dart';\n\nclass YouTubeAudioSource extends StreamAudioSource {\n  final String videoId;\n  final String quality; // 'high' or 'low'\n  final YoutubeExplode ytExplode;\n\n  YouTubeAudioSource({required this.videoId, required this.quality, super.tag})\n    : ytExplode = YoutubeExplode();\n\n  @override\n  Future<StreamAudioResponse> request([int? start, int? end]) async {\n    try {\n      final manifest = await ytExplode.videos.streams.getManifest(\n        videoId,\n        requireWatchPage: true,\n        ytClients: [YoutubeApiClient.androidVr],\n      );\n      final supportedStreams = manifest.audioOnly.sortByBitrate();\n\n      final audioStream = quality == 'high'\n          ? supportedStreams.firstOrNull\n          : supportedStreams.lastOrNull;\n\n      if (audioStream == null) {\n        throw Exception('No audio stream available for this video.');\n      }\n\n      start ??= 0;\n      end ??= (audioStream.isThrottled\n          ? (end ?? (start + 10379935))\n          : audioStream.size.totalBytes);\n      if (end > audioStream.size.totalBytes) {\n        end = audioStream.size.totalBytes;\n      }\n\n      final stream = ytExplode.videos.streams.get(audioStream, start, end);\n      return StreamAudioResponse(\n        sourceLength: audioStream.size.totalBytes,\n        contentLength: end - start,\n        offset: start,\n        stream: stream,\n        contentType: audioStream.codec.mimeType,\n      );\n    } catch (e) {\n      throw Exception('Failed to load audio: $e');\n    }\n  }\n}\n\nfinal YoutubeExplode ytExplode = YoutubeExplode();\n\n/// Starts a generic HTTP server that listens for requests to stream YouTube audio.\n///\n/// Clients must pass 'id' and 'quality' as URL query parameters.\n/// The server binds to a random available port.\n/// Returns the base URL for the streaming endpoint.\nFuture<String> createAudioStreamServer() async {\n  // Bind to a random port (port 0) on the loopback interface.\n  final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);\n\n  // Listen for requests and dispatch them to the handler\n  server.listen((HttpRequest request) {\n    // Pass only the request object to the handler\n    handleAudioRequest(request);\n  });\n\n  // Construct the base streaming URL\n  final host = server.address.host;\n  final port = server.port;\n  final url = 'http://$host:$port/audio';\n\n  debugPrint(\n    'Generic streaming server started on $url. Use ?id=...&quality=... to stream.',\n  );\n\n  // You would typically return the server instance here too, if you needed\n  // to close it later (e.g., return {'server': server, 'url': url})\n  return url;\n}\n\n// ----------------------------------------------------------------------------\n// HANDLER FUNCTION (Modified to read parameters from the request URI)\n// ----------------------------------------------------------------------------\n\nFuture<void> handleAudioRequest(HttpRequest request) async {\n  final response = request.response;\n\n  // 1. Check the path and extract parameters from URL query\n  if (request.uri.path != '/audio') {\n    response.statusCode = HttpStatus.notFound;\n    response.write('404 Not Found');\n    await response.close();\n    return;\n  }\n\n  final queryParams = request.uri.queryParameters;\n  final videoId = queryParams['id'];\n  final quality = queryParams['quality'] ?? 'high'; // Default to 'high'\n\n  if (videoId == null || videoId.isEmpty) {\n    response.statusCode = HttpStatus.badRequest;\n    response.write('Missing required query parameter: id');\n    await response.close();\n    return;\n  }\n\n  debugPrint('Processing request for video ID: $videoId (Quality: $quality)');\n\n  try {\n    // 2. Get the Stream Manifest and select the audio stream\n    final manifest = await ytExplode.videos.streamsClient.getManifest(\n      videoId,\n      requireWatchPage: true,\n      ytClients: [YoutubeApiClient.androidVr],\n    );\n\n    final supportedStreams = manifest.audioOnly.sortByBitrate();\n    final audioStreamInfo = quality == 'high'\n        ? supportedStreams.firstOrNull\n        : supportedStreams.lastOrNull;\n\n    if (audioStreamInfo == null) {\n      response.statusCode = HttpStatus.internalServerError;\n      response.write('No audio stream available for video $videoId.');\n      await response.close();\n      return;\n    }\n\n    final totalLength = audioStreamInfo.size.totalBytes;\n\n    // 3. Parse the client's 'Range' header (same logic)\n\n    (int start, int end)? parseRange(String rangeHeader, int totalLength) {\n      // Expected format: \"bytes=start-end\" or \"bytes=start-\"\n      if (!rangeHeader.startsWith('bytes=')) return null;\n\n      final parts = rangeHeader.substring(6).split('-');\n      if (parts.length != 2) return null;\n\n      final startStr = parts[0];\n      final endStr = parts[1];\n\n      final start = int.tryParse(startStr) ?? 0;\n\n      // If end is missing (e.g., \"bytes=1000-\"), it means until the end of the file\n      final end = endStr.isEmpty ? totalLength - 1 : int.tryParse(endStr);\n\n      if (end == null ||\n          end >= totalLength ||\n          start >= totalLength ||\n          start > end) {\n        return null; // Invalid range or past the end of the file\n      }\n\n      return (start, end);\n    }\n\n    int start = 0;\n    int end = totalLength - 1;\n    bool isPartial = false;\n\n    final rangeHeader = request.headers.value(HttpHeaders.rangeHeader);\n    if (rangeHeader != null) {\n      final range = parseRange(rangeHeader, totalLength);\n      if (range != null) {\n        start = range.$1;\n        end = range.$2;\n        isPartial = true;\n      }\n    }\n\n    // 4. Get the *actual* byte stream from YouTube\n    final stream = ytExplode.videos.streamsClient.get(\n      audioStreamInfo,\n      start,\n      end + 1,\n    );\n\n    // 5. Set the HTTP headers and pipe the stream (same logic)\n    final contentLength = end - start + 1;\n    final mimeType = audioStreamInfo.codec.mimeType;\n\n    response.statusCode = isPartial ? HttpStatus.partialContent : HttpStatus.ok;\n    response.headers.set(HttpHeaders.acceptRangesHeader, 'bytes');\n    response.headers.contentType = ContentType.parse(mimeType);\n    response.headers.contentLength = contentLength;\n    if (isPartial) {\n      response.headers.set(\n        HttpHeaders.contentRangeHeader,\n        'bytes $start-$end/$totalLength',\n      );\n    }\n    response.bufferOutput = false;\n\n    await response.addStream(stream);\n\n    await response.close();\n    debugPrint(\n      '[$videoId] Served ${isPartial ? 'partial' : 'full'} stream: bytes $start-$end',\n    );\n  } catch (e) {\n    debugPrint('Error serving audio for ID $videoId: $e');\n  }\n}\n"
  },
  {
    "path": "lib/themes/colors.dart",
    "content": "import 'package:flutter/material.dart';\n\nColor greyColor = Colors.grey.withAlpha(100);\nColor darkGreyColor = Colors.grey.withAlpha(70);\nconst MaterialColor primaryBlack = MaterialColor(\n  0xFF000000,\n  <int, Color>{\n    50: Color.fromRGBO(0, 0, 0, .1),\n    100: Color.fromRGBO(0, 0, 0, .2),\n    200: Color.fromRGBO(0, 0, 0, .3),\n    300: Color.fromRGBO(0, 0, 0, .4),\n    400: Color.fromRGBO(0, 0, 0, .5),\n    500: Color.fromRGBO(0, 0, 0, .6),\n    600: Color.fromRGBO(0, 0, 0, .7),\n    700: Color.fromRGBO(0, 0, 0, .8),\n    800: Color.fromRGBO(0, 0, 0, .9),\n    900: Color.fromRGBO(0, 0, 0, 1),\n  },\n);\n\nconst MaterialColor primaryWhite = MaterialColor(\n  0xFFFFFFFF,\n  <int, Color>{\n    50: Color.fromRGBO(255, 255, 255, .1),\n    100: Color.fromRGBO(255, 255, 255, .2),\n    200: Color.fromRGBO(255, 255, 255, .3),\n    300: Color.fromRGBO(255, 255, 255, .4),\n    400: Color.fromRGBO(255, 255, 255, .5),\n    500: Color.fromRGBO(255, 255, 255, .6),\n    600: Color.fromRGBO(255, 255, 255, .7),\n    700: Color.fromRGBO(255, 255, 255, .8),\n    800: Color.fromRGBO(255, 255, 255, .9),\n    900: Color.fromRGBO(255, 255, 255, 1),\n  },\n);\n"
  },
  {
    "path": "lib/themes/dark.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:google_fonts/google_fonts.dart';\n\n// ColorScheme.fromSeed(\n//             seedColor: accentColor,\n//             brightness: darkScheme.brightness,\n//             primary: accentColor,\n//             primaryContainer: accentColor,\n//             onPrimaryContainer: Colors.black,\n//             surface: accentColor.withAlpha(10),\n//           )\nfinal defaultFontStyle = GoogleFonts.poppins();\nColorScheme darkScheme = const ColorScheme.dark();\nThemeData darkTheme({required ColorScheme colorScheme}) {\n  return ThemeData.dark().copyWith(\n    colorScheme: colorScheme,\n    scaffoldBackgroundColor:\n        Platform.isWindows ? Colors.transparent : colorScheme.surface,\n    primaryColor: colorScheme.primary,\n    appBarTheme: AppBarTheme(\n      centerTitle: true,\n      backgroundColor: Colors.transparent,\n      surfaceTintColor: Platform.isWindows ? Colors.transparent : null,\n      systemOverlayStyle: const SystemUiOverlayStyle(\n        statusBarBrightness: Brightness.dark,\n        statusBarColor: Colors.transparent,\n        statusBarIconBrightness: Brightness.light,\n        systemNavigationBarColor: Colors.transparent,\n      ),\n    ),\n    textTheme: TextTheme(\n      headlineLarge: defaultFontStyle.copyWith(color: Colors.white),\n      headlineMedium: defaultFontStyle.copyWith(color: Colors.white),\n      headlineSmall: defaultFontStyle.copyWith(color: Colors.white),\n      bodyLarge: defaultFontStyle.copyWith(color: Colors.white),\n      bodyMedium: defaultFontStyle.copyWith(color: Colors.white),\n      bodySmall: defaultFontStyle.copyWith(color: Colors.white),\n      displayLarge: defaultFontStyle.copyWith(color: Colors.white),\n      displayMedium: defaultFontStyle.copyWith(color: Colors.white),\n      displaySmall: defaultFontStyle.copyWith(color: Colors.white),\n      titleLarge: defaultFontStyle.copyWith(color: Colors.white),\n      titleMedium: defaultFontStyle.copyWith(color: Colors.white),\n      titleSmall: defaultFontStyle.copyWith(color: Colors.white),\n      labelLarge: defaultFontStyle.copyWith(color: Colors.white),\n      labelMedium: defaultFontStyle.copyWith(color: Colors.white),\n      labelSmall: defaultFontStyle.copyWith(color: Colors.white),\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/themes/light.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:google_fonts/google_fonts.dart';\n\nfinal defaultFontStyle = GoogleFonts.poppins();\n\nThemeData lightTheme({required ColorScheme colorScheme}) {\n  return ThemeData.light().copyWith(\n    colorScheme: colorScheme,\n    primaryColor: colorScheme.primary,\n    scaffoldBackgroundColor: colorScheme.surface,\n    appBarTheme: AppBarTheme(\n      backgroundColor: Colors.transparent,\n      centerTitle: true,\n      systemOverlayStyle: const SystemUiOverlayStyle(\n        statusBarBrightness: Brightness.light,\n        statusBarColor: Colors.transparent,\n        statusBarIconBrightness: Brightness.dark,\n        systemNavigationBarColor: Colors.transparent,\n      ),\n    ),\n    textTheme: TextTheme(\n      headlineLarge: defaultFontStyle.copyWith(color: Colors.black),\n      headlineMedium: defaultFontStyle.copyWith(color: Colors.black),\n      headlineSmall: defaultFontStyle.copyWith(color: Colors.black),\n      bodyLarge: defaultFontStyle.copyWith(color: Colors.black),\n      bodyMedium: defaultFontStyle.copyWith(color: Colors.black),\n      bodySmall: defaultFontStyle.copyWith(color: Colors.black),\n      displayLarge: defaultFontStyle.copyWith(color: Colors.black),\n      displayMedium: defaultFontStyle.copyWith(color: Colors.black),\n      displaySmall: defaultFontStyle.copyWith(color: Colors.black),\n      titleLarge: defaultFontStyle.copyWith(color: Colors.black),\n      titleMedium: defaultFontStyle.copyWith(color: Colors.black),\n      titleSmall: defaultFontStyle.copyWith(color: Colors.black),\n      labelLarge: defaultFontStyle.copyWith(color: Colors.black),\n      labelMedium: defaultFontStyle.copyWith(color: Colors.black),\n      labelSmall: defaultFontStyle.copyWith(color: Colors.black),\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/themes/text_styles.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:google_fonts/google_fonts.dart';\n\nfinal defaultFontStyle = GoogleFonts.poppins();\nTextStyle bigTextStyle(BuildContext context,\n    {double opacity = 1, bool bold = true}) {\n  return defaultFontStyle.copyWith(\n    fontSize: 30,\n    fontWeight: bold ? FontWeight.w900 : FontWeight.normal,\n    color: Theme.of(context).textTheme.bodyMedium?.color,\n  );\n}\n\nTextStyle mediumTextStyle(BuildContext context,\n    {double opacity = 1, bool bold = true}) {\n  return defaultFontStyle.copyWith(\n    fontSize: 24,\n    fontWeight: bold ? FontWeight.w900 : FontWeight.normal,\n    color: Theme.of(context).textTheme.bodyMedium?.color,\n  );\n}\n\nTextStyle textStyle(BuildContext context,\n    {double opacity = 1, bool bold = true}) {\n  return defaultFontStyle.copyWith(\n    fontSize: 19,\n    fontWeight: bold ? FontWeight.w600 : FontWeight.normal,\n    color: Theme.of(context).textTheme.bodyMedium?.color,\n  );\n}\n\nTextStyle subtitleTextStyle(BuildContext context,\n    {double opacity = 1, bool bold = false}) {\n  return defaultFontStyle.copyWith(\n    fontSize: 15,\n    fontWeight: bold ? FontWeight.w600 : FontWeight.normal,\n    color: Colors.grey.withAlpha(200),\n  );\n}\n\nTextStyle smallTextStyle(BuildContext context,\n    {double opacity = 1, bool bold = false}) {\n  return defaultFontStyle.copyWith(\n    fontSize: 13,\n    fontWeight: bold ? FontWeight.w600 : FontWeight.normal,\n    color: Theme.of(context).textTheme.bodyMedium?.color,\n  );\n}\n\nTextStyle tinyTextStyle(BuildContext context,\n    {double opacity = 1, bool bold = false}) {\n  return defaultFontStyle.copyWith(\n    fontSize: 11,\n    fontWeight: bold ? FontWeight.w600 : FontWeight.normal,\n    color: Theme.of(context).textTheme.bodyMedium?.color,\n  );\n}\n\nTextStyle customTextStyle(BuildContext context,\n    {double opacity = 1, bool bold = false, double? fontSize}) {\n  return defaultFontStyle.copyWith(\n      fontSize: fontSize,\n      fontWeight: bold ? FontWeight.w600 : FontWeight.normal);\n}\n"
  },
  {
    "path": "lib/themes/theme.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\n\nimport 'typography.dart';\n\nclass AppTheme {\n  static ThemeData light({Color? primary}) {\n    final colorScheme = ColorScheme.fromSeed(\n            seedColor: primary??Colors.red,\n            brightness: Brightness.light,\n          );\n\n    return ThemeData.light(useMaterial3: true).copyWith(\n      scaffoldBackgroundColor: colorScheme.surface,\n      textTheme: appTextTheme(ThemeData.light().textTheme),\n      colorScheme: colorScheme,\n      visualDensity: VisualDensity.adaptivePlatformDensity,\n      appBarTheme: AppBarTheme(\n        systemOverlayStyle: const SystemUiOverlayStyle(\n          statusBarBrightness: Brightness.light,\n          statusBarColor: Colors.transparent,\n          statusBarIconBrightness: Brightness.dark,\n          systemNavigationBarColor: Colors.transparent,\n        ),\n      ),\n      pageTransitionsTheme: PageTransitionsTheme(\n        builders: Map<TargetPlatform, PageTransitionsBuilder>.fromIterable(\n          TargetPlatform.values,\n          value: (_) => const FadeForwardsPageTransitionsBuilder(),\n        ),\n      ),\n    );\n  }\n\n  static ThemeData dark({Color? primary, bool isPureBlack = false}) {\n    final colorScheme = ColorScheme.fromSeed(\n            seedColor: primary??Colors.deepPurpleAccent,\n            brightness: Brightness.dark,\n          );\n    return ThemeData.dark(useMaterial3: true).copyWith(\n      scaffoldBackgroundColor: isPureBlack\n          ? Colors.black\n          : colorScheme.surface,\n      textTheme: appTextTheme(ThemeData.dark().textTheme),\n      colorScheme: colorScheme,\n      visualDensity: VisualDensity.adaptivePlatformDensity,\n      appBarTheme: AppBarTheme(\n        backgroundColor: isPureBlack ? Colors.black : null,\n        surfaceTintColor: isPureBlack ? Colors.black : null,\n        systemOverlayStyle: const SystemUiOverlayStyle(\n          statusBarBrightness: Brightness.dark,\n          statusBarColor: Colors.transparent,\n          statusBarIconBrightness: Brightness.light,\n          systemNavigationBarColor: Colors.transparent,\n        ),\n      ),\n      navigationBarTheme: NavigationBarThemeData(\n        backgroundColor: isPureBlack ? Colors.black : null,\n      ),\n      pageTransitionsTheme: PageTransitionsTheme(\n        builders: Map<TargetPlatform, PageTransitionsBuilder>.fromIterable(\n          TargetPlatform.values,\n          value: (_) => const FadeForwardsPageTransitionsBuilder(),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/themes/typography.dart",
    "content": "import 'package:flutter/material.dart';\n\nTextTheme appTextTheme(TextTheme? textTheme) {\n  textTheme ??= ThemeData.light().textTheme;\n\n  return TextTheme(\n    displayLarge: TextStyle(\n      color: textTheme.displayLarge?.color,\n      fontWeight: FontWeight.normal,\n      fontSize: 57,\n      height: 64 / 57,\n      letterSpacing: -0.25,\n    ),\n    displayMedium: TextStyle(\n      color: textTheme.displayMedium?.color,\n      fontWeight: FontWeight.normal,\n      fontSize: 45,\n      height: 52 / 45,\n      letterSpacing: 0,\n    ),\n    displaySmall: TextStyle(\n      color: textTheme.displaySmall?.color,\n      fontWeight: FontWeight.normal,\n      fontSize: 36,\n      height: 44 / 36,\n      letterSpacing: 0,\n    ),\n    headlineLarge: TextStyle(\n      color: textTheme.headlineLarge?.color,\n      fontWeight: FontWeight.normal,\n      fontSize: 32,\n      height: 40 / 32,\n      letterSpacing: 0,\n    ),\n    headlineMedium: TextStyle(\n      color: textTheme.headlineMedium?.color,\n      fontWeight: FontWeight.normal,\n      fontSize: 28,\n      height: 36 / 28,\n      letterSpacing: 0,\n    ),\n    headlineSmall: TextStyle(\n      color: textTheme.headlineSmall?.color,\n      fontWeight: FontWeight.normal,\n      fontSize: 24,\n      height: 32 / 24,\n      letterSpacing: 0,\n    ),\n    titleLarge: TextStyle(\n      color: textTheme.titleLarge?.color,\n      fontWeight: FontWeight.normal, // M3 uses normal, M2 used w500\n      fontSize: 22,\n      height: 28 / 22,\n      letterSpacing: 0,\n    ),\n    titleMedium: TextStyle(\n      color: textTheme.titleMedium?.color,\n      fontWeight: FontWeight.w500,\n      fontSize: 16,\n      height: 24 / 16,\n      letterSpacing: 0.15,\n    ),\n    titleSmall: TextStyle(\n      color: textTheme.titleSmall?.color,\n      fontWeight: FontWeight.w500,\n      fontSize: 14,\n      height: 20 / 14,\n      letterSpacing: 0.1,\n    ),\n    bodyLarge: TextStyle(\n      color: textTheme.bodyLarge?.color,\n      fontWeight: FontWeight.normal,\n      fontSize: 16,\n      height: 24 / 16,\n      letterSpacing: 0.5, // M3 uses 0.5, M2 used 0.15\n    ),\n    bodyMedium: TextStyle(\n      color: textTheme.bodyMedium?.color,\n      fontWeight: FontWeight.normal,\n      fontSize: 14,\n      height: 20 / 14,\n      letterSpacing: 0.25,\n    ),\n    bodySmall: TextStyle(\n      color: textTheme.bodySmall?.color,\n      fontWeight: FontWeight.normal,\n      fontSize: 12,\n      height: 16 / 12,\n      letterSpacing: 0.4,\n    ),\n    labelLarge: TextStyle(\n      color: textTheme.labelLarge?.color?.withAlpha(200),\n      fontWeight: FontWeight.w600,\n      fontSize: 14,\n      height: 20 / 14,\n      letterSpacing: 0.1,\n    ),\n    labelMedium: TextStyle(\n      color: textTheme.labelMedium?.color?.withAlpha(200),\n      fontWeight: FontWeight.w500,\n      fontSize: 12,\n      height: 16 / 12,\n      letterSpacing: 0.5,\n    ),\n    labelSmall: TextStyle(\n      color: textTheme.labelSmall?.color?.withAlpha(200),\n      fontWeight: FontWeight.w500,\n      fontSize: 11,\n      height: 16 / 11,\n      letterSpacing: 0.5,\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/adaptive_widgets.dart",
    "content": "export 'appbar.dart';\nexport 'buttons.dart';\nexport 'card.dart';\nexport 'dropdown_button.dart';\nexport 'icons.dart';\nexport 'inkwell.dart';\nexport 'listtile.dart';\nexport 'progress_ring.dart';\nexport 'scaffold.dart';\nexport 'slider.dart';\nexport 'switch.dart';\nexport 'text_field.dart';\nexport 'theme.dart';\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/appbar.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter/material.dart';\n\nclass AdaptiveAppBar extends StatelessWidget implements PreferredSizeWidget {\n  const AdaptiveAppBar({\n    super.key,\n    this.leading,\n    this.title,\n    this.centerTitle,\n    this.automaticallyImplyLeading = true,\n    this.bottom,\n    this.actions,\n  });\n\n  ///The widget displayed before the [title]\n  ///\n  ///Usually an [Icon] widget.\n  final Widget? leading;\n\n  /// The title of this [AdaptiveAppBar].\n  final Widget? title;\n\n  /// Whether the title should be centered.\n  ///\n  /// Works only on android.\n  final bool? centerTitle;\n\n  /// Controls whether we should try to imply the leading widget if null.\n  final bool automaticallyImplyLeading;\n\n  /// This widget appears across the bottom of the [AdaptiveAppBar].\n  final PreferredSizeWidget? bottom;\n\n  /// A list of Widgets to display in a row after the [title] widget.\n  final List<Widget>? actions;\n\n  @override\n  Widget build(BuildContext context) {\n    return AppBar(\n      leading: leading,\n      title: title,\n      centerTitle: centerTitle,\n      automaticallyImplyLeading: automaticallyImplyLeading,\n      bottom: bottom,\n      actions: actions,\n    );\n  }\n\n  @override\n  Size get preferredSize {\n    if (Platform.isWindows) {\n      return Size.fromHeight(50.0 + (bottom == null ? 0 : kTextTabBarHeight));\n    } else {\n      return Size.fromHeight(\n          kToolbarHeight + (bottom == null ? 0 : kTextTabBarHeight));\n    }\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/buttons.dart",
    "content": "import 'package:flutter/material.dart';\n\nimport 'icons.dart';\n\nclass AdaptiveButton extends StatelessWidget {\n  final Widget child;\n  final void Function()? onPressed;\n  final Color? color;\n  const AdaptiveButton(\n      {super.key, required this.child, required this.onPressed, this.color});\n\n  @override\n  Widget build(BuildContext context) {\n    return TextButton(\n      key: key,\n      onPressed: onPressed,\n      child: child,\n    );\n  }\n}\n\nclass AdaptiveFilledButton extends StatelessWidget {\n  final Widget child;\n  final void Function()? onPressed;\n  final Color? color;\n  final OutlinedBorder? shape;\n  final EdgeInsetsGeometry? padding;\n  const AdaptiveFilledButton({\n    super.key,\n    required this.child,\n    required this.onPressed,\n    this.color,\n    this.shape,\n    this.padding,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return FilledButton(\n      key: key,\n      onPressed: onPressed,\n      style: ButtonStyle(\n        backgroundColor: WidgetStateProperty.all(color),\n        shape: WidgetStateProperty.all(shape),\n        padding: WidgetStateProperty.all(padding),\n      ),\n      child: child,\n    );\n  }\n}\n\nclass AdaptiveOutlinedButton extends StatelessWidget {\n  final Widget child;\n  final void Function()? onPressed;\n  final Color? color;\n  const AdaptiveOutlinedButton(\n      {super.key, required this.child, required this.onPressed, this.color});\n\n  @override\n  Widget build(BuildContext context) {\n    return OutlinedButton(\n      key: key,\n      onPressed: onPressed,\n      style: ButtonStyle(\n          backgroundColor:\n              color != null ? WidgetStateProperty.all(color) : null,\n          foregroundColor:\n              WidgetStateProperty.all(Theme.of(context).colorScheme.primary)),\n      child: child,\n    );\n  }\n}\n\nclass AdaptiveIconButton extends StatelessWidget {\n  const AdaptiveIconButton({\n    super.key,\n    required this.icon,\n    required this.onPressed,\n    this.isSelected,\n    this.color,\n  });\n  final Widget icon;\n  final void Function()? onPressed;\n  final bool? isSelected;\n  final Color? color;\n\n  @override\n  Widget build(BuildContext context) {\n    return IconButton(\n      key: key,\n      icon: icon,\n      onPressed: onPressed,\n      isSelected: isSelected,\n      color: color,\n    );\n  }\n}\n\nclass AdaptiveBackButton extends StatelessWidget {\n  const AdaptiveBackButton({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return AdaptiveIconButton(\n      icon: Icon(AdaptiveIcons.back),\n      onPressed: () => Navigator.of(context).maybePop(),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/card.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass Adaptivecard extends StatelessWidget {\n  const Adaptivecard({\n    super.key,\n    required this.child,\n    this.backgroundColor,\n    this.borderRadius,\n    this.margin,\n    this.padding,\n    this.elevation,\n  });\n\n  final Widget child;\n  final Color? backgroundColor;\n  final BorderRadius? borderRadius;\n  final EdgeInsetsGeometry? margin;\n  final EdgeInsetsGeometry? padding;\n  final double? elevation;\n\n  @override\n  Widget build(BuildContext context) {\n    return Card(\n      margin: margin ?? const EdgeInsets.all(1),\n      color: backgroundColor,\n      elevation: elevation,\n      shape: RoundedRectangleBorder(\n          borderRadius:\n              borderRadius ?? const BorderRadius.all(Radius.circular(8.0))),\n      child: Padding(\n        padding: padding ?? const EdgeInsets.all(12.0),\n        child: child,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/dropdown_button.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass AdaptiveDropdownButton<T> extends StatelessWidget {\n  final T? value;\n  final List<AdaptiveDropdownMenuItem<T>>? items;\n  final TextStyle? style;\n  final void Function(T?)? onChanged;\n  const AdaptiveDropdownButton({\n    super.key,\n    this.value,\n    this.items,\n    this.style,\n    this.onChanged,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return DropdownButton<T>(\n      style: style,\n      underline: const SizedBox(),\n      value: value,\n      isDense: true,\n      borderRadius: BorderRadius.circular(8),\n      alignment: AlignmentDirectional.centerEnd,\n      items: items\n          ?.map(\n            (item) => DropdownMenuItem(\n              value: item.value,\n              enabled: item.enabled,\n              onTap: item.onTap,\n              child: item.child,\n            ),\n          )\n          .toList(),\n      onChanged: onChanged,\n    );\n  }\n}\n\nclass AdaptiveDropdownMenuItem<T> {\n  final Widget child;\n  final T? value;\n  final bool enabled;\n  final void Function()? onTap;\n  AdaptiveDropdownMenuItem(\n      {required this.child, this.value, this.enabled = true, this.onTap});\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/icons.dart",
    "content": "// ignore_for_file: non_constant_identifier_names\n\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\n\nabstract final class AdaptiveIcons {\n  static IconData home = Icons.home;\n  static IconData home_filled = Icons.home_filled;\n  static IconData back = Icons.arrow_back;\n  static IconData chevron_left = Icons.chevron_left;\n  static IconData chevron_right = Icons.chevron_right;\n  static IconData chevron_down = CupertinoIcons.chevron_down;\n  static IconData search = Icons.search;\n  static IconData download = Icons.download;\n  static IconData downloading = Icons.downloading;\n  static IconData sync = Icons.sync;\n  static IconData create = Icons.edit;\n  static IconData add = Icons.add;\n  static IconData import = Icons.import_export_outlined;\n  static IconData heart = CupertinoIcons.heart;\n  static IconData heart_fill = CupertinoIcons.heart_fill;\n  static IconData lyrics = Icons.lyrics;\n  static IconData queue = Icons.queue_music;\n  static IconData play = Icons.play_arrow;\n\n  static IconData pause = Icons.pause;\n\n  static IconData skip_previous = Icons.skip_previous;\n  static IconData skip_next = Icons.skip_next;\n  static IconData repeat = Icons.repeat;\n  static IconData repeat_one = Icons.repeat_one;\n  static IconData repeat_all = Icons.repeat;\n  static IconData more_vertical = Icons.more_vert;\n  static IconData delete = Icons.delete;\n  static IconData volume(double range) {\n    if (range == 0) {\n      return Icons.volume_off;\n    } else if (range < .2) {\n      return Icons.volume_down;\n    } else if (range < .6) {\n      return Icons.volume_down;\n    }\n    return Icons.volume_up;\n  }\n\n  static IconData library_add = Icons.library_add;\n  static IconData library_add_check = Icons.library_add_check;\n  static IconData playlist_play = Icons.playlist_play;\n  static IconData queue_add = Icons.queue;\n  static IconData radio = Icons.radar;\n  static IconData person = Icons.person;\n  static IconData people = Icons.people;\n  static IconData album = Icons.album;\n  static IconData equalizer = Icons.equalizer;\n  static IconData timer = Icons.timer;\n  static IconData share = Icons.share;\n  static IconData wifi_off_rounded = Icons.wifi_off_rounded;\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/inkwell.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter/material.dart';\n\nimport 'no_splash_factory.dart';\n\nclass AdaptiveInkWell extends StatelessWidget {\n  final Widget? child;\n  final EdgeInsetsGeometry? padding;\n  final EdgeInsetsGeometry? margin;\n  final bool enabled;\n  final GestureTapCallback? onTap;\n  final GestureLongPressCallback? onLongPress;\n  final GestureLongPressCallback? onDoubleTap;\n  final GestureLongPressCallback? onSecondaryTap;\n  final BorderRadius? borderRadius;\n  final bool selected;\n\n  const AdaptiveInkWell({\n    super.key,\n    this.child,\n    this.padding,\n    this.margin,\n    this.enabled = true,\n    this.onTap,\n    this.onLongPress,\n    this.onDoubleTap,\n    this.onSecondaryTap,\n    this.borderRadius,\n    this.selected = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      margin: margin,\n      child: Material(\n        color: Colors.transparent,\n        child: InkWell(\n          onTap: enabled ? onTap : null,\n          onLongPress: enabled ? onLongPress : null,\n          onDoubleTap: enabled ? onDoubleTap : null,\n          onSecondaryTap: enabled ? onSecondaryTap : null,\n          borderRadius: borderRadius ?? BorderRadius.circular(4),\n          splashFactory: (Platform.isWindows) ? const NoSplashFactory() : null,\n          child: Padding(\n            padding: padding ?? const EdgeInsets.all(0),\n            child: child,\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/listtile.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter/material.dart';\n\nimport '../../themes/text_styles.dart';\nimport 'no_splash_factory.dart';\n\nclass AdaptiveListTile extends StatelessWidget {\n  final Widget? leading;\n  final Widget? title;\n  final Widget? subtitle;\n  final Widget? trailing;\n  final Widget? description;\n  final bool isThreeLine;\n  final bool dense;\n  final EdgeInsetsGeometry? contentPadding;\n  final EdgeInsetsGeometry? margin;\n  final bool enabled;\n  final GestureTapCallback? onTap;\n  final GestureLongPressCallback? onLongPress;\n  final GestureLongPressCallback? onDoubleTap;\n  final GestureLongPressCallback? onSecondaryTap;\n  final bool selected;\n  final Color? backgroundColor;\n\n  const AdaptiveListTile({\n    super.key,\n    this.leading,\n    this.title,\n    this.subtitle,\n    this.trailing,\n    this.description,\n    this.isThreeLine = false,\n    this.dense = false,\n    this.contentPadding,\n    this.margin,\n    this.enabled = true,\n    this.onTap,\n    this.onLongPress,\n    this.onDoubleTap,\n    this.onSecondaryTap,\n    this.selected = false,\n    this.backgroundColor,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final ThemeData theme = Theme.of(context);\n    final TextStyle titleStyle = mediumTextStyle(context, bold: false).copyWith(\n      fontSize: dense ? 14.0 : 16.0,\n    );\n\n    final TextStyle subtitleStyle = subtitleTextStyle(context).copyWith(\n      fontSize: dense ? 12.0 : 14.0,\n    );\n\n    final TextStyle descriptionStyle = smallTextStyle(context).copyWith(\n      fontSize: dense ? 12.0 : 14.0,\n    );\n\n    return Container(\n      margin: margin,\n      child: Material(\n        color: Colors.transparent,\n        child: InkWell(\n          onTap: enabled ? onTap : null,\n          onLongPress: enabled ? onLongPress : null,\n          onDoubleTap: enabled ? onDoubleTap : null,\n          onSecondaryTap: enabled ? onSecondaryTap : null,\n          borderRadius: BorderRadius.circular(4),\n          splashFactory: (Platform.isWindows) ? const NoSplashFactory() : null,\n          child: ClipRRect(\n            borderRadius: BorderRadius.circular(4),\n            child: Container(\n              padding: contentPadding ??\n                  const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),\n              decoration: BoxDecoration(\n                color: selected\n                    ? theme.colorScheme.primary.withValues(alpha:0.1)\n                    : backgroundColor,\n              ),\n              child: Column(\n                children: [\n                  Row(\n                    children: [\n                      if (leading != null) ...[\n                        IconTheme(\n                            data: Theme.of(context)\n                                .iconTheme\n                                .copyWith(size: dense ? 24 : 28),\n                            child: leading!),\n                        const SizedBox(width: 16.0),\n                      ],\n                      Expanded(\n                        child: Column(\n                          mainAxisAlignment: MainAxisAlignment.center,\n                          mainAxisSize: MainAxisSize.max,\n                          crossAxisAlignment: CrossAxisAlignment.start,\n                          children: [\n                            if (title != null) ...[\n                              DefaultTextStyle(\n                                style: titleStyle,\n                                child: title!,\n                              ),\n                              if (subtitle != null || isThreeLine)\n                                SizedBox(height: dense ? 2.0 : 4.0),\n                            ],\n                            if (subtitle != null || isThreeLine) ...[\n                              DefaultTextStyle(\n                                style: subtitleStyle,\n                                child: subtitle ?? Container(),\n                              ),\n                            ],\n                          ],\n                        ),\n                      ),\n                      if (trailing != null) ...[\n                        const SizedBox(width: 16.0),\n                        trailing!,\n                      ],\n                    ],\n                  ),\n                  if (subtitle != null || isThreeLine)\n                    SizedBox(height: dense ? 2.0 : 4.0),\n                  if (description != null) ...[\n                    DefaultTextStyle(\n                      style: descriptionStyle,\n                      child: description!,\n                    ),\n                  ],\n                  if (description != null) const Divider(),\n                ],\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/no_splash_factory.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass NoSplashFactory extends InteractiveInkFeatureFactory {\n  const NoSplashFactory();\n\n  @override\n  InteractiveInkFeature create({\n    required MaterialInkController controller,\n    required RenderBox referenceBox,\n    required Offset position,\n    required Color color,\n    required TextDirection textDirection,\n    bool containedInkWell = false,\n    RectCallback? rectCallback,\n    BorderRadius? borderRadius,\n    ShapeBorder? customBorder,\n    double? radius,\n    onRemoved,\n  }) {\n    return NoSplash(\n      controller: controller,\n      referenceBox: referenceBox,\n      color: color,\n    );\n  }\n}\n\nclass NoSplash extends InteractiveInkFeature {\n  @override\n  final Color color; // Color for onTap effect\n\n  NoSplash({\n    required super.controller,\n    required super.referenceBox,\n    required this.color,\n  }) : super(color: color);\n\n  @override\n  void paintFeature(Canvas canvas, Matrix4 transform) {\n    final Paint paint = Paint()\n      ..color = color.withAlpha(50); // Adjust opacity as needed\n    canvas.drawRect(Offset.zero & referenceBox.size, paint);\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/progress_ring.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass AdaptiveProgressRing extends StatelessWidget {\n  final double? value;\n  final double strokeWidth;\n  final Color? backgroundColor;\n  final Color? color;\n\n  /// Creates progress ring.\n  ///\n  /// [value], if non-null, must be in the range of 0 to 100\n  ///\n  /// [strokeWidth] must be equal or greater than 0\n  const AdaptiveProgressRing({\n    super.key,\n    this.value,\n    this.strokeWidth = 4.5,\n    this.backgroundColor,\n    this.color,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return CircularProgressIndicator(\n      value: value,\n      strokeWidth: strokeWidth,\n      backgroundColor: backgroundColor,\n      color: color,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/scaffold.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass AdaptiveScaffold extends StatelessWidget {\n  /// Creates a new [AdaptiveScaffold].\n  const AdaptiveScaffold({this.body, this.appBar, super.key});\n\n  /// The primary content of the [AdaptiveScaffold].\n  final Widget? body;\n\n  /// An app bar to display at the top of the [AdaptiveScaffold].\n  final PreferredSizeWidget? appBar;\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      key: key,\n      appBar: appBar,\n      body: body,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/slider.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass AdaptiveSlider extends StatelessWidget {\n  final double value;\n  final bool disabled;\n  final double min;\n  final double max;\n  final int? divisions;\n  final String? label;\n  final bool vertical;\n  final void Function(double)? onChanged;\n  const AdaptiveSlider(\n      {required this.value,\n      this.disabled = false,\n      this.onChanged,\n      this.min = 0,\n      this.max = 1,\n      this.divisions,\n      this.label,\n      this.vertical = false,\n      super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return RotatedBox(\n      quarterTurns: vertical ? 3 : 0,\n      child: Slider(\n        value: value,\n        min: min,\n        max: max,\n        label: label,\n        divisions: divisions,\n        onChanged: disabled ? null : onChanged,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/switch.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass AdaptiveSwitch extends StatelessWidget {\n  final bool value;\n  final void Function(bool)? onChanged;\n  const AdaptiveSwitch({super.key, required this.value, this.onChanged});\n\n  @override\n  Widget build(BuildContext context) {\n    return Switch(value: value, onChanged: onChanged);\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/text_field.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass AdaptiveTextField extends StatelessWidget {\n  final TextEditingController? controller;\n  final void Function(String)? onChanged;\n  final void Function(String)? onSubmitted;\n  final void Function()? onTap;\n  final FocusNode? focusNode;\n  final bool readOnly;\n  final Color? fillColor;\n  final EdgeInsetsGeometry? contentPadding;\n  final TextInputType? keyboardType;\n  final String? hintText;\n  final Widget? prefix;\n  final Widget? suffix;\n  final bool autofocus;\n  final int? maxLines;\n  final TextInputAction? textInputAction;\n  final BorderRadius borderRadius;\n  final double borderWidth;\n  const AdaptiveTextField({\n    super.key,\n    this.controller,\n    this.onTap,\n    this.contentPadding,\n    this.fillColor,\n    this.focusNode,\n    this.hintText,\n    this.keyboardType,\n    this.onChanged,\n    this.onSubmitted,\n    this.prefix,\n    this.suffix,\n    this.textInputAction,\n    this.readOnly = false,\n    this.autofocus = false,\n    this.maxLines = 1,\n    this.borderRadius = const BorderRadius.all(Radius.circular(4.0)),\n    this.borderWidth = 0,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return TextField(\n      key: key,\n      controller: controller,\n      onChanged: onChanged,\n      onSubmitted: onSubmitted,\n      onTap: onTap,\n      focusNode: focusNode,\n      readOnly: readOnly,\n      keyboardType: keyboardType,\n      autofocus: autofocus,\n      maxLines: maxLines,\n      textInputAction: textInputAction,\n      decoration: InputDecoration(\n        fillColor: fillColor,\n        filled: fillColor != null,\n        contentPadding: contentPadding,\n        hintText: hintText,\n        prefixIcon: prefix,\n        suffixIcon: suffix,\n        border: OutlineInputBorder(\n          borderSide: borderWidth > 0\n              ? BorderSide(width: borderWidth)\n              : BorderSide.none,\n          borderRadius: borderRadius,\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/adaptive_widgets/theme.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass AdaptiveTheme {\n  static ThemeData getMaterialTheme(BuildContext context) {\n    return Theme.of(context);\n  }\n\n  static AdaptiveThemeData of(BuildContext context) {\n    AdaptiveThemeData adaptiveThemeData;\n    final materialTheme = Theme.of(context);\n    adaptiveThemeData = AdaptiveThemeData(\n      primaryColor: materialTheme.colorScheme.primary,\n      inactiveBackgroundColor: materialTheme.scaffoldBackgroundColor,\n    );\n    return adaptiveThemeData;\n  }\n}\n\nclass AdaptiveThemeData {\n  Color primaryColor;\n  Color inactiveBackgroundColor;\n  AdaptiveThemeData(\n      {required this.primaryColor, required this.inactiveBackgroundColor});\n}\n"
  },
  {
    "path": "lib/utils/add_history.dart",
    "content": "import 'package:get_it/get_it.dart';\nimport 'package:yt_music/ytmusic.dart';\n\nimport '../services/download_manager.dart';\nimport '../services/history_manager.dart';\nimport '../services/settings_manager.dart';\n\nFuture<void> addHistory(Map song) async {\n  await GetIt.I<HistoryManager>().songs.add(song);\n  final downloadSong = GetIt.I<DownloadManager>().getDownload(song['videoId']);\n  if (GetIt.I<SettingsManager>().personalisedContent &&\n      (downloadSong == null || downloadSong['status'] != 'DOWNLOADED')) {\n    GetIt.I<YTMusic>().addYoutubeHistory(song['videoId']);\n  }\n}\n"
  },
  {
    "path": "lib/utils/bottom_modals.dart",
    "content": "import 'dart:io';\nimport 'dart:math';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:duration_picker/duration_picker.dart';\nimport 'package:fluentui_system_icons/fluentui_system_icons.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_colorpicker/flutter_colorpicker.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/screens/settings/player/equalizer/equalizer_page.dart';\nimport 'package:provider/provider.dart';\nimport 'package:share_plus/share_plus.dart';\n\nimport 'playlist_icon.dart';\nimport 'playlist_icons.dart';\nimport 'playlist_icon_widget.dart';\nimport '../generated/l10n.dart';\nimport '../screens/settings/widgets/color_icon.dart';\nimport '../services/bottom_message.dart';\nimport '../services/download_manager.dart';\nimport '../services/favourites_manager.dart';\nimport '../services/library.dart';\nimport '../services/media_player.dart';\nimport '../services/settings_manager.dart';\nimport '../utils/text_controller_builder.dart';\nimport '../utils/playlist_thumbnail.dart';\nimport '../themes/colors.dart';\nimport '../themes/text_styles.dart';\nimport 'adaptive_widgets/adaptive_widgets.dart';\nimport 'format_duration.dart';\nimport '../utils/extensions.dart';\n\nclass Modals {\n  static Future showCenterLoadingModal(BuildContext context, {String? title}) {\n    return showDialog(\n      context: context,\n      useRootNavigator: false,\n      builder: (context) {\n        return AlertDialog(\n          title: Text(title ?? S.of(context).Progress),\n          content: const Row(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: [CircularProgressIndicator()],\n          ),\n        );\n      },\n    );\n  }\n\n  // static Future showUpdateDialog(\n  //         BuildContext context, UpdateInfo? updateInfo) =>\n  //     showDialog(\n  //       context: context,\n  //       useRootNavigator: false,\n  //       builder: (context) {\n  //         return _updateDialog(context, updateInfo);\n  //       },\n  //     );\n  static Future<String?> showTextField(\n    BuildContext context, {\n    String? title,\n    String? hintText,\n    String? doneText,\n  }) {\n    return showModalBottomSheet<String?>(\n      context: context,\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      isScrollControlled: true,\n      useSafeArea: true,\n      builder: (context) => _textFieldBottomModal(\n        context,\n        title: title,\n        hintText: hintText,\n        doneText: doneText,\n      ),\n    );\n  }\n\n  static Future<T?> showSelection<T>(\n    BuildContext context,\n    List<SelectionItem> items,\n  ) {\n    return showModalBottomSheet<T>(\n      context: context,\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      isScrollControlled: true,\n      useSafeArea: true,\n      builder: (context) => _showSelection(context, items),\n    );\n  }\n\n  static void showSongBottomModal(BuildContext context, Map song) {\n    showModalBottomSheet(\n      context: context,\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      isScrollControlled: true,\n      useSafeArea: true,\n      builder: (context) => _songBottomModal(context, song),\n    );\n  }\n\n  static void showPlayerOptionsModal(BuildContext context, Map song) {\n    showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      context: context,\n      useSafeArea: true,\n      isScrollControlled: true,\n      builder: (context) => _playerOptionsModal(context, song),\n    );\n  }\n\n  static void showPlaylistBottomModal(BuildContext context, Map playlist) {\n    showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) => _playlistBottomModal(context, playlist),\n    );\n  }\n\n  static void showFavouritesBottomModal(BuildContext context, Map playlist) {\n    showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) => _favouritesBottomModal(context, playlist),\n    );\n  }\n\n  static void showDownloadBottomModal(BuildContext context) {\n    showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) => _downloadBottomModal(context),\n    );\n  }\n\n  static void showDownloadDetailsBottomModal(\n    BuildContext context,\n    Map playlist,\n  ) {\n    showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) => _downloadDetailsBottomModal(context, playlist),\n    );\n  }\n\n  static Future showArtistsBottomModal(\n    BuildContext context,\n    List artists, {\n    String? leading,\n    bool shouldPop = false,\n  }) {\n    return showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) =>\n          _artistsBottomModal(context, artists, shouldPop: shouldPop),\n    );\n  }\n\n  static void showCreateplaylistModal(BuildContext context, {Map? item}) {\n    PlaylistIcon selectedIcon = PlaylistIcons.musicNoteList;\n    showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) {\n        return StatefulBuilder(\n          builder: (context, setState) {\n            return _createPlaylistModal(\n              context,\n              item,\n              selectedIcon,\n              (icon) => setState(() => selectedIcon = icon),\n            );\n          },\n        );\n      },\n    );\n  }\n\n  static Future<PlaylistIcon?> showSelectPlaylistIconModal(\n    BuildContext context, {\n    Map? item,\n  }) async {\n    return await showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) => _selectPlaylistIconModal(context),\n    );\n  }\n\n  static void showImportplaylistModal(BuildContext context, {Map? item}) {\n    showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) => _importPlaylistModal(context),\n    );\n  }\n\n  static void showEditPlaylistBottomModal(\n    BuildContext context, {\n    required String playlistId,\n    required String iconId,\n    String? name,\n  }) {\n    PlaylistIcon selectedIcon = PlaylistIcons.byId(iconId);\n    showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) {\n        return StatefulBuilder(\n          builder: (context, setState) {\n            return _editPlaylistBottomModal(\n              context,\n              playlistId: playlistId,\n              name: name,\n              selectedIcon: selectedIcon,\n              onIconChanged: (icon) => setState(() => selectedIcon = icon),\n            );\n          },\n        );\n      },\n    );\n  }\n\n  static void addToPlaylist(BuildContext context, Map item) {\n    showModalBottomSheet(\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      useSafeArea: true,\n      isScrollControlled: true,\n      context: context,\n      builder: (context) => _addToPlaylist(context, item),\n    );\n  }\n\n  static Future<bool> showConfirmBottomModal(\n    BuildContext context, {\n    required String message,\n    bool isDanger = false,\n    String? doneText,\n    String? cancelText,\n  }) async {\n    return await showModalBottomSheet(\n          useRootNavigator: false,\n          backgroundColor: Colors.transparent,\n          useSafeArea: true,\n          isScrollControlled: true,\n          context: context,\n          builder: (context) => _confirmBottomModal(\n            context,\n            message: message,\n            isDanger: isDanger,\n            doneText: doneText,\n            cancelText: cancelText,\n          ),\n        ) ??\n        false;\n  }\n\n  static void showAccentSelector(BuildContext context) {\n    showModalBottomSheet(\n      context: context,\n      useRootNavigator: false,\n      backgroundColor: Colors.transparent,\n      isScrollControlled: true,\n      useSafeArea: true,\n      builder: (context) => _accentSelector(context),\n    );\n  }\n}\n\nBottomModalLayout _confirmBottomModal(\n  BuildContext context, {\n  required String message,\n  bool isDanger = false,\n  String? doneText,\n  String? cancelText,\n}) {\n  return BottomModalLayout(\n    title: Center(\n      child: Text(S.of(context).Confirm, style: bigTextStyle(context)),\n    ),\n    actions: [\n      AdaptiveButton(\n        color: Platform.isAndroid\n            ? Theme.of(context).colorScheme.primary.withAlpha(30)\n            : null,\n        onPressed: () {\n          Navigator.pop(context, false);\n        },\n        child: Text(cancelText ?? S.of(context).No),\n      ),\n      const SizedBox(width: 16),\n      AdaptiveFilledButton(\n        onPressed: () {\n          Navigator.pop(context, true);\n        },\n        color: isDanger ? Colors.red : Theme.of(context).colorScheme.primary,\n        child: Text(\n          doneText ?? S.of(context).Yes,\n          style: TextStyle(color: isDanger ? Colors.white : null),\n        ),\n      ),\n    ],\n    child: SingleChildScrollView(\n      child: Center(\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.center,\n          children: [Text(message, textAlign: TextAlign.center)],\n        ),\n      ),\n    ),\n  );\n}\n\nWidget _editPlaylistBottomModal(\n  BuildContext context, {\n  String? name,\n  required String playlistId,\n  required PlaylistIcon selectedIcon,\n  required Function(PlaylistIcon) onIconChanged,\n}) {\n  return TextControllerBuilder(\n    initialText: name,\n    builder: (context, controller) {\n      final bool blockInput = context.isKeyboardSpaceLimited;\n      return BottomModalLayout(\n        title: Center(\n          child: Text(\n            S.of(context).Edit_Playlist,\n            style: mediumTextStyle(context),\n          ),\n        ),\n        actions: [\n          AdaptiveFilledButton(\n            onPressed: () async {\n              String text = controller.text;\n              context\n                  .read<LibraryService>()\n                  .editPlaylist(\n                    playlistId: playlistId,\n                    iconId: selectedIcon.toId(),\n                    title: text.trim().isNotEmpty ? text : null,\n                  )\n                  .then((String message) {\n                    if (context.mounted) {\n                      Navigator.pop(context);\n                      BottomMessage.showText(context, message);\n                    }\n                  });\n            },\n            child: Text(S.of(context).Edit),\n          ),\n        ],\n        child: Row(\n          spacing: 10,\n          children: [\n            GestureDetector(\n              onTap: () async {\n                final icon = await Modals.showSelectPlaylistIconModal(context);\n                if (icon != null) {\n                  onIconChanged(icon);\n                }\n              },\n              child: Stack(\n                clipBehavior: Clip.none,\n                children: [\n                  Container(\n                    padding: const EdgeInsets.all(10),\n                    decoration: BoxDecoration(\n                      borderRadius: BorderRadius.circular(6),\n                      color: Theme.of(context).colorScheme.primaryContainer,\n                    ),\n                    child: PlaylistIconWidget(data: selectedIcon, size: 36),\n                  ),\n                  Positioned(\n                    right: -4,\n                    bottom: -4,\n                    child: CircleAvatar(\n                      radius: 10,\n                      backgroundColor: Theme.of(context).colorScheme.primary,\n                      child: Icon(\n                        Icons.edit,\n                        size: 15,\n                        color: Theme.of(context).colorScheme.onPrimary,\n                      ),\n                    ),\n                  ),\n                ],\n              ),\n            ),\n            Expanded(\n              child: AdaptiveTextField(\n                controller: controller,\n                fillColor: Platform.isAndroid ? greyColor : null,\n                hintText: S.of(context).Playlist_Name,\n                readOnly: blockInput,\n                onTap: () {\n                  if (blockInput) {\n                    FocusScope.of(context).unfocus();\n                    BottomMessage.showText(\n                      context,\n                      S.of(context).Rotate_Device,\n                    );\n                  }\n                },\n              ),\n            ),\n          ],\n        ),\n      );\n    },\n  );\n}\n\nBottomModalLayout _artistsBottomModal(\n  BuildContext context,\n  List<dynamic> artists, {\n  bool shouldPop = false,\n}) {\n  return BottomModalLayout(\n    title: Center(\n      child: Text(S.of(context).Artists, style: mediumTextStyle(context)),\n    ),\n    child: SingleChildScrollView(\n      child: Column(\n        children: [\n          ...artists.map(\n            (artist) => AdaptiveListTile(\n              dense: true,\n              title: Text(\n                artist['name'],\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n              ),\n              leading: Icon(AdaptiveIcons.person),\n              trailing: Icon(AdaptiveIcons.chevron_right),\n              onTap: () {\n                if (shouldPop) {\n                  context.go(\n                    '/browse',\n                    extra: {\n                      'endpoint': artist['endpoint'].cast<String, dynamic>(),\n                    },\n                  );\n                } else {\n                  Navigator.pop(context);\n                  context.push(\n                    '/browse',\n                    extra: {\n                      'endpoint': artist['endpoint'].cast<String, dynamic>(),\n                    },\n                  );\n                }\n              },\n            ),\n          ),\n        ],\n      ),\n    ),\n  );\n}\n\nWidget _createPlaylistModal(\n  BuildContext context,\n  Map<dynamic, dynamic>? item,\n  PlaylistIcon selectedIcon,\n  Function(PlaylistIcon) onIconChanged,\n) {\n  return TextControllerBuilder(\n    builder: (context, controller) {\n      final bool blockInput = context.isKeyboardSpaceLimited;\n      return Padding(\n        padding: EdgeInsets.only(\n          bottom: MediaQuery.of(context).viewInsets.bottom,\n        ),\n        child: BottomModalLayout(\n          title: Text(\n            S.of(context).Create_Playlist,\n            style: mediumTextStyle(context),\n          ),\n          actions: [\n            AdaptiveButton(\n              onPressed: () => Navigator.pop(context),\n              child: Text(S.of(context).Cancel),\n            ),\n            AdaptiveFilledButton(\n              color: Theme.of(context).colorScheme.primary,\n              onPressed: () async {\n                final message = await context\n                    .read<LibraryService>()\n                    .createPlaylist(\n                      controller.text,\n                      selectedIcon.toId(),\n                      item: item,\n                    );\n                if (!context.mounted) return;\n                Navigator.pop(context);\n                BottomMessage.showText(context, message);\n              },\n              child: Text(\n                S.of(context).Create,\n                style: TextStyle(\n                  color: context.isDarkMode ? Colors.black : Colors.white,\n                ),\n              ),\n            ),\n          ],\n          child: Row(\n            spacing: 10,\n            children: [\n              GestureDetector(\n                onTap: () async {\n                  final icon = await Modals.showSelectPlaylistIconModal(\n                    context,\n                  );\n                  if (icon != null) {\n                    onIconChanged(icon);\n                  }\n                },\n                child: Stack(\n                  clipBehavior: Clip.none,\n                  children: [\n                    Container(\n                      padding: const EdgeInsets.all(10),\n                      decoration: BoxDecoration(\n                        borderRadius: BorderRadius.circular(6),\n                        color: Theme.of(context).colorScheme.primaryContainer,\n                      ),\n                      child: PlaylistIconWidget(data: selectedIcon, size: 36),\n                    ),\n                    Positioned(\n                      right: -4,\n                      bottom: -4,\n                      child: CircleAvatar(\n                        radius: 10,\n                        backgroundColor: Theme.of(context).colorScheme.primary,\n                        child: Icon(\n                          Icons.edit,\n                          size: 15,\n                          color: Theme.of(context).colorScheme.onPrimary,\n                        ),\n                      ),\n                    ),\n                  ],\n                ),\n              ),\n              Expanded(\n                child: AdaptiveTextField(\n                  controller: controller,\n                  fillColor: Platform.isAndroid ? greyColor : null,\n                  hintText: S.of(context).Playlist_Name,\n                  readOnly: blockInput,\n                  onTap: () {\n                    if (blockInput) {\n                      FocusScope.of(context).unfocus();\n                      BottomMessage.showText(\n                        context,\n                        S.of(context).Rotate_Device,\n                      );\n                    }\n                  },\n                ),\n              ),\n            ],\n          ),\n        ),\n      );\n    },\n  );\n}\n\nWidget _selectPlaylistIconModal(BuildContext context) {\n  return BottomModalLayout(\n    title: Text(\n      S.of(context).Select_Playlist_Icon,\n      style: mediumTextStyle(context),\n    ),\n    child: Padding(\n      padding: const EdgeInsets.symmetric(horizontal: 20),\n      child: GridView.builder(\n        shrinkWrap: true,\n        itemCount: PlaylistIcons.values.length,\n        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(\n          crossAxisCount: 5,\n        ),\n        itemBuilder: (context, index) {\n          final icon = PlaylistIcons.values[index];\n          return GestureDetector(\n            onTap: () => Navigator.pop(context, icon),\n            child: Center(child: PlaylistIconWidget(data: icon, size: 36)),\n          );\n        },\n      ),\n    ),\n  );\n}\n\nWidget _importPlaylistModal(BuildContext context) {\n  final bool blockInput = context.isKeyboardSpaceLimited;\n  return TextControllerBuilder(\n    builder: (context, controller) {\n      return BottomModalLayout(\n        title: Center(\n          child: Text(\n            S.of(context).Import_Playlist,\n            style: mediumTextStyle(context),\n          ),\n        ),\n        actions: [\n          AdaptiveButton(\n            onPressed: () async {\n              Navigator.pop(context);\n            },\n            child: Text(S.of(context).Cancel),\n          ),\n          AdaptiveFilledButton(\n            color: Theme.of(context).colorScheme.primary,\n            onPressed: () async {\n              Modals.showCenterLoadingModal(context);\n              String message = await GetIt.I<LibraryService>().importPlaylist(\n                controller.text,\n              );\n              if (context.mounted) {\n                Navigator.pop(context);\n                Navigator.pop(context);\n                BottomMessage.showText(context, message);\n              }\n            },\n            child: Text(\n              S.of(context).Import,\n              style: TextStyle(\n                color: context.isDarkMode ? Colors.black : Colors.white,\n              ),\n            ),\n          ),\n        ],\n        child: SingleChildScrollView(\n          child: Column(\n            children: [\n              Column(\n                children: [\n                  AdaptiveTextField(\n                    controller: controller,\n                    keyboardType: TextInputType.url,\n                    hintText: 'https://music.youtube.com/playlist?list=',\n                    prefix: Padding(\n                      padding: const EdgeInsets.symmetric(\n                        horizontal: 8,\n                        vertical: 4,\n                      ),\n                      child: Icon(Icons.title),\n                    ),\n                    fillColor: Platform.isWindows ? null : greyColor,\n                    contentPadding: const EdgeInsets.symmetric(\n                      vertical: 2,\n                      horizontal: 16,\n                    ),\n                    readOnly: blockInput,\n                    onTap: () {\n                      if (blockInput) {\n                        FocusScope.of(context).unfocus();\n                        BottomMessage.showText(\n                          context,\n                          S.of(context).Rotate_Device,\n                        );\n                      }\n                    },\n                  ),\n                ],\n              ),\n            ],\n          ),\n        ),\n      );\n    },\n  );\n}\n\nBottomModalLayout _addToPlaylist(BuildContext context, Map item) {\n  return BottomModalLayout(\n    title: AdaptiveListTile(\n      contentPadding: EdgeInsets.zero,\n      title: Text(\n        S.of(context).Add_To_Playlist,\n        style: mediumTextStyle(context),\n      ),\n      trailing: AdaptiveIconButton(\n        onPressed: () {\n          Navigator.pop(context);\n          Modals.showCreateplaylistModal(context, item: item);\n        },\n        icon: const Icon(Icons.playlist_add, size: 20),\n      ),\n    ),\n    child: SingleChildScrollView(\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          ...context.read<LibraryService>().userPlaylists.map((key, playlist) {\n            return MapEntry(\n              key,\n              playlist['songs']\n                      .map((song) => song[\"videoId\"])\n                      .contains(item[\"videoId\"])\n                  ? const SizedBox.shrink()\n                  : AdaptiveListTile(\n                      dense: true,\n                      title: Text(playlist['title']),\n                      leading: playlist['isPredefined'] == true\n                          ? ClipRRect(\n                              borderRadius: BorderRadius.circular(\n                                playlist['type'] == 'ARTIST' ? 50 : 3,\n                              ),\n                              child: CachedNetworkImage(\n                                imageUrl: playlist['thumbnails'].first['url']\n                                    .replaceAll('w540-h225', 'w60-h60'),\n                                height: 50,\n                                width: 50,\n                              ),\n                            )\n                          : Container(\n                              padding: const EdgeInsets.all(6),\n                              decoration: BoxDecoration(\n                                color: Theme.of(\n                                  context,\n                                ).colorScheme.primaryContainer,\n                                borderRadius: BorderRadius.circular(8),\n                              ),\n                              child: PlaylistIconWidget(\n                                data: PlaylistIcons.byId(playlist['iconId']),\n                                size: 30,\n                              ),\n                            ),\n                      onTap: () async {\n                        await context\n                            .read<LibraryService>()\n                            .addToPlaylist(item: item, key: key)\n                            .then((String message) {\n                              if (context.mounted) {\n                                Navigator.pop(context);\n                                BottomMessage.showText(context, message);\n                              }\n                            });\n                      },\n                    ),\n            );\n          }).values,\n        ],\n      ),\n    ),\n  );\n}\n\n// SizedBox _updateDialog(BuildContext context, UpdateInfo? updateInfo) {\n//   final f = DateFormat('MMMM dd, yyyy');\n\n//   return SizedBox(\n//     height: MediaQuery.of(context).size.height,\n//     width: MediaQuery.of(context).size.width,\n//     child: LayoutBuilder(builder: (context, constraints) {\n//       return AlertDialog(\n//         icon: Center(\n//           child: Container(\n//             padding: const EdgeInsets.all(16),\n//             decoration: BoxDecoration(\n//                 color: Colors.green.withAlpha(100),\n//                 borderRadius: BorderRadius.circular(16)),\n//             child: const Icon(\n//               Icons.update_outlined,\n//               size: 70,\n//             ),\n//           ),\n//         ),\n//         scrollable: true,\n//         title: Column(\n//           children: [\n//             Text(updateInfo != null ? 'Update Available' : 'Update Info'),\n//             if (updateInfo != null)\n//               Text(\n//                 '${updateInfo.name}\\n${f.format(DateTime.parse(updateInfo.publishedAt))}',\n//                 style: TextStyle(fontSize: 16, color: context.subtitleColor),\n//               )\n//           ],\n//         ),\n//         content: updateInfo != null\n//             ? SizedBox(\n//                 width: constraints.maxWidth,\n//                 height: constraints.maxHeight - 400,\n//                 child: Markdown(\n//                   data: updateInfo.body,\n//                   shrinkWrap: true,\n//                   softLineBreak: true,\n//                   onTapLink: (text, href, title) {\n//                     if (href != null) {\n//                       launchUrl(Uri.parse(href),\n//                           mode: LaunchMode.platformDefault);\n//                     }\n//                   },\n//                 ),\n//               )\n//             : const Center(\n//                 child: Text(\"You are already up to date.\"),\n//               ),\n//         actions: [\n//           if (updateInfo != null)\n//             AdaptiveButton(\n//               onPressed: () {\n//                 Navigator.pop(context);\n//               },\n//               child: const Text('Cancel'),\n//             ),\n//           AdaptiveFilledButton(\n//             onPressed: () {\n//               Navigator.pop(context);\n//               if (updateInfo != null) {\n//                 launchUrl(Uri.parse(updateInfo.downloadUrl),\n//                     mode: LaunchMode.externalApplication);\n//               }\n//             },\n//             child: Text(updateInfo != null ? 'Update' : 'Done'),\n//           ),\n//         ],\n//       );\n//     }),\n//   );\n// }\n\nWidget _textFieldBottomModal(\n  BuildContext context, {\n  String? title,\n  String? hintText,\n  String? doneText,\n}) {\n  final bool blockInput = context.isKeyboardSpaceLimited;\n  return TextControllerBuilder(\n    builder: (context, controller) {\n      return BottomModalLayout(\n        title: (title != null)\n            ? Center(child: Text(title, style: mediumTextStyle(context)))\n            : null,\n        actions: [\n          AdaptiveFilledButton(\n            onPressed: () async {\n              Navigator.pop(context, controller.text);\n            },\n            child: Text(doneText ?? S.of(context).Done),\n          ),\n        ],\n        child: SingleChildScrollView(\n          child: Column(\n            mainAxisSize: MainAxisSize.min,\n            children: [\n              Padding(\n                padding: const EdgeInsets.symmetric(\n                  horizontal: 16,\n                  vertical: 16,\n                ),\n                child: Column(\n                  children: [\n                    AdaptiveTextField(\n                      controller: controller,\n                      fillColor: greyColor,\n                      contentPadding: const EdgeInsets.symmetric(\n                        vertical: 2,\n                        horizontal: 16,\n                      ),\n                      hintText: hintText,\n                      prefix: const Icon(Icons.title),\n                      readOnly: blockInput,\n                      onTap: () {\n                        if (blockInput) {\n                          FocusScope.of(context).unfocus();\n                          BottomMessage.showText(\n                            context,\n                            S.of(context).Rotate_Device,\n                          );\n                        }\n                      },\n                    ),\n                  ],\n                ),\n              ),\n            ],\n          ),\n        ),\n      );\n    },\n  );\n}\n\nBottomModalLayout _playerOptionsModal(BuildContext context, Map song) {\n  return BottomModalLayout(\n    child: SingleChildScrollView(\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          Column(\n            children: [\n              StreamBuilder(\n                stream: GetIt.I<MediaPlayer>().player.volumeStream,\n                builder: (context, progress) {\n                  return AdaptiveListTile(\n                    dense: true,\n                    leading: Icon(\n                      AdaptiveIcons.volume(\n                        (progress.hasData && progress.data != null)\n                            ? progress.data!\n                            : GetIt.I<MediaPlayer>().player.volume,\n                      ),\n                    ),\n                    title: AdaptiveSlider(\n                      label:\n                          (((progress.hasData && progress.data != null)\n                                      ? progress.data!\n                                      : GetIt.I<MediaPlayer>().player.volume) *\n                                  100)\n                              .toStringAsFixed(1),\n                      value: (progress.hasData && progress.data != null)\n                          ? progress.data!\n                          : GetIt.I<MediaPlayer>().player.volume,\n                      onChanged: (volume) {\n                        GetIt.I<MediaPlayer>().player.setVolume(volume);\n                      },\n                    ),\n                  );\n                },\n              ),\n              StreamBuilder(\n                stream: GetIt.I<MediaPlayer>().player.speedStream,\n                builder: (context, progress) {\n                  return AdaptiveListTile(\n                    dense: true,\n                    leading: const Icon(Icons.speed),\n                    title: AdaptiveSlider(\n                      max: 2,\n                      min: 0.25,\n                      divisions: 7,\n                      label:\n                          ((progress.hasData && progress.data != null)\n                                  ? progress.data!\n                                  : GetIt.I<MediaPlayer>().player.speed)\n                              .toString(),\n                      value: (progress.hasData && progress.data != null)\n                          ? progress.data!\n                          : GetIt.I<MediaPlayer>().player.speed,\n                      onChanged: (speed) {\n                        GetIt.I<MediaPlayer>().player.setSpeed(speed);\n                      },\n                    ),\n                  );\n                },\n              ),\n            ],\n          ),\n          if (Platform.isAndroid)\n            AdaptiveListTile(\n              dense: true,\n              title: Text(S.of(context).Equalizer),\n              leading: Icon(AdaptiveIcons.equalizer),\n              onTap: () {\n                Navigator.of(context).push(\n                  MaterialPageRoute(\n                    builder: (context) => const EqualizerPage(),\n                  ),\n                );\n              },\n              trailing: Icon(Icons.chevron_right),\n            ),\n          if (song['artists'] != null)\n            AdaptiveListTile(\n              dense: true,\n              title: Text(S.of(context).Artists),\n              leading: Icon(AdaptiveIcons.people),\n              trailing: Icon(Icons.chevron_right),\n              onTap: () {\n                Navigator.pop(context);\n                Modals.showArtistsBottomModal(\n                  context,\n                  song['artists'],\n                  leading: song['thumbnails'].first['url'],\n                  shouldPop: true,\n                );\n              },\n            ),\n          if (song['album'] != null)\n            AdaptiveListTile(\n              dense: true,\n              title: Text(\n                S.of(context).Album,\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n              ),\n              leading: Icon(AdaptiveIcons.album),\n              trailing: Icon(Icons.chevron_right),\n              onTap: () {\n                context.go(\n                  '/browse',\n                  extra: {\n                    'endpoint': song['album']['endpoint']\n                        .cast<String, dynamic>(),\n                  },\n                );\n              },\n            ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Add_To_Playlist),\n            leading: Icon(AdaptiveIcons.library_add),\n            onTap: () {\n              Navigator.pop(context);\n              Modals.addToPlaylist(context, song);\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            leading: Icon(AdaptiveIcons.timer),\n            title: Text(S.of(context).Sleep_Timer),\n            onTap: () {\n              showDurationPicker(\n                context: context,\n                initialTime: const Duration(minutes: 30),\n                decoration: BoxDecoration(\n                  borderRadius: BorderRadius.circular(10),\n                  color: AdaptiveTheme.of(context).inactiveBackgroundColor,\n                ),\n              ).then((duration) {\n                if (duration != null) {\n                  if (context.mounted) {\n                    context.read<MediaPlayer>().setTimer(duration);\n                  }\n                }\n              });\n            },\n            trailing: ValueListenableBuilder(\n              valueListenable: GetIt.I<MediaPlayer>().timerDuration,\n              builder: (context, value, child) {\n                return value == null\n                    ? const SizedBox.shrink()\n                    : TextButton.icon(\n                        onPressed: () {\n                          GetIt.I<MediaPlayer>().cancelTimer();\n                        },\n                        label: Text(formatDuration(value)),\n                        icon: const Icon(CupertinoIcons.clear),\n                        iconAlignment: IconAlignment.end,\n                      );\n              },\n            ),\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: const Text('Share'),\n            leading: Icon(AdaptiveIcons.share),\n            onTap: () {\n              Navigator.pop(context);\n              Share.shareUri(\n                Uri.parse(\n                  'https://music.youtube.com/watch?v=${song['videoId']}',\n                ),\n              );\n            },\n          ),\n        ],\n      ),\n    ),\n  );\n}\n\nBottomModalLayout _showSelection(\n  BuildContext context,\n  List<SelectionItem> items,\n) {\n  return BottomModalLayout(\n    title: Center(child: Text(\"Select\", style: mediumTextStyle(context))),\n    child: SingleChildScrollView(\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          ...items.map(\n            (item) => AdaptiveListTile(\n              dense: true,\n              title: Text(item.title),\n              onTap: () {\n                Navigator.pop(context, item.data);\n              },\n            ),\n          ),\n        ],\n      ),\n    ),\n  );\n}\n\nBottomModalLayout _songBottomModal(BuildContext context, Map song) {\n  return BottomModalLayout(\n    title: AdaptiveListTile(\n      contentPadding: EdgeInsets.zero,\n      title: Text(song['title'], maxLines: 1, overflow: TextOverflow.ellipsis),\n      leading: ClipRRect(\n        borderRadius: BorderRadius.circular(10),\n        child: CachedNetworkImage(\n          imageUrl: song['thumbnails'].first['url'],\n          height: 50,\n          width: song['type'] == 'VIDEO' ? 80 : 50,\n        ),\n      ),\n      subtitle: song['subtitle'] != null\n          ? Text(song['subtitle'], maxLines: 1, overflow: TextOverflow.ellipsis)\n          : null,\n      trailing: IconButton(\n        onPressed: () => Share.shareUri(\n          Uri.parse('https://music.youtube.com/watch?v=${song['videoId']}'),\n        ),\n        icon: const Icon(CupertinoIcons.share),\n      ),\n    ),\n    child: SingleChildScrollView(\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Play_Next),\n            leading: Icon(AdaptiveIcons.playlist_play),\n            onTap: () async {\n              Navigator.pop(context);\n              await GetIt.I<MediaPlayer>().playNext(Map.from(song));\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Add_To_Queue),\n            leading: Icon(AdaptiveIcons.queue_add),\n            onTap: () async {\n              Navigator.pop(context);\n              await GetIt.I<MediaPlayer>().addToQueue(Map.from(song));\n            },\n          ),\n          ListenableBuilder(\n            listenable: GetIt.I<FavouritesManager>().listenable,\n            builder: (context, child) {\n              bool isFavorite = GetIt.I<FavouritesManager>().isFavourite(song);\n              return AdaptiveListTile(\n                dense: true,\n                title: Text(\n                  !isFavorite\n                      ? S.of(context).Add_To_Favourites\n                      : S.of(context).Remove_From_Favourites,\n                ),\n                leading: Icon(\n                  !isFavorite ? AdaptiveIcons.heart : AdaptiveIcons.heart_fill,\n                ),\n                onTap: () async {\n                  Navigator.pop(context);\n                  GetIt.I<FavouritesManager>().addOrRemove(song);\n                },\n              );\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Download),\n            leading: Icon(AdaptiveIcons.download),\n            onTap: () {\n              Navigator.pop(context);\n              BottomMessage.showText(context, S.of(context).Download_Started);\n              GetIt.I<DownloadManager>().downloadSong(song);\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Add_To_Playlist),\n            leading: Icon(AdaptiveIcons.library_add),\n            onTap: () {\n              Navigator.pop(context);\n              Modals.addToPlaylist(context, song);\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Start_Radio),\n            leading: Icon(AdaptiveIcons.radio),\n            onTap: () {\n              Navigator.pop(context);\n              GetIt.I<MediaPlayer>().startRelated(Map.from(song), radio: true);\n            },\n          ),\n          if (song['artists'] != null)\n            AdaptiveListTile(\n              dense: true,\n              title: Text(S.of(context).Artists),\n              leading: Icon(AdaptiveIcons.people),\n              trailing: Icon(AdaptiveIcons.chevron_right),\n              onTap: () {\n                Navigator.pop(context);\n                Modals.showArtistsBottomModal(\n                  context,\n                  song['artists'],\n                  leading: song['thumbnails'].first['url'],\n                );\n              },\n            ),\n          if (song['album'] != null)\n            AdaptiveListTile(\n              dense: true,\n              title: Text(\n                S.of(context).Album,\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n              ),\n              leading: Icon(AdaptiveIcons.album),\n              trailing: Icon(AdaptiveIcons.chevron_right),\n              onTap: () {\n                Navigator.pop(context);\n                context.push(\n                  '/browse',\n                  extra: {\n                    'endpoint': song['album']['endpoint']\n                        .cast<String, dynamic>(),\n                  },\n                );\n              },\n            ),\n        ],\n      ),\n    ),\n  );\n}\n\nBottomModalLayout _playlistBottomModal(BuildContext context, Map playlist) {\n  return BottomModalLayout(\n    title: AdaptiveListTile(\n      contentPadding: EdgeInsets.zero,\n      title: Text(\n        playlist['title'],\n        maxLines: 1,\n        overflow: TextOverflow.ellipsis,\n      ),\n      leading: (playlist['isPredefined'] != false)\n          ? ClipRRect(\n              borderRadius: BorderRadius.circular(\n                playlist['type'] == 'ARTIST' ? 30 : 8,\n              ),\n              child: CachedNetworkImage(\n                imageUrl: playlist['thumbnails'].first['url'].replaceAll(\n                  'w540-h225',\n                  'w60-h60',\n                ),\n                height: 40,\n                width: 40,\n              ),\n            )\n          : Container(\n              padding: const EdgeInsets.all(6),\n              decoration: BoxDecoration(\n                color: Theme.of(context).colorScheme.primaryContainer,\n                borderRadius: BorderRadius.circular(8),\n              ),\n              child: PlaylistIconWidget(\n                data: PlaylistIcons.byId(playlist['iconId']),\n                size: 30,\n              ),\n            ),\n      subtitle: playlist['subtitle'] != null\n          ? Text(\n              playlist['subtitle'],\n              maxLines: 1,\n              overflow: TextOverflow.ellipsis,\n            )\n          : null,\n      trailing: playlist['isPredefined'] != false\n          ? IconButton(\n              onPressed: () => Share.shareUri(\n                Uri.parse(\n                  playlist['type'] == 'ARTIST'\n                      ? 'https://music.youtube.com/channel/${playlist['endpoint']['browseId']}'\n                      : 'https://music.youtube.com/playlist?list=${playlist['playlistId']}',\n                ),\n              ),\n              icon: const Icon(CupertinoIcons.share),\n            )\n          : null,\n    ),\n    child: SingleChildScrollView(\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Play_Next),\n            leading: Icon(AdaptiveIcons.playlist_play),\n            onTap: () async {\n              Navigator.pop(context);\n              await GetIt.I<MediaPlayer>().playNext(Map.from(playlist));\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Add_To_Queue),\n            leading: Icon(AdaptiveIcons.queue_add),\n            onTap: () async {\n              Navigator.pop(context);\n              await GetIt.I<MediaPlayer>().addToQueue(Map.from(playlist));\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Download),\n            leading: Icon(AdaptiveIcons.download),\n            onTap: () async {\n              Navigator.pop(context);\n              BottomMessage.showText(context, S.of(context).Download_Started);\n              GetIt.I<DownloadManager>().downloadPlaylist(playlist);\n            },\n          ),\n          if (playlist['isPredefined'] == false)\n            AdaptiveListTile(\n              dense: true,\n              leading: const Icon(Icons.edit),\n              title: Text(S.of(context).Edit),\n              onTap: () {\n                Navigator.pop(context);\n                Modals.showEditPlaylistBottomModal(\n                  context,\n                  playlistId: playlist['playlistId'],\n                  name: playlist['title'],\n                  iconId: playlist['iconId'],\n                );\n              },\n            ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(\n              context.watch<LibraryService>().getPlaylist(\n                        playlist['playlistId'] ??\n                            playlist['endpoint']['browseId'],\n                      ) ==\n                      null\n                  ? S.of(context).Add_To_Library\n                  : S.of(context).Remove_From_Library,\n            ),\n            leading: Icon(\n              context.watch<LibraryService>().getPlaylist(\n                        playlist['playlistId'] ??\n                            playlist['endpoint']['browseId'],\n                      ) ==\n                      null\n                  ? AdaptiveIcons.library_add\n                  : AdaptiveIcons.library_add_check,\n            ),\n            onTap: () async {\n              if (context.read<LibraryService>().getPlaylist(\n                    playlist['playlistId'],\n                  ) ==\n                  null) {\n                final String message = await GetIt.I<LibraryService>()\n                    .addToOrRemoveFromLibrary(playlist);\n                if (!context.mounted) return;\n                BottomMessage.showText(context, message);\n              } else {\n                final bool confirm = await Modals.showConfirmBottomModal(\n                  context,\n                  message: S.of(context).Delete_Item_Message,\n                  isDanger: true,\n                );\n                if (confirm != true) return;\n                final String message = await GetIt.I<LibraryService>()\n                    .addToOrRemoveFromLibrary(playlist);\n                if (!context.mounted) return;\n                BottomMessage.showText(context, message);\n              }\n              Navigator.pop(context);\n            },\n          ),\n          if (playlist['playlistId'] != null && playlist['type'] == 'ARTIST')\n            AdaptiveListTile(\n              dense: true,\n              title: Text(S.of(context).Start_Radio),\n              leading: Icon(AdaptiveIcons.radio),\n              onTap: () async {\n                Navigator.pop(context);\n                BottomMessage.showText(\n                  context,\n                  S.of(context).Songs_Will_Start_Playing_Soon,\n                );\n                await GetIt.I<MediaPlayer>().startRelated(\n                  Map.from(playlist),\n                  radio: true,\n                  isArtist: playlist['type'] == 'ARTIST',\n                );\n              },\n            ),\n          if (playlist['artists'] != null && playlist['artists'].isNotEmpty)\n            AdaptiveListTile(\n              dense: true,\n              title: Text(S.of(context).Artists),\n              leading: Icon(AdaptiveIcons.people),\n              trailing: Icon(AdaptiveIcons.chevron_right),\n              onTap: () {\n                Navigator.pop(context);\n                Modals.showArtistsBottomModal(\n                  context,\n                  playlist['artists'],\n                  leading: playlist['thumbnails'].first['url'],\n                );\n              },\n            ),\n          if (playlist['album'] != null)\n            AdaptiveListTile(\n              dense: true,\n              title: Text(\n                S.of(context).Album,\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n              ),\n              leading: Icon(AdaptiveIcons.album),\n              trailing: Icon(AdaptiveIcons.chevron_right),\n              onTap: () => context.push(\n                '/browse',\n                extra: {'endpoint': playlist['album']['endpoint']},\n              ),\n            ),\n        ],\n      ),\n    ),\n  );\n}\n\nBottomModalLayout _favouritesBottomModal(BuildContext context, Map playlist) {\n  return BottomModalLayout(\n    title: AdaptiveListTile(\n      contentPadding: EdgeInsets.zero,\n      title: Text(\n        S.of(context).Favourites,\n        maxLines: 1,\n        overflow: TextOverflow.ellipsis,\n      ),\n      leading: ColorIcon(\n        icon: FluentIcons.heart_24_filled,\n        boxColor: Theme.of(context).colorScheme.primaryContainer,\n        iconColor: Theme.of(context).colorScheme.onPrimaryContainer,\n        size: 30,\n      ),\n    ),\n    child: SingleChildScrollView(\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Play_Next),\n            leading: Icon(AdaptiveIcons.playlist_play),\n            onTap: () async {\n              Navigator.pop(context);\n              await GetIt.I<MediaPlayer>().playNext(Map.from(playlist));\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Add_To_Queue),\n            leading: Icon(AdaptiveIcons.queue_add),\n            onTap: () async {\n              Navigator.pop(context);\n              await GetIt.I<MediaPlayer>().addToQueue(Map.from(playlist));\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Download),\n            leading: Icon(AdaptiveIcons.download),\n            onTap: () async {\n              Navigator.pop(context);\n              BottomMessage.showText(context, S.of(context).Download_Started);\n              GetIt.I<DownloadManager>().downloadPlaylist(playlist);\n            },\n          ),\n        ],\n      ),\n    ),\n  );\n}\n\nBottomModalLayout _downloadBottomModal(BuildContext context) {\n  return BottomModalLayout(\n    title: AdaptiveListTile(\n      contentPadding: EdgeInsets.zero,\n      title: Text(\n        S.of(context).Downloads,\n        maxLines: 1,\n        overflow: TextOverflow.ellipsis,\n      ),\n      leading: ColorIcon(\n        icon: FluentIcons.cloud_arrow_down_24_filled,\n        boxColor: Theme.of(context).colorScheme.primaryContainer,\n        iconColor: Theme.of(context).colorScheme.onPrimaryContainer,\n        size: 30,\n      ),\n    ),\n    child: SingleChildScrollView(\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Downloading),\n            leading: Icon(AdaptiveIcons.downloading),\n            onTap: () async {\n              context.push('/library/downloads/downloading');\n              Navigator.pop(context);\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Restore_Missing_Songs),\n            leading: Icon(AdaptiveIcons.sync),\n            onTap: () async {\n              Navigator.pop(context);\n              BottomMessage.showText(\n                context,\n                S.of(context).Restoring_Missing_Songs,\n              );\n              GetIt.I<DownloadManager>().restoreDownloads();\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Delete_All_Songs),\n            leading: Icon(AdaptiveIcons.delete),\n            onTap: () async {\n              bool shouldDelete = await Modals.showConfirmBottomModal(\n                context,\n                message: S.of(context).Confirm_Delete_All_Message,\n                isDanger: true,\n                doneText: S.of(context).Yes,\n                cancelText: S.of(context).No,\n              );\n              if (shouldDelete) {\n                if (context.mounted) {\n                  Navigator.pop(context);\n                  BottomMessage.showText(context, S.of(context).Deleting_Songs);\n                }\n                await GetIt.I<DownloadManager>().deleteAllSongs();\n              }\n            },\n          ),\n        ],\n      ),\n    ),\n  );\n}\n\nBottomModalLayout _downloadDetailsBottomModal(\n  BuildContext context,\n  Map playlist,\n) {\n  return BottomModalLayout(\n    title: AdaptiveListTile(\n      contentPadding: EdgeInsets.zero,\n      title: Text(\n        playlist['title'],\n        maxLines: 1,\n        overflow: TextOverflow.ellipsis,\n      ),\n      leading:\n          (playlist['songs']?.length > 0 &&\n              playlist['id'] != DownloadManager.songsPlaylistId &&\n              playlist['id'] != FavouritesManager.playlistId)\n          ? (playlist['type'] == \"ALBUM\")\n                ? PlaylistThumbnail(\n                    playlist: [playlist['songs'][0]],\n                    size: 50,\n                    radius: 8,\n                  )\n                : PlaylistThumbnail(\n                    playlist: playlist['songs'],\n                    size: 50,\n                    radius: 8,\n                  )\n          : Container(\n              height: 40,\n              width: 40,\n              decoration: BoxDecoration(\n                color: Theme.of(context).colorScheme.primaryContainer,\n                borderRadius: BorderRadius.circular(8),\n              ),\n              child: Icon(\n                playlist['id'] == FavouritesManager.playlistId\n                    ? AdaptiveIcons.heart_fill\n                    : CupertinoIcons.music_note_list,\n                color: Theme.of(context).colorScheme.onPrimaryContainer,\n              ),\n            ),\n      subtitle: playlist['subtitle'] != null\n          ? Text(\n              playlist['subtitle'],\n              maxLines: 1,\n              overflow: TextOverflow.ellipsis,\n            )\n          : null,\n    ),\n    child: SingleChildScrollView(\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Play_Next),\n            leading: Icon(AdaptiveIcons.playlist_play),\n            onTap: () async {\n              Navigator.pop(context);\n              final plst = {\n                ...playlist,\n                'songs': GetIt.I<DownloadManager>().getDownloadedSongs(\n                  playlist['id'],\n                ),\n              };\n              await GetIt.I<MediaPlayer>().playNext(Map.from(plst));\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Add_To_Queue),\n            leading: Icon(AdaptiveIcons.queue_add),\n            onTap: () async {\n              Navigator.pop(context);\n              final plst = {\n                ...playlist,\n                'songs': GetIt.I<DownloadManager>().getDownloadedSongs(\n                  playlist['id'],\n                ),\n              };\n              await GetIt.I<MediaPlayer>().addToQueue(Map.from(plst));\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Restore_Missing_Songs),\n            leading: Icon(Icons.restore),\n            onTap: () async {\n              Navigator.pop(context);\n              BottomMessage.showText(\n                context,\n                S.of(context).Restoring_Missing_Songs,\n              );\n              GetIt.I<DownloadManager>().restoreDownloads(\n                songs: playlist['songs'],\n              );\n            },\n          ),\n          AdaptiveListTile(\n            dense: true,\n            title: Text(S.of(context).Delete_All_Songs),\n            leading: Icon(AdaptiveIcons.delete),\n            onTap: () async {\n              Modals.showConfirmBottomModal(\n                context,\n                message: S.of(context).Confirm_Delete_All_Message,\n                isDanger: true,\n              ).then((bool confirm) async {\n                if (confirm) {\n                  if (context.mounted) {\n                    Navigator.pop(context);\n\n                    BottomMessage.showText(\n                      context,\n                      S.of(context).Deleting_Songs,\n                    );\n                  }\n                  for (var song in playlist['songs']) {\n                    await GetIt.I<DownloadManager>().deleteSong(\n                      key: song['videoId'],\n                      playlistId: playlist['id'],\n                    );\n                  }\n                }\n              });\n            },\n          ),\n        ],\n      ),\n    ),\n  );\n}\n\nBottomModalLayout _accentSelector(BuildContext context) {\n  Color? accentColor = GetIt.I<SettingsManager>().accentColor;\n  return BottomModalLayout(\n    title: Center(child: Text('Select Color', style: mediumTextStyle(context))),\n    actions: [\n      AdaptiveButton(\n        onPressed: () {\n          Navigator.pop(context);\n          GetIt.I<SettingsManager>().accentColor = null;\n        },\n        child: const Text('Reset'),\n      ),\n      AdaptiveFilledButton(\n        child: Text(S.of(context).Done),\n        onPressed: () => Navigator.pop(context),\n      ),\n    ],\n    child: Column(\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        ColorPicker(\n          pickerColor: accentColor ?? Colors.white,\n          onColorChanged: (color) {\n            GetIt.I<SettingsManager>().accentColor = color;\n          },\n          labelTypes: const [],\n          portraitOnly: true,\n          colorPickerWidth: min(300, MediaQuery.of(context).size.width - 32),\n          pickerAreaHeightPercent: 0.7,\n          enableAlpha: false,\n          displayThumbColor: false,\n          paletteType: PaletteType.hueWheel,\n        ),\n      ],\n    ),\n  );\n}\n\nclass BottomModalLayout extends StatelessWidget {\n  const BottomModalLayout({\n    required this.child,\n    this.title,\n    this.actions,\n    super.key,\n  });\n  final Widget child;\n  final Widget? title;\n  final List<Widget>? actions;\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      width: double.maxFinite,\n      constraints: const BoxConstraints(maxWidth: 600),\n      child: Material(\n        color: Theme.of(context).colorScheme.surfaceContainerLow,\n        borderRadius: const BorderRadius.only(\n          topLeft: Radius.circular(16),\n          topRight: Radius.circular(16),\n          bottomLeft: Radius.circular(0),\n          bottomRight: Radius.circular(0),\n        ),\n        child: Padding(\n          padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),\n          child: SafeArea(\n            child: Column(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                Center(\n                  child: Container(\n                    width: 40,\n                    height: 4,\n                    margin: const EdgeInsets.only(bottom: 8),\n                    decoration: BoxDecoration(\n                      color: Theme.of(\n                        context,\n                      ).colorScheme.onSurfaceVariant.withValues(alpha: 0.4),\n                      borderRadius: BorderRadius.circular(2),\n                    ),\n                  ),\n                ),\n                if (title != null)\n                  Padding(\n                    padding: const EdgeInsets.symmetric(\n                      vertical: 8,\n                      horizontal: 0,\n                    ),\n                    child: title!,\n                  ),\n                Flexible(\n                  child: SingleChildScrollView(\n                    physics: const ClampingScrollPhysics(),\n                    child: child,\n                  ),\n                ),\n                if (actions != null)\n                  Padding(\n                    padding: const EdgeInsets.symmetric(\n                      vertical: 8,\n                      horizontal: 0,\n                    ),\n                    child: Row(\n                      mainAxisAlignment: MainAxisAlignment.end,\n                      children: actions!,\n                    ),\n                  ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass SelectionItem<T> {\n  final String title;\n  final IconData? icon;\n  final T data;\n\n  SelectionItem({required this.title, this.icon, required this.data});\n}\n"
  },
  {
    "path": "lib/utils/check_update.dart",
    "content": "// import 'dart:convert';\n// import 'dart:io';\n\n// import 'package:device_info_plus/device_info_plus.dart';\n// import 'package:flutter/material.dart';\n// import 'package:http/http.dart' as http;\n// import 'package:pub_semver/pub_semver.dart';\n\n// import '../app_config.dart';\n\n// Future<UpdateInfo?> checkUpdate({BaseDeviceInfo? deviceInfo}) async {\n//   try {\n//     final response = await http.get(appConfig.updateUri,\n//         headers: {'Accept': 'application/vnd.github+json'});\n//     Map update = jsonDecode(response.body);\n//     Version currentVersion = Version.parse(appConfig.codeName);\n\n//     Version remoteVersion =\n//         Version.parse(update['tag_name'].toString().replaceAll('v', ''));\n\n//     int comparison = remoteVersion.compareTo(currentVersion);\n\n//     if (comparison > 0) {\n//       if (deviceInfo == null) {\n//         final deviceInfoPlugin = DeviceInfoPlugin();\n//         deviceInfo = await deviceInfoPlugin.deviceInfo;\n//       }\n\n//       Map? supportedAsset;\n//       List assets = update['assets'];\n//       if (Platform.isAndroid) {\n//         List<String> supportedAbis =\n//             deviceInfo.data['supportedAbis'].cast<String>();\n\n//         for (var supportedAbi in supportedAbis) {\n//           List supportedAssets = assets\n//               .where((asset) => asset['name'].contains(supportedAbi))\n//               .toList();\n//           if (supportedAssets.isNotEmpty) {\n//             supportedAsset = supportedAssets.first;\n//             break;\n//           }\n//         }\n//       } else if (Platform.isWindows) {\n//         List supportedAssets = assets\n//             .where(\n//               (asset) =>\n//                   asset[\"content_type\"] == \"application/x-msdownload\" ||\n//                   asset['name'].toString().endsWith('.exe'),\n//             )\n//             .toList();\n//         supportedAsset =\n//             supportedAssets.isNotEmpty ? supportedAssets.first : null;\n//       }\n//       if (supportedAsset == null) return null;\n//       int downloadCount = 0;\n//       for (var asset in assets) {\n//         downloadCount += (asset['download_count'] as int);\n//       }\n//       return UpdateInfo(\n//         name: update['name'],\n//         publishedAt: update['published_at'],\n//         body: update['body'],\n//         downloadUrl: supportedAsset['browser_download_url'],\n//         downloadCount: downloadCount,\n//       );\n//     } else {\n//       return null;\n//     }\n//   } catch (e) {\n//     debugPrint(\"Exception in checkUpdate: $e\");\n//     return null;\n//   }\n// }\n\n// class UpdateInfo {\n//   String name;\n//   String publishedAt;\n//   String body;\n//   String downloadUrl;\n//   int downloadCount;\n//   UpdateInfo({\n//     required this.name,\n//     required this.publishedAt,\n//     required this.body,\n//     required this.downloadCount,\n//     required this.downloadUrl,\n//   });\n//   Map<String, dynamic> toMap() {\n//     return {\n//       'name': name,\n//       'publishedAt': publishedAt,\n//       'body': body,\n//       'downloadUrl': downloadUrl,\n//       'downloadCount': downloadCount,\n//     };\n//   }\n// }\n"
  },
  {
    "path": "lib/utils/enhanced_image.dart",
    "content": "String getEnhancedImage(String imageUrl,\n    {String quality = 'high', double? width, double? dp}) {\n  if (width != null) {\n    int newWidth = ((dp ?? 1) * width).ceil();\n    return imageUrl\n        .trim()\n        .replaceAll('w60-h60', 'w${newWidth.toInt()}-h${newWidth.toInt()}')\n        .replaceAll('w226-h226', 'w${newWidth.toInt()}-h${newWidth.toInt()}')\n        .replaceAll('w540-h225', 'w${newWidth.toInt()}-h${newWidth.toInt()}')\n        .replaceAll('w544-h544', 'w${newWidth.toInt()}-h${newWidth.toInt()}')\n        .replaceAll('=s192', '=s$newWidth')\n        .replaceAll('=s1200', '=s$newWidth')\n        .replaceAll('sddefault', 'mqdefault');\n  }\n  switch (quality) {\n    case 'high':\n      return imageUrl\n          .trim()\n          .replaceAll('w60-h60', 'w500-h500')\n          .replaceAll('w226-h226', 'w500-h500')\n          .replaceAll('w540-h225', 'w500-h500')\n          .replaceAll('w544-h544', 'w500-h500')\n          .replaceAll('sddefault', 'mqdefault');\n    case 'medium':\n      return imageUrl\n          .trim()\n          .replaceAll('w60-h60', 'w300-h300')\n          .replaceAll('w226-h226', 'w300-h300')\n          .replaceAll('w540-h225', 'w300-h300')\n          .replaceAll('w544-h544', 'w300-h300')\n          .replaceAll('sddefault', 'mqdefault');\n    case 'low':\n      return imageUrl\n          .trim()\n          .replaceAll('w60-h60', 'w100-h100')\n          .replaceAll('w226-h226', 'w100-h100')\n          .replaceAll('w540-h225', 'w100-h100')\n          .replaceAll('w544-h544', 'w100-h100')\n          .replaceAll('sddefault', 'mqdefault');\n\n    default:\n      return imageUrl.trim().replaceAll('sddefault', 'maxresdefault');\n  }\n}\n"
  },
  {
    "path": "lib/utils/extensions.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\n\nimport '../services/settings_manager.dart';\n\nextension DarkMode on BuildContext {\n  /// is dark mode currently enabled?\n  bool get isDarkMode {\n    final themeMode = select<SettingsManager, ThemeMode>((s) => s.themeMode);\n    final brightness = themeMode == ThemeMode.system\n        ? MediaQuery.of(this).platformBrightness\n        : themeMode == ThemeMode.dark\n        ? Brightness.dark\n        : Brightness.light;\n    return brightness == Brightness.dark;\n  }\n\n  bool get isDarkModeOnce {\n    final brightness = read<SettingsManager>().themeMode == ThemeMode.system\n        ? MediaQuery.of(this).platformBrightness\n        : read<SettingsManager>().themeMode == ThemeMode.dark\n        ? Brightness.dark\n        : Brightness.light;\n    return brightness == Brightness.dark;\n  }\n\n  Color get subtitleColor =>\n      isDarkMode ? Colors.white.withAlpha(150) : Colors.black.withAlpha(150);\n  Color get bottomModalBackgroundColor => isDarkMode\n      ? Color.alphaBlend(Colors.black.withAlpha(220), Colors.white)\n      : Colors.white;\n}\n\nextension LayoutExtensions on BuildContext {\n  bool get isKeyboardSpaceLimited {\n    final mediaQuery = MediaQuery.of(this);\n    final isLandscape = mediaQuery.orientation == Orientation.landscape;\n    return isLandscape && mediaQuery.size.height < 450;\n  }\n}\n\nextension StringMani on String {\n  String get breakWord {\n    String breakWord = '';\n    for (var element in runes) {\n      breakWord += String.fromCharCode(element);\n      breakWord += '\\u200B';\n    }\n    return breakWord;\n  }\n}\n"
  },
  {
    "path": "lib/utils/format_duration.dart",
    "content": "String formatDuration(Duration duration) {\n  String twoDigits(int n) => n.toString().padLeft(2, \"\");\n  String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));\n  String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));\n\n  String newHours = duration.inHours > 0 ? '${duration.inHours}:' : '';\n  String newMinutes = int.parse(twoDigitMinutes) > 0 ? '$twoDigitMinutes:' : '';\n\n  return \"$newHours$newMinutes$twoDigitSeconds\";\n}\n"
  },
  {
    "path": "lib/utils/internet_guard.dart",
    "content": "// import 'dart:async';\n// import 'package:flutter/material.dart';\n// import 'package:get_it/get_it.dart';\n// import 'package:go_router/go_router.dart';\n// import 'package:gyawun/generated/l10n.dart';\n// import 'package:gyawun/themes/colors.dart';\n// import 'package:gyawun/utils/adaptive_widgets/buttons.dart';\n// import 'package:gyawun/utils/adaptive_widgets/icons.dart';\n// import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart';\n// import 'package:yt_music/ytmusic.dart';\n\n// class InternetGuard extends StatefulWidget {\n//   final Widget child;\n//   final VoidCallback? onInternetLost;\n//   final VoidCallback? onInternetRestored;\n\n//   const InternetGuard({\n//     super.key,\n//     required this.child,\n//     this.onInternetLost,\n//     this.onInternetRestored,\n//   });\n\n//   @override\n//   State<InternetGuard> createState() => _InternetGuardState();\n// }\n\n// class _InternetGuardState extends State<InternetGuard> {\n//   int _lastHandledError = 0;\n//   bool _isOfflineMode = false;\n//   ValueNotifier<int> get lastConnectionError =>\n//       GetIt.I<YTMusic>().lastConnectionError;\n//   StreamSubscription<InternetStatus>? _networkSubscription;\n//   final InternetConnection _internetConnection = InternetConnection();\n\n//   @override\n//   void initState() {\n//     super.initState();\n//     lastConnectionError.addListener(_onConnectionErrorDetected);\n//     _lastHandledError = lastConnectionError.value;\n//   }\n\n//   @override\n//   void dispose() {\n//     _networkSubscription?.cancel();\n//     lastConnectionError.removeListener(_onConnectionErrorDetected);\n//     super.dispose();\n//   }\n\n//   void _onConnectionErrorDetected() {\n//     if (!mounted) return;\n//     final currentErrorTime = lastConnectionError.value;\n//     if (currentErrorTime > _lastHandledError) {\n//       _lastHandledError = currentErrorTime;\n//       _enterOfflineMode();\n//     }\n//   }\n\n//   void _enterOfflineMode() {\n//     if (_isOfflineMode) return;\n//     setState(() {\n//       _isOfflineMode = true;\n//     });\n//     widget.onInternetLost?.call();\n//     _startMonitoringNetwork();\n//   }\n\n//   void _startMonitoringNetwork() async {\n//     _networkSubscription?.cancel();\n//     // Prevent flickering loops: only auto-restore if we transition from Offline to Online.\n//     // If we are already connected (API error), we wait for manual retry.\n//     InternetStatus lastKnownStatus = await _internetConnection.internetStatus;\n//     _networkSubscription = _internetConnection.onStatusChange.listen((status) {\n//       if (lastKnownStatus == InternetStatus.disconnected &&\n//           status == InternetStatus.connected) {\n//         _tryRestoreConnection();\n//       }\n//     });\n//   }\n\n//   void _tryRestoreConnection() {\n//     if (!mounted || !_isOfflineMode) return;\n//     setState(() {\n//       _isOfflineMode = false;\n//     });\n//     _networkSubscription?.cancel();\n//     _networkSubscription = null;\n//     widget.onInternetRestored?.call();\n//   }\n\n//   @override\n//   Widget build(BuildContext context) {\n//     return Stack(\n//       children: [\n//         widget.child,\n//         if (_isOfflineMode)\n//           Scaffold(\n//             backgroundColor: Theme.of(context).scaffoldBackgroundColor,\n//             body: Center(\n//               child: Column(\n//                 mainAxisAlignment: MainAxisAlignment.center,\n//                 children: [\n//                   Icon(AdaptiveIcons.wifi_off_rounded,\n//                       size: 80, color: greyColor),\n//                   const SizedBox(height: 20),\n//                   Text(\n//                     S.of(context).No_Internet_Connection,\n//                     textAlign: TextAlign.center,\n//                     style: const TextStyle(\n//                         fontSize: 18, fontWeight: FontWeight.bold),\n//                   ),\n//                   const SizedBox(height: 20),\n//                   AdaptiveFilledButton(\n//                     onPressed: () => context.go('/saved/downloads'),\n//                     child: Text(S.of(context).Go_To_Downloads),\n//                   ),\n//                   const SizedBox(height: 20),\n//                   OutlinedButton.icon(\n//                     icon: const Icon(Icons.refresh),\n//                     label: Text(S.of(context).Retry),\n//                     onPressed: () {\n//                       _tryRestoreConnection();\n//                     },\n//                   ),\n//                 ],\n//               ),\n//             ),\n//           ),\n//       ],\n//     );\n//   }\n// }\n"
  },
  {
    "path": "lib/utils/playlist_icon.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:m3e_collection/m3e_collection.dart';\n\nabstract class PlaylistIcon {\n  final String id;\n\n  const PlaylistIcon(this.id);\n\n  String toId() => id;\n}\n\nclass MaterialPlaylistIcon extends PlaylistIcon {\n  final IconData iconData;\n\n  const MaterialPlaylistIcon(super.id, this.iconData);\n}\n\nclass PolygonPlaylistIcon extends PlaylistIcon {\n  final RoundedPolygon polygon;\n\n  const PolygonPlaylistIcon(super.id, this.polygon);\n}\n"
  },
  {
    "path": "lib/utils/playlist_icon_widget.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:gyawun/core/widgets/rounded_polygon_icon.dart';\n\nimport 'playlist_icon.dart';\n\nclass MaterialIconWidget extends StatelessWidget {\n  final MaterialPlaylistIcon data;\n  final double size;\n\n  const MaterialIconWidget({super.key, required this.data, required this.size});\n\n  @override\n  Widget build(BuildContext context) {\n    return Icon(\n      data.iconData,\n      size: size,\n      color: Theme.of(context).colorScheme.onPrimaryContainer,\n    );\n  }\n}\n\nclass PolygonIconWidget extends StatelessWidget {\n  final PolygonPlaylistIcon data;\n  final double size;\n\n  const PolygonIconWidget({super.key, required this.data, required this.size});\n\n  @override\n  Widget build(BuildContext context) {\n    return RoundedPolygonIcon(\n      polygon: data.polygon,\n      size: size,\n      color: Theme.of(context).colorScheme.onPrimaryContainer,\n    );\n  }\n}\n\nclass PlaylistIconWidget extends StatelessWidget {\n  final PlaylistIcon data;\n  final double size;\n\n  const PlaylistIconWidget({super.key, required this.data, required this.size});\n\n  @override\n  Widget build(BuildContext context) {\n    return switch (data) {\n      MaterialPlaylistIcon m => MaterialIconWidget(data: m, size: size),\n      PolygonPlaylistIcon p => PolygonIconWidget(data: p, size: size),\n      _ => SizedBox(width: size, height: size),\n    };\n  }\n}\n"
  },
  {
    "path": "lib/utils/playlist_icons.dart",
    "content": "import 'package:collection/collection.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:gyawun/utils/playlist_icon.dart';\nimport 'package:m3e_collection/m3e_collection.dart';\nimport 'package:font_awesome_flutter/font_awesome_flutter.dart';\n\nclass PlaylistIcons {\n  PlaylistIcons._();\n\n  static final musicNoteList = MaterialPlaylistIcon(\n    'musicNoteList',\n    CupertinoIcons.music_note_list,\n  );\n  static final compactDisc = MaterialPlaylistIcon(\n    'compactDisc',\n    FontAwesomeIcons.compactDisc,\n  );\n  static final radio = MaterialPlaylistIcon('radio', FontAwesomeIcons.radio);\n  static final podcast = MaterialPlaylistIcon(\n    'podcast',\n    FontAwesomeIcons.podcast,\n  );\n  static final guitar = MaterialPlaylistIcon('guitar', FontAwesomeIcons.guitar);\n  static final drum = MaterialPlaylistIcon('drum', FontAwesomeIcons.drum);\n  static final dumbbell = MaterialPlaylistIcon(\n    'dumbbell',\n    FontAwesomeIcons.dumbbell,\n  );\n  static final personRunning = MaterialPlaylistIcon(\n    'person_running',\n    FontAwesomeIcons.personRunning,\n  );\n  static final atom = MaterialPlaylistIcon('atom', FontAwesomeIcons.atom);\n  static final circleRadiation = MaterialPlaylistIcon(\n    'circleRadiation',\n    FontAwesomeIcons.circleRadiation,\n  );\n  static final spa = MaterialPlaylistIcon('spa', FontAwesomeIcons.spa);\n  static final bed = MaterialPlaylistIcon('bed', FontAwesomeIcons.bed);\n  static final sun = MaterialPlaylistIcon('sun', FontAwesomeIcons.sun);\n  static final water = MaterialPlaylistIcon('water', FontAwesomeIcons.water);\n  static final fire = MaterialPlaylistIcon('fire', FontAwesomeIcons.fire);\n  static final wind = MaterialPlaylistIcon('wind', FontAwesomeIcons.wind);\n  static final car = MaterialPlaylistIcon('car', FontAwesomeIcons.car);\n  static final motorcycle = MaterialPlaylistIcon(\n    'motorcycle',\n    FontAwesomeIcons.motorcycle,\n  );\n  static final fly = MaterialPlaylistIcon('fly', FontAwesomeIcons.fly);\n  static final dna = MaterialPlaylistIcon('dna', FontAwesomeIcons.dna);\n  static final skull = MaterialPlaylistIcon('skull', FontAwesomeIcons.skull);\n  static final virus = MaterialPlaylistIcon('virus', FontAwesomeIcons.virus);\n  static final flask = MaterialPlaylistIcon('flask', FontAwesomeIcons.flask);\n  static final faceLaugh = MaterialPlaylistIcon(\n    'faceLaugh',\n    FontAwesomeIcons.faceLaugh,\n  );\n  static final faceSadCry = MaterialPlaylistIcon(\n    'faceSadCry',\n    FontAwesomeIcons.faceSadCry,\n  );\n  static final brain = MaterialPlaylistIcon('brain', FontAwesomeIcons.brain);\n  static final earthAmericas = MaterialPlaylistIcon(\n    'earthAmericas',\n    FontAwesomeIcons.earthAmericas,\n  );\n  static final heartCrack = MaterialPlaylistIcon(\n    'heartCrack',\n    FontAwesomeIcons.heartCrack,\n  );\n  static final spotify = MaterialPlaylistIcon(\n    'spotify',\n    FontAwesomeIcons.spotify,\n  );\n  static final pizzaSlice = MaterialPlaylistIcon(\n    'pizzaSlice',\n    FontAwesomeIcons.pizzaSlice,\n  );\n  static final pill = PolygonPlaylistIcon('pill', MaterialShapes.pill);\n  static final arrow = PolygonPlaylistIcon('arrow', MaterialShapes.arrow);\n  static final boom = PolygonPlaylistIcon('boom', MaterialShapes.boom);\n  static final circle = PolygonPlaylistIcon('circle', MaterialShapes.circle);\n  static final clover4Leaf = PolygonPlaylistIcon(\n    'clover4Leaf',\n    MaterialShapes.clover4Leaf,\n  );\n\n  static List<PlaylistIcon> values = [\n    musicNoteList,\n    compactDisc,\n    radio,\n    podcast,\n    guitar,\n    drum,\n    dumbbell,\n    personRunning,\n    atom,\n    circleRadiation,\n    spa,\n    bed,\n    sun,\n    water,\n    fire,\n    wind,\n    car,\n    motorcycle,\n    fly,\n    dna,\n    skull,\n    virus,\n    flask,\n    faceLaugh,\n    faceSadCry,\n    brain,\n    earthAmericas,\n    heartCrack,\n    spotify,\n    pizzaSlice,\n    pill,\n    arrow,\n    boom,\n    circle,\n    clover4Leaf,\n  ];\n\n  static PlaylistIcon byId(String id) =>\n      values.firstWhereOrNull((icon) => icon.toId() == id) ?? values.first;\n}\n"
  },
  {
    "path": "lib/utils/playlist_thumbnail.dart",
    "content": "import 'dart:math';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';\nimport 'package:gyawun/utils/song_thumbnail.dart';\n\nclass PlaylistThumbnail extends StatefulWidget {\n  final List playlist;\n  final double size;\n  final double radius;\n\n  const PlaylistThumbnail({\n    super.key,\n    required this.playlist,\n    required this.size,\n    this.radius = 0,\n  });\n\n  @override\n  State<PlaylistThumbnail> createState() => _PlaylistThumbnailState();\n}\n\nclass _PlaylistThumbnailState extends State<PlaylistThumbnail> {\n  List _itemsToDisplay = [];\n\n  @override\n  void initState() {\n    super.initState();\n    _calculateItems(forceUpdate: true);\n  }\n\n  @override\n  void didUpdateWidget(covariant PlaylistThumbnail oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    _calculateItems();\n  }\n\n  void _calculateItems({bool forceUpdate = false}) {\n    final int count = min(widget.playlist.length, 4);\n    final List sublist = widget.playlist.sublist(0, count);\n    final List<String> currentIds =\n        sublist.map((e) => e['videoId'].toString()).toList();\n    final List<String> cachedIds =\n        _itemsToDisplay.map((e) => e['videoId'].toString()).toList();\n    if (!forceUpdate && listEquals(cachedIds, currentIds)) {\n      return;\n    }\n    if (forceUpdate) {\n      _itemsToDisplay = sublist;\n    } else {\n      setState(() {\n        _itemsToDisplay = sublist;\n      });\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ClipRRect(\n      borderRadius: BorderRadius.circular(widget.radius),\n      child: SizedBox(\n        height: widget.size,\n        width: widget.size,\n        child: ClipRRect(\n          borderRadius: .circular(8),\n          child: StaggeredGrid.count(\n            crossAxisCount: _itemsToDisplay.length > 1 ? 2 : 1,\n            children: _itemsToDisplay.indexed.map((ind) {\n              int index = ind.$1;\n              Map song = ind.$2;\n              return SongThumbnail(\n                key: ValueKey(song['videoId']),\n                song: song,\n                height: (_itemsToDisplay.length <= 2 ||\n                        (_itemsToDisplay.length == 3 && index == 0))\n                    ? widget.size\n                    : widget.size / 2,\n                width: _itemsToDisplay.length > 1 ? widget.size / 2 : widget.size,\n                fit: BoxFit.cover,\n              );\n            }).toList(),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/pprint.dart",
    "content": "import 'dart:convert';\nimport 'dart:developer';\n\nvoid pprint(Object? data) {\n  const JsonEncoder encoder = JsonEncoder.withIndent('  ');\n  final jsonString = encoder.convert(data);\n  log(jsonString);\n}\n"
  },
  {
    "path": "lib/utils/router.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter/cupertino.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:gyawun/screens/browse/browse_page.dart';\nimport 'package:gyawun/screens/chip/chip_page.dart';\nimport 'package:gyawun/screens/home/home_page.dart';\nimport 'package:gyawun/screens/library/downloads/downloading/downloading_page.dart';\nimport 'package:gyawun/screens/library/downloads/downloads_page.dart';\nimport 'package:gyawun/screens/library/downloads/playlist/download_playlist_page.dart';\nimport 'package:gyawun/screens/library/favourites/favourites_page.dart';\nimport 'package:gyawun/screens/library/history/history_page.dart';\nimport 'package:gyawun/screens/library/library_page.dart';\nimport 'package:gyawun/screens/library/playlist/playlist_details_page.dart';\nimport 'package:gyawun/screens/player/player_page.dart';\nimport 'package:gyawun/screens/search/search_page.dart';\nimport 'package:gyawun/screens/settings/about/about_page.dart';\nimport 'package:gyawun/screens/settings/appearance/appearance_page.dart';\nimport 'package:gyawun/screens/settings/backup_storage/backup_storage_page.dart';\nimport 'package:gyawun/screens/settings/player/equalizer/equalizer_page.dart';\nimport 'package:gyawun/screens/settings/player/player_settings_page.dart';\nimport 'package:gyawun/screens/settings/privacy/privacy_page.dart';\nimport 'package:gyawun/screens/settings/services/yt_music/yt_music_page.dart';\nimport 'package:gyawun/screens/settings/settings_page.dart';\nimport 'package:gyawun/screens/shell/app_shell.dart';\n\nGoRouter router = GoRouter(\n  initialLocation: '/',\n  routes: [\n    ShellRoute(\n      builder: (context, state, child) => child,\n      routes: [\n        StatefulShellRoute(\n          branches: branches,\n          builder: (context, state, navigationShell) => AppShell(\n            navigationShell: navigationShell,\n          ),\n          navigatorContainerBuilder: (context, navigationShell, children) =>\n              MyPageView(\n            currentIndex: navigationShell.currentIndex,\n            children: children,\n          ),\n        ),\n        GoRoute(\n          path: '/player',\n          pageBuilder: (context, state) {\n            final videoId = state.extra as String?;\n            return CustomTransitionPage(\n              key: state.pageKey,\n              child: PlayerPage(videoId: videoId),\n              transitionsBuilder:\n                  (context, animation, secondaryAnimation, child) {\n                const begin = Offset(0.0, 1.0);\n                const end = Offset.zero;\n                final curve = Curves.ease;\n                final tween = Tween(begin: begin, end: end)\n                    .chain(CurveTween(curve: curve));\n                return SlideTransition(\n                  position: animation.drive(tween),\n                  child: child,\n                );\n              },\n            );\n          },\n        ),\n      ],\n    ),\n  ],\n);\n\nList<StatefulShellBranch> branches = [\n  StatefulShellBranch(\n    routes: [\n      GoRoute(\n          path: '/',\n          builder: (context, state) => const HomePage(),\n          routes: [\n            GoRoute(\n              path: 'chip',\n              builder: (context, state) {\n                Map<String, dynamic> args = state.extra as Map<String, dynamic>;\n                return ChipPage(\n                    title: args['title'] ?? '',\n                    endpoint: args['endpoint'] ?? {});\n              },\n            ),\n            GoRoute(\n              path: 'browse',\n              builder: (context, state) {\n                final args = state.extra as Map<String, dynamic>? ?? {};\n                return BrowsePage(\n                  endpoint: args['endpoint'] as Map<String, dynamic>,\n                  isMore: args['isMore'] as bool? ?? false,\n                );\n              },\n            ),\n            GoRoute(\n              path: 'search',\n              builder: (context, state) {\n                final args = state.extra as Map<String, dynamic>?;\n                return SearchPage(\n                  endpoint: args?['endpoint'] as Map<String, dynamic>?,\n                  isMore: args?['isMore'] as bool? ?? false,\n                );\n              },\n            ),\n          ]),\n    ],\n  ),\n  StatefulShellBranch(routes: [\n    GoRoute(\n      path: '/library',\n      builder: (context, state) => const LibraryPage(),\n      routes: [\n        GoRoute(\n          path: 'favourites',\n          builder: (context, state) => const FavouritesPage(),\n        ),\n        GoRoute(\n          path: 'downloads',\n          builder: (context, state) => const DownloadsPage(),\n          routes: [\n            GoRoute(\n              path: 'download_playlist',\n              builder: (context, state) {\n                final args = state.extra as Map<String, dynamic>;\n                return DownloadPlaylistPage(\n                  playlistId: args['playlistId'] as String,\n                );\n              },\n            ),\n            GoRoute(\n              path: 'downloading',\n              builder: (context, state) => const DownloadingPage(),\n            ),\n          ],\n        ),\n        GoRoute(\n          path: 'history',\n          builder: (context, state) => const HistoryPage(),\n        ),\n        GoRoute(\n          path: 'playlist_details',\n          builder: (context, state) {\n            final args = state.extra as Map<String, dynamic>;\n            return PlaylistDetailsPage(\n              playlistkey: args['playlistkey'] as String,\n            );\n          },\n        ),\n      ],\n    ),\n  ]),\n  StatefulShellBranch(routes: [\n    GoRoute(\n        path: '/settings',\n        builder: (context, state) => const SettingsPage(),\n        routes: [\n          GoRoute(\n            path: 'appearance',\n            builder: (context, state) => const AppearancePage(),\n          ),\n          GoRoute(\n              path: 'player',\n              builder: (context, state) => const PlayerSettingsPage(),\n              routes: [\n                GoRoute(\n                  path: 'equalizer',\n                  builder: (context, state) => const EqualizerPage(),\n                )\n              ]),\n          GoRoute(\n            path: 'services/ytmusic',\n            builder: (context, state) => const YTMusicPage(),\n          ),\n          GoRoute(\n            path: 'backup_storage',\n            builder: (context, state) => const BackupStoragePage(),\n          ),\n          GoRoute(\n            path: 'privacy',\n            builder: (context, state) => const PrivacyPage(),\n          ),\n          GoRoute(\n            path: 'about',\n            builder: (context, state) => const AboutPage(),\n          ),\n        ]),\n  ])\n];\n\nclass MyPageView extends StatefulWidget {\n  final int currentIndex;\n  final List<Widget> children;\n\n  const MyPageView(\n      {super.key, required this.currentIndex, required this.children});\n\n  @override\n  MyPageViewState createState() => MyPageViewState();\n}\n\nclass MyPageViewState extends State<MyPageView> {\n  final PageController controller = PageController(initialPage: 0);\n\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  void didUpdateWidget(covariant MyPageView oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.currentIndex != widget.currentIndex) {\n      controller.animateToPage(widget.currentIndex,\n          duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return PageView(\n      scrollDirection:\n          (Platform.isWindows || MediaQuery.of(context).size.width >= 450)\n              ? Axis.vertical\n              : Axis.horizontal,\n      physics: const NeverScrollableScrollPhysics(),\n      controller: controller,\n      children: widget.children,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/song_thumbnail.dart",
    "content": "import 'package:audiotags/audiotags.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:gyawun/utils/enhanced_image.dart';\n\nimport '../services/download_manager.dart';\n\nclass SongThumbnail extends StatefulWidget {\n  final Map song;\n  final double? dp;\n  final double? width;\n  final double? height;\n  final FilterQuality filterQuality;\n  final BoxFit? fit;\n  final Widget Function(BuildContext, String, Object)? errorWidget;\n  final void Function(ImageProvider)? onImageReady;\n\n  const SongThumbnail({\n    super.key,\n    required this.song,\n    this.dp,\n    this.height,\n    this.width,\n    this.filterQuality = FilterQuality.high,\n    this.fit,\n    this.errorWidget,\n    this.onImageReady,\n  });\n\n  @override\n  State<SongThumbnail> createState() => _SongThumbnailState();\n}\n\nclass _SongThumbnailState extends State<SongThumbnail> {\n  MemoryImage? _localImageProvider;\n  bool _isCheckingLocal = true;\n  ImageProvider? _lastNotifiedProvider;\n\n  @override\n  void initState() {\n    super.initState();\n    _checkLocalThumbnail();\n  }\n\n  @override\n  void didUpdateWidget(covariant SongThumbnail oldWidget) {\n    super.didUpdateWidget(oldWidget);\n\n    final oldId = oldWidget.song['videoId'] ?? '';\n    final newId = widget.song['videoId'] ?? '';\n\n    if (oldId != newId) {\n      _lastNotifiedProvider = null;\n      _checkLocalThumbnail();\n    }\n  }\n\n  Future<void> _checkLocalThumbnail() async {\n    if (!_isCheckingLocal) setState(() => _isCheckingLocal = true);\n    MemoryImage? foundImage;\n    final downloadSong = GetIt.I<DownloadManager>().getDownload(\n      widget.song['videoId'],\n    );\n    if (downloadSong != null &&\n        downloadSong['status'] == \"DOWNLOADED\" &&\n        downloadSong['path'] != null) {\n      try {\n        final Tag? tag = await AudioTags.read(downloadSong['path']);\n        if (tag?.pictures.isNotEmpty == true) {\n          foundImage = MemoryImage(tag!.pictures.first.bytes);\n        }\n      } catch (e) {\n        debugPrint(\"Errore lettura tag: $e\");\n        _localImageProvider = null;\n        _isCheckingLocal = false;\n      }\n    }\n    if (!mounted) return;\n    setState(() {\n      _localImageProvider = foundImage;\n      _isCheckingLocal = false;\n    });\n  }\n\n  Widget _buildDisplayImage(ImageProvider provider) {\n    if (widget.onImageReady != null && provider != _lastNotifiedProvider) {\n      _lastNotifiedProvider = provider;\n      WidgetsBinding.instance.addPostFrameCallback((_) {\n        if (mounted) widget.onImageReady!(provider);\n      });\n    }\n    return RepaintBoundary(\n      child: Image(\n        image: provider,\n        height: widget.height,\n        width: widget.width,\n        fit: widget.fit,\n        filterQuality: widget.filterQuality,\n        gaplessPlayback: true,\n      ),\n    );\n  }\n\n  Widget _buildCachedNetworkImage(List<String> urls, int index) {\n    return RepaintBoundary(\n      child: CachedNetworkImage(\n        imageUrl: urls[index],\n        height: widget.height,\n        width: widget.width,\n        fit: widget.fit,\n        filterQuality: widget.filterQuality,\n        imageBuilder: (context, provider) => _buildDisplayImage(provider),\n        placeholder: (context, url) =>\n            SizedBox(height: widget.height, width: widget.width),\n        errorWidget: (index + 1 < urls.length)\n            ? (context, url, error) => _buildCachedNetworkImage(urls, index + 1)\n            : widget.errorWidget,\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    if (_isCheckingLocal) {\n      return SizedBox(height: widget.height, width: widget.width);\n    }\n    if (_localImageProvider != null) {\n      return _buildDisplayImage(_localImageProvider!);\n    }\n    final String baseUrl = widget.song['thumbnails'].first['url'];\n    final List<String> urls = [\n      getEnhancedImage(baseUrl, dp: widget.dp, width: widget.width),\n      getEnhancedImage(baseUrl, quality: 'medium'),\n      getEnhancedImage(baseUrl, quality: 'low'),\n    ];\n    return _buildCachedNetworkImage(urls, 0);\n  }\n}\n"
  },
  {
    "path": "lib/utils/text_controller_builder.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass TextControllerBuilder extends StatefulWidget {\n  final String? initialText;\n  final Widget Function(BuildContext context, TextEditingController controller)\n      builder;\n\n  const TextControllerBuilder({\n    super.key,\n    this.initialText,\n    required this.builder,\n  });\n\n  @override\n  State<TextControllerBuilder> createState() => _TextControllerBuilderState();\n}\n\nclass _TextControllerBuilderState extends State<TextControllerBuilder> {\n  late final TextEditingController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = TextEditingController(text: widget.initialText);\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.builder(context, _controller);\n  }\n}\n"
  },
  {
    "path": "pubspec.yaml",
    "content": "name: gyawun\ndescription: \"A new Flutter project.\"\npublish_to: 'none'\nversion: 2.0.18-beta.1+218\n\nenvironment:\n  sdk: '>=3.10.0 <4.0.0'\ndependencies:\n  flutter:\n    sdk: flutter\n  flutter_localizations:\n    sdk: flutter\n\n  cupertino_icons: ^1.0.6\n  go_router: ^16.2.4\n  google_fonts: ^6.2.1\n  intl: any\n  http: ^1.5.0\n  hive: ^2.2.3\n  hive_flutter: ^1.1.0\n  youtube_explode_dart:\n    git:\n      url: https://github.com/sheikhhaziq/youtube_explode_dart.git\n  expandable_page_view: ^1.0.17\n  get_it: ^8.0.3\n  cached_network_image: ^3.3.1\n  flutter_typeahead: ^5.2.0\n  expandable_text: ^2.3.0\n  salomon_bottom_bar: ^3.3.2\n  share_plus: ^12.0.0\n  provider: ^6.1.2\n  flutter_staggered_grid_view: ^0.7.0\n  flutter_swipe_action_cell: ^3.1.3\n  just_audio: ^0.10.5\n  just_audio_background: ^0.0.1-beta.14\n  audio_video_progress_bar: ^2.0.3\n  sliding_up_panel: ^2.0.0+1\n  url_launcher: ^6.3.0\n  country_picker: ^2.0.26\n  text_scroll: ^0.2.0\n  duration_picker: ^1.2.0\n  path_provider: ^2.1.3\n  \n  path: ^1.9.0\n  file_picker: ^10.1.9\n  dynamic_color: ^1.7.0\n  audiotags: ^1.4.3\n  pub_semver: ^2.1.4\n  device_info_plus: ^12.1.0\n  flutter_expandable_fab: ^2.1.0\n  flutter_lyric: ^2.0.4+6\n  media_kit_libs_windows_audio: any\n  media_kit_libs_linux: any\n  just_audio_media_kit: ^2.0.6\n  media_kit_native_event_loop: any\n  receive_sharing_intent:\n    git:\n      url: https://github.com/sheikhhaziq/receive_sharing_intent.git\n  fl_toast: ^3.2.0 # Discontinued\n  flutter_colorpicker: ^1.1.0\n  easy_folder_picker: ^1.3.3\n  permission_handler: ^11.4.0\n  http_server: ^1.0.0\n  rxdart: ^0.28.0\n  translator: ^1.0.4+1\n  language_detector: ^1.0.1\n  wakelock_plus: ^1.4.0\n  internet_connection_checker_plus: ^2.9.1\n  collection: ^1.19.1\n  flutter_bloc: ^9.1.1\n  bloc: ^9.2.0\n  meta: ^1.17.0\n  loading_indicator_m3e: ^0.1.1\n  # m3e_collection: ^0.3.7\n  package_info_plus: ^9.0.0\n  flutter_markdown_plus: ^1.0.7\n  yt_music:\n    git:\n      url: https://github.com/sheikhhaziq/yt_music.git\n  connectivity_plus: ^7.0.0\n  audio_service_mpris: ^0.2.0\n  fluentui_system_icons: ^1.1.273\n  navigation_rail_m3e: ^0.3.5\n  m3e_collection: ^0.3.7\n  font_awesome_flutter: ^10.12.0\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^6.0.0\n  change_app_package_name: ^1.3.0\n  flutter_native_splash: ^2.4.7\n  intl_utils: ^2.5.0\nflutter:\n  uses-material-design: true\n  generate: true\n  assets:\n    - assets/images/\nflutter_intl:\n  enabled: true\n"
  },
  {
    "path": "test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility in the flutter_test package. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nimport 'package:gyawun/main.dart';\n\nvoid main() {\n  testWidgets('Counter increments smoke test', (WidgetTester tester) async {\n    // Build our app and trigger a frame.\n    await tester.pumpWidget(const Gyawun());\n\n    // Verify that our counter starts at 0.\n    expect(find.text('0'), findsOneWidget);\n    expect(find.text('1'), findsNothing);\n\n    // Tap the '+' icon and trigger a frame.\n    await tester.tap(find.byIcon(Icons.add));\n    await tester.pump();\n\n    // Verify that our counter has incremented.\n    expect(find.text('0'), findsNothing);\n    expect(find.text('1'), findsOneWidget);\n  });\n}\n"
  }
]