Full Code of asmroneapp/Yuro for AI

main 2817087af4a0 cached
318 files
818.7 KB
215.1k tokens
1131 symbols
1 requests
Download .txt
Showing preview only (927K chars total). Download the full file or copy to clipboard to get everything.
Repository: asmroneapp/Yuro
Branch: main
Commit: 2817087af4a0
Files: 318
Total size: 818.7 KB

Directory structure:
gitextract_mk7l9x6b/

├── .github/
│   ├── scripts/
│   │   └── process_commits.sh
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .metadata
├── LICENSE
├── README.md
├── README_en.md
├── analysis_options.yaml
├── android/
│   ├── .gitignore
│   ├── app/
│   │   ├── build.gradle
│   │   ├── proguard-rules.pro
│   │   └── src/
│   │       ├── debug/
│   │       │   └── AndroidManifest.xml
│   │       ├── main/
│   │       │   ├── AndroidManifest.xml
│   │       │   ├── kotlin/
│   │       │   │   ├── com/
│   │       │   │   │   └── example/
│   │       │   │   │       └── asmrapp/
│   │       │   │   │           └── MainActivity.kt
│   │       │   │   └── one/
│   │       │   │       └── asmr/
│   │       │   │           └── yuro/
│   │       │   │               ├── MainActivity.kt
│   │       │   │               └── lyric/
│   │       │   │                   ├── LyricOverlayPlugin.kt
│   │       │   │                   └── LyricOverlayService.kt
│   │       │   └── res/
│   │       │       ├── drawable/
│   │       │       │   └── launch_background.xml
│   │       │       ├── drawable-v21/
│   │       │       │   └── launch_background.xml
│   │       │       ├── layout/
│   │       │       │   └── lyric_overlay.xml
│   │       │       ├── values/
│   │       │       │   └── styles.xml
│   │       │       ├── values-night/
│   │       │       │   └── styles.xml
│   │       │       └── xml/
│   │       │           └── network_security_config.xml
│   │       └── profile/
│   │           └── AndroidManifest.xml
│   ├── build.gradle
│   ├── gradle/
│   │   └── wrapper/
│   │       └── gradle-wrapper.properties
│   ├── gradle.properties
│   └── settings.gradle
├── devtools_options.yaml
├── docs/
│   ├── architecture.md
│   ├── audio_architecture.md
│   ├── guidelines.md
│   ├── guidelines_en.md
│   └── guidelines_zh.md
├── ios/
│   ├── .gitignore
│   ├── Flutter/
│   │   ├── AppFrameworkInfo.plist
│   │   ├── Debug.xcconfig
│   │   └── Release.xcconfig
│   ├── Podfile
│   ├── Runner/
│   │   ├── AppDelegate.swift
│   │   ├── Assets.xcassets/
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   └── Contents.json
│   │   │   └── LaunchImage.imageset/
│   │   │       ├── Contents.json
│   │   │       └── README.md
│   │   ├── Base.lproj/
│   │   │   ├── LaunchScreen.storyboard
│   │   │   └── Main.storyboard
│   │   ├── Info.plist
│   │   └── Runner-Bridging-Header.h
│   ├── Runner.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   ├── contents.xcworkspacedata
│   │   │   └── xcshareddata/
│   │   │       ├── IDEWorkspaceChecks.plist
│   │   │       └── WorkspaceSettings.xcsettings
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── Runner.xcscheme
│   ├── Runner.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       ├── IDEWorkspaceChecks.plist
│   │       └── WorkspaceSettings.xcsettings
│   └── RunnerTests/
│       └── RunnerTests.swift
├── lib/
│   ├── common/
│   │   └── constants/
│   │       └── strings.dart
│   ├── core/
│   │   ├── audio/
│   │   │   ├── README.md
│   │   │   ├── audio_player_handler.dart
│   │   │   ├── audio_player_service.dart
│   │   │   ├── audio_service.dart
│   │   │   ├── cache/
│   │   │   │   └── audio_cache_manager.dart
│   │   │   ├── controllers/
│   │   │   │   └── playback_controller.dart
│   │   │   ├── events/
│   │   │   │   ├── playback_event.dart
│   │   │   │   └── playback_event_hub.dart
│   │   │   ├── i_audio_player_service.dart
│   │   │   ├── models/
│   │   │   │   ├── audio_track_info.dart
│   │   │   │   ├── file_path.dart
│   │   │   │   ├── play_mode.dart
│   │   │   │   ├── playback_context.dart
│   │   │   │   └── subtitle.dart
│   │   │   ├── notification/
│   │   │   │   └── audio_notification_service.dart
│   │   │   ├── state/
│   │   │   │   └── playback_state_manager.dart
│   │   │   ├── storage/
│   │   │   │   ├── i_playback_state_repository.dart
│   │   │   │   └── playback_state_repository.dart
│   │   │   └── utils/
│   │   │       ├── audio_error_handler.dart
│   │   │       ├── playlist_builder.dart
│   │   │       └── track_info_creator.dart
│   │   ├── cache/
│   │   │   └── recommendation_cache_manager.dart
│   │   ├── di/
│   │   │   └── service_locator.dart
│   │   ├── platform/
│   │   │   ├── dummy_lyric_overlay_controller.dart
│   │   │   ├── i_lyric_overlay_controller.dart
│   │   │   ├── lyric_overlay_controller.dart
│   │   │   ├── lyric_overlay_manager.dart
│   │   │   └── wakelock_controller.dart
│   │   ├── subtitle/
│   │   │   ├── cache/
│   │   │   │   └── subtitle_cache_manager.dart
│   │   │   ├── i_subtitle_service.dart
│   │   │   ├── managers/
│   │   │   │   └── subtitle_state_manager.dart
│   │   │   ├── parsers/
│   │   │   │   ├── lrc_parser.dart
│   │   │   │   ├── subtitle_parser.dart
│   │   │   │   ├── subtitle_parser_factory.dart
│   │   │   │   └── vtt_parser.dart
│   │   │   ├── subtitle_loader.dart
│   │   │   ├── subtitle_service.dart
│   │   │   └── utils/
│   │   │       └── subtitle_matcher.dart
│   │   └── theme/
│   │       ├── app_colors.dart
│   │       ├── app_theme.dart
│   │       └── theme_controller.dart
│   ├── data/
│   │   ├── models/
│   │   │   ├── audio/
│   │   │   │   └── README.md
│   │   │   ├── auth/
│   │   │   │   └── auth_resp/
│   │   │   │       ├── auth_resp.dart
│   │   │   │       ├── auth_resp.freezed.dart
│   │   │   │       ├── auth_resp.g.dart
│   │   │   │       ├── user.dart
│   │   │   │       ├── user.freezed.dart
│   │   │   │       └── user.g.dart
│   │   │   ├── files/
│   │   │   │   ├── child.dart
│   │   │   │   ├── child.freezed.dart
│   │   │   │   ├── child.g.dart
│   │   │   │   ├── files.dart
│   │   │   │   ├── files.freezed.dart
│   │   │   │   ├── files.g.dart
│   │   │   │   ├── work.dart
│   │   │   │   ├── work.freezed.dart
│   │   │   │   └── work.g.dart
│   │   │   ├── mark_lists/
│   │   │   │   ├── mark_lists.dart
│   │   │   │   ├── mark_lists.freezed.dart
│   │   │   │   ├── mark_lists.g.dart
│   │   │   │   ├── pagination.dart
│   │   │   │   ├── pagination.freezed.dart
│   │   │   │   ├── pagination.g.dart
│   │   │   │   ├── playlist.dart
│   │   │   │   ├── playlist.freezed.dart
│   │   │   │   └── playlist.g.dart
│   │   │   ├── mark_status.dart
│   │   │   ├── my_lists/
│   │   │   │   ├── README.md
│   │   │   │   └── my_playlists/
│   │   │   │       ├── my_playlists.dart
│   │   │   │       ├── my_playlists.freezed.dart
│   │   │   │       ├── my_playlists.g.dart
│   │   │   │       ├── pagination.dart
│   │   │   │       ├── pagination.freezed.dart
│   │   │   │       ├── pagination.g.dart
│   │   │   │       ├── playlist.dart
│   │   │   │       ├── playlist.freezed.dart
│   │   │   │       └── playlist.g.dart
│   │   │   ├── playback/
│   │   │   │   ├── playback_state.dart
│   │   │   │   ├── playback_state.freezed.dart
│   │   │   │   └── playback_state.g.dart
│   │   │   ├── playlists_with_exist_statu/
│   │   │   │   ├── pagination.dart
│   │   │   │   ├── pagination.freezed.dart
│   │   │   │   ├── pagination.g.dart
│   │   │   │   ├── playlist.dart
│   │   │   │   ├── playlist.freezed.dart
│   │   │   │   ├── playlist.g.dart
│   │   │   │   ├── playlists_with_exist_statu.dart
│   │   │   │   ├── playlists_with_exist_statu.freezed.dart
│   │   │   │   └── playlists_with_exist_statu.g.dart
│   │   │   └── works/
│   │   │       ├── circle.dart
│   │   │       ├── circle.freezed.dart
│   │   │       ├── circle.g.dart
│   │   │       ├── en_us.dart
│   │   │       ├── en_us.freezed.dart
│   │   │       ├── en_us.g.dart
│   │   │       ├── i18n.dart
│   │   │       ├── i18n.freezed.dart
│   │   │       ├── i18n.g.dart
│   │   │       ├── ja_jp.dart
│   │   │       ├── ja_jp.freezed.dart
│   │   │       ├── ja_jp.g.dart
│   │   │       ├── language_edition.dart
│   │   │       ├── language_edition.freezed.dart
│   │   │       ├── language_edition.g.dart
│   │   │       ├── other_language_editions_in_db.dart
│   │   │       ├── other_language_editions_in_db.freezed.dart
│   │   │       ├── other_language_editions_in_db.g.dart
│   │   │       ├── pagination.dart
│   │   │       ├── pagination.freezed.dart
│   │   │       ├── pagination.g.dart
│   │   │       ├── tag.dart
│   │   │       ├── tag.freezed.dart
│   │   │       ├── tag.g.dart
│   │   │       ├── translation_bonus_lang.dart
│   │   │       ├── translation_bonus_lang.freezed.dart
│   │   │       ├── translation_bonus_lang.g.dart
│   │   │       ├── translation_info.dart
│   │   │       ├── translation_info.freezed.dart
│   │   │       ├── translation_info.g.dart
│   │   │       ├── work.dart
│   │   │       ├── work.freezed.dart
│   │   │       ├── work.g.dart
│   │   │       ├── works.dart
│   │   │       ├── works.freezed.dart
│   │   │       ├── works.g.dart
│   │   │       ├── zh_cn.dart
│   │   │       ├── zh_cn.freezed.dart
│   │   │       └── zh_cn.g.dart
│   │   ├── repositories/
│   │   │   ├── audio/
│   │   │   │   └── README.md
│   │   │   └── auth_repository.dart
│   │   └── services/
│   │       ├── api_service.dart
│   │       ├── auth_service.dart
│   │       └── interceptors/
│   │           └── auth_interceptor.dart
│   ├── main.dart
│   ├── presentation/
│   │   ├── layouts/
│   │   │   ├── work_layout_config.dart
│   │   │   └── work_layout_strategy.dart
│   │   ├── models/
│   │   │   └── filter_state.dart
│   │   ├── viewmodels/
│   │   │   ├── auth_viewmodel.dart
│   │   │   ├── base/
│   │   │   │   └── paginated_works_viewmodel.dart
│   │   │   ├── detail_viewmodel.dart
│   │   │   ├── favorites_viewmodel.dart
│   │   │   ├── home_viewmodel.dart
│   │   │   ├── player_viewmodel.dart
│   │   │   ├── playlist_works_viewmodel.dart
│   │   │   ├── playlists_viewmodel.dart
│   │   │   ├── popular_viewmodel.dart
│   │   │   ├── recommend_viewmodel.dart
│   │   │   ├── search_viewmodel.dart
│   │   │   ├── settings/
│   │   │   │   └── cache_manager_viewmodel.dart
│   │   │   └── similar_works_viewmodel.dart
│   │   └── widgets/
│   │       └── auth/
│   │           └── login_dialog.dart
│   ├── screens/
│   │   ├── contents/
│   │   │   ├── home_content.dart
│   │   │   ├── playlists/
│   │   │   │   ├── playlist_works_view.dart
│   │   │   │   └── playlists_list_view.dart
│   │   │   ├── playlists_content.dart
│   │   │   ├── popular_content.dart
│   │   │   └── recommend_content.dart
│   │   ├── detail_screen.dart
│   │   ├── docs/
│   │   │   └── main_screen.md
│   │   ├── favorites_screen.dart
│   │   ├── main_screen.dart
│   │   ├── player_screen.dart
│   │   ├── search_screen.dart
│   │   ├── settings/
│   │   │   └── cache_manager_screen.dart
│   │   └── similar_works_screen.dart
│   ├── utils/
│   │   ├── file_size_formatter.dart
│   │   └── logger.dart
│   └── widgets/
│       ├── common/
│       │   └── tag_chip.dart
│       ├── detail/
│       │   ├── mark_selection_dialog.dart
│       │   ├── playlist_selection_dialog.dart
│       │   ├── work_action_buttons.dart
│       │   ├── work_cover.dart
│       │   ├── work_file_item.dart
│       │   ├── work_files_list.dart
│       │   ├── work_files_skeleton.dart
│       │   ├── work_folder_item.dart
│       │   ├── work_info.dart
│       │   ├── work_info_header.dart
│       │   └── work_stats_info.dart
│       ├── drawer_menu.dart
│       ├── filter/
│       │   ├── filter_panel.dart
│       │   └── filter_with_keyword.dart
│       ├── lyrics/
│       │   └── components/
│       │       ├── lyric_line.dart
│       │       └── player_lyric_view.dart
│       ├── mini_player/
│       │   ├── mini_player.dart
│       │   ├── mini_player_controls.dart
│       │   ├── mini_player_cover.dart
│       │   └── mini_player_progress.dart
│       ├── pagination_controls.dart
│       ├── player/
│       │   ├── player_controls.dart
│       │   ├── player_cover.dart
│       │   ├── player_progress.dart
│       │   ├── player_seek_controls.dart
│       │   └── player_work_info.dart
│       ├── work_card/
│       │   ├── components/
│       │   │   ├── work_cover_image.dart
│       │   │   ├── work_footer.dart
│       │   │   ├── work_info_section.dart
│       │   │   ├── work_tags_panel.dart
│       │   │   └── work_title.dart
│       │   └── work_card.dart
│       ├── work_grid/
│       │   ├── components/
│       │   │   ├── grid_content.dart
│       │   │   ├── grid_empty.dart
│       │   │   ├── grid_error.dart
│       │   │   └── grid_loading.dart
│       │   ├── enhanced_work_grid_view.dart
│       │   └── models/
│       │       └── grid_config.dart
│       ├── work_grid.dart
│       ├── work_grid_view.dart
│       └── work_row.dart
├── linux/
│   ├── .gitignore
│   ├── CMakeLists.txt
│   ├── flutter/
│   │   ├── CMakeLists.txt
│   │   ├── generated_plugin_registrant.cc
│   │   ├── generated_plugin_registrant.h
│   │   └── generated_plugins.cmake
│   ├── main.cc
│   ├── my_application.cc
│   └── my_application.h
├── macos/
│   ├── .gitignore
│   ├── Flutter/
│   │   ├── Flutter-Debug.xcconfig
│   │   ├── Flutter-Release.xcconfig
│   │   └── GeneratedPluginRegistrant.swift
│   ├── Podfile
│   ├── Runner/
│   │   ├── AppDelegate.swift
│   │   ├── Assets.xcassets/
│   │   │   └── AppIcon.appiconset/
│   │   │       └── Contents.json
│   │   ├── Base.lproj/
│   │   │   └── MainMenu.xib
│   │   ├── Configs/
│   │   │   ├── AppInfo.xcconfig
│   │   │   ├── Debug.xcconfig
│   │   │   ├── Release.xcconfig
│   │   │   └── Warnings.xcconfig
│   │   ├── DebugProfile.entitlements
│   │   ├── Info.plist
│   │   ├── MainFlutterWindow.swift
│   │   └── Release.entitlements
│   ├── Runner.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   └── xcshareddata/
│   │   │       └── IDEWorkspaceChecks.plist
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── Runner.xcscheme
│   ├── Runner.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   └── RunnerTests/
│       └── RunnerTests.swift
├── pubspec.yaml
├── test/
│   └── widget_test.dart
├── web/
│   ├── index.html
│   └── manifest.json
└── windows/
    ├── .gitignore
    ├── CMakeLists.txt
    ├── flutter/
    │   ├── CMakeLists.txt
    │   ├── generated_plugin_registrant.cc
    │   ├── generated_plugin_registrant.h
    │   └── generated_plugins.cmake
    └── runner/
        ├── CMakeLists.txt
        ├── Runner.rc
        ├── flutter_window.cpp
        ├── flutter_window.h
        ├── main.cpp
        ├── resource.h
        ├── runner.exe.manifest
        ├── utils.cpp
        ├── utils.h
        ├── win32_window.cpp
        └── win32_window.h

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

================================================
FILE: .github/scripts/process_commits.sh
================================================
#!/bin/bash

# 主标题的 emoji 映射
process_commit() {
  local title="$1"
  case "$title" in
    feat:*|feature:*) echo "✨ ${title#*: }" ;; # 新功能
    fix:*) echo "🐛 ${title#*: }" ;; # 修复
    docs:*) echo "📝 ${title#*: }" ;; # 文档
    style:*) echo "💄 ${title#*: }" ;; # 样式
    refactor:*) echo "♻️ ${title#*: }" ;; # 重构
    perf:*) echo "⚡️ ${title#*: }" ;; # 性能
    test:*) echo "🧪 ${title#*: }" ;; # 测试
    build:*) echo "📦 ${title#*: }" ;; # 构建
    ci:*) echo "🎡 ${title#*: }" ;; # CI
    chore:*) echo "🔧 ${title#*: }" ;; # 杂项
    revert:*) echo "⏮️ ${title#*: }" ;; # 回退
    *) 
      case "$title" in
        Add*|Implement*) echo "✨ $title" ;; 
        Enhance*|Improve*) echo "🚀 $title" ;; 
        Update*) echo "⚡️ $title" ;; 
        Integrate*|Configure*) echo "🔌 $title" ;; 
        Fix*|Resolve*) echo "🐛 $title" ;; 
        Refactor*) echo "♻️ $title" ;; 
        Remove*|Delete*) echo "🔥 $title" ;; 
        Revert*) echo "⏮️ $title" ;; 
        *) echo "🔧 $title" ;; 
      esac
      ;;
  esac
}

# 处理详细信息
process_details() {
  local details=""
  local in_commit_body=false
  
  while IFS= read -r line; do
    if [[ $line == -* ]]; then
      # 处理列表项
      details+="$(process_detail "$line")\n"
    elif [[ $line =~ ^These[[:space:]]changes ]]; then
      # 处理总结行,保持原样并添加换行
      details+="     $line\n"
    elif [[ -n $line ]]; then
      # 处理其他非空行(比如列表项的延续行),保持缩进
      details+="     $line\n"
    else
      # 空行处理
      details+="\n"
    fi
  done
  
  echo -e "$details"
}

# 详细信息的 emoji 映射
process_detail() {
  local content="${1:2}" # 删除开头的 "- "
  local prefix="     - " # 修改缩进格式,保持列表形式
  
  # 1. 首先检查动词开头
  case "$content" in
    Replace*|Swap*|Change*) echo "${prefix}🔄 $content" && return ;;
    Increase*|Add*|Extend*) echo "${prefix}⬆️ $content" && return ;;
    Decrease*|Reduce*|Remove*) echo "${prefix}⬇️ $content" && return ;;
    Update*|Refresh*) echo "${prefix}🔁 $content" && return ;;
    Enhance*|Improve*) echo "${prefix}⚡️ $content" && return ;;
    Create*|Generate*) echo "${prefix}✨ $content" && return ;;
    Modify*|Adjust*) echo "${prefix}🔧 $content" && return ;;
    Fix*|Resolve*) echo "${prefix}🐛 $content" && return ;;
    Refactor*) echo "${prefix}♻️ $content" && return ;;
    Implement*) echo "${prefix}🎯 $content" && return ;;
    Integrate*) echo "${prefix}🔌 $content" && return ;;
    Ensure*|Verify*) echo "${prefix}✅ $content" && return ;;
    Develop*) echo "${prefix}🏗️ $content" && return ;;
  esac
  
  # 2. 检查特定功能/组件组合
  if [[ "$content" =~ (audio.*player|player.*audio) ]]; then
    echo "${prefix}🎵 $content" # 音频播放器特定
    return
  fi
  
  if [[ "$content" =~ (lyric.*overlay|overlay.*lyric) ]]; then
    echo "${prefix}📺 $content" # 歌词覆盖层特定
    return
  fi
  
  if [[ "$content" =~ (cache.*response|response.*cache) ]]; then
    echo "${prefix}💾 $content" # 响应缓存特定
    return
  fi
  
  if [[ "$content" =~ (error.*handling|handling.*error) ]]; then
    echo "${prefix}🛡️ $content" # 错误处理特定
    return
  fi
  
  # 3. 检查特定技术术语
  if [[ "$content" =~ dependency.injection ]]; then
    echo "${prefix}💉 $content" # 依赖注入
    return
  fi
  
  if [[ "$content" =~ state.management ]]; then
    echo "${prefix}📊 $content" # 状态管理
    return
  fi
  
  # 4. 检查具体内容类型
  case "$content" in
    *cache*|*Cache*|*storage*) 
      echo "${prefix}💾 $content" ;; # 缓存/存储相关
    *API*|*service*|*Service*|*request*) 
      echo "${prefix}🌐 $content" ;; # API/服务相关
    *UI*|*Screen*|*interface*|*layout*|*visual*|*theme*) 
      echo "${prefix}💫 $content" ;; # UI/布局/主题相关
    *audio*|*playback*|*media*) 
      echo "${prefix}🎵 $content" ;; # 音频相关
    *test*|*Test*) 
      echo "${prefix}🧪 $content" ;; # 测试相关
    *security*|*permission*|*auth*) 
      echo "${prefix}🔒 $content" ;; # 安全/权限相关
    *document*|*template*|*readability*) 
      echo "${prefix}📝 $content" ;; # 文档相关
    *component*|*widget*|*display*) 
      echo "${prefix}🎨 $content" ;; # 组件/显示相关
    *logic*|*handling*|*management*|*dependency*) 
      echo "${prefix}🧮 $content" ;; # 逻辑/处理/依赖相关
    *performance*|*efficiency*|*optimization*) 
      echo "${prefix}⚡️ $content" ;; # 性能相关
    *error*|*exception*|*handling*) 
      echo "${prefix}🛡️ $content" ;; # 错误处理相关
    *animation*|*transition*) 
      echo "${prefix}✨ $content" ;; # 动画相关
    *network*|*connectivity*) 
      echo "${prefix}📡 $content" ;; # 网络相关
    *data*|*model*|*entity*) 
      echo "${prefix}💽 $content" ;; # 数据模型相关
    *button*|*control*|*interaction*) 
      echo "${prefix}🎮 $content" ;; # 控件/交互相关
    *style*|*color*|*font*) 
      echo "${prefix}🎨 $content" ;; # 样式相关
    *) 
      echo "${prefix}📌 $content" ;; # 其他细节
  esac
}

# 主处理逻辑
current_commit=""
commit_details=""

while IFS= read -r line; do
  if [[ $line =~ ^[A-Za-z] ]] && [[ ! $line =~ ^These[[:space:]]changes ]]; then
    # 如果有之前的 commit,先输出它
    if [ -n "$current_commit" ]; then
      if [ -n "$commit_details" ]; then
        echo "▶ $current_commit"
        echo -e "$(process_details "$commit_details")\n"
      else
        echo "  $current_commit"
      fi
    fi
    current_commit=$(process_commit "$line")
    commit_details=""
  elif [[ $line == -* ]]; then
    commit_details+="$line\n"
  fi
done

# 输出最后一个 commit
if [ -n "$current_commit" ]; then
  if [ -n "$commit_details" ]; then
    echo "▶ $current_commit"
    echo -e "$(process_details "$commit_details")"
  else
    echo "  $current_commit"
  fi
fi 

================================================
FILE: .github/workflows/build.yml
================================================
name: Build and Release

permissions:
  contents: write

on:
  push:
    tags:
      - 'v*'  # 当推送 v 开头的tag时触发,如 v1.0.0
  workflow_dispatch:

env:
  FLUTTER_VERSION: '3.27.0'

jobs:
  build-android:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0  # 获取完整的 git 历史

      - name: Setup Java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '17'

      - name: Setup Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: ${{ env.FLUTTER_VERSION }}
          channel: 'stable'

      - name: Get dependencies
        run: flutter pub get

      - name: Create key.properties
        run: |
          echo "storePassword=${{ secrets.KEY_STORE_PASSWORD }}" >> android/key.properties
          echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
          echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties
          echo "storeFile=upload-keystore.jks" >> android/key.properties

      - name: Create keystore file
        run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/upload-keystore.jks

      - name: Build APK
        run: flutter build apk --release

      - name: Build App Bundle
        run: flutter build appbundle --release

      - name: Upload Android artifacts
        uses: actions/upload-artifact@v4
        with:
          name: android-build
          path: |
            build/app/outputs/flutter-apk/app-release.apk
            build/app/outputs/bundle/release/app-release.aab

  build-ios:
    runs-on: macos-latest

    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0  # 获取完整的 git 历史

      - name: Setup Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.27.0'
          channel: 'stable'

      - name: Get dependencies
        run: flutter pub get

      - name: Build iOS
        run: flutter build ios --release --no-codesign

      - name: Package IPA
        run: |
          cd build/ios/iphoneos/
          mkdir Payload
          cp -r Runner.app Payload/
          zip -qq -r -9 app-release.ipa Payload

      - name: Upload iOS artifacts
        uses: actions/upload-artifact@v4
        with:
          name: ios-build
          path: build/ios/iphoneos/app-release.ipa

  upload:
    runs-on: ubuntu-latest
    needs: [ build-android, build-ios ]

    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0  # 获取完整的 git 历史

      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          path: ./dist/
          merge-multiple: true

      - name: List files
        run: tree dist

      - name: Prepare scripts
        run: |
          mkdir -p .github/scripts
          chmod +x .github/scripts/process_commits.sh

      - name: Get Previous tag
        id: previoustag
        run: |
          CURRENT_TAG=${GITHUB_REF#refs/tags/}
          PREVIOUS_TAG=$(git describe --tags --abbrev=0 ${CURRENT_TAG}^ 2>/dev/null || echo "v0.0.0")
          echo "tag=${PREVIOUS_TAG}" >> $GITHUB_OUTPUT

      - name: Generate commit messages
        id: commits
        run: |
          CURRENT_TAG=${GITHUB_REF#refs/tags/}
          PREV_TAG=${{ steps.previoustag.outputs.tag }}
          COMMITS=$(git log ${PREV_TAG}..${CURRENT_TAG} --pretty=format:"%s%n%b" | .github/scripts/process_commits.sh)
          echo "commits<<EOF" >> $GITHUB_OUTPUT
          echo "$COMMITS" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Create Release
        if: startsWith(github.ref, 'refs/tags/')
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          draft: false
          body: |
            ## 🚧 Pre-release Version
            
            ### 📋 Release Information
            **Version:** ${{ github.ref_name }}
            **Previous Version:** ${{ steps.previoustag.outputs.tag }}
            **Build Environment:** Flutter ${{ env.FLUTTER_VERSION }}
            
            ### 📝 Changelog
            ${{ steps.commits.outputs.commits }}
            
            ### 📦 Distribution
            | File | Description | Purpose |
            |------|-------------|----------|
            | `.apk` | Android Package | Direct installation for testing |
            | `.aab` | Android App Bundle | Google Play Store deployment |
            
            ### 🔍 Additional Notes
            - This is a pre-release build intended for testing purposes
            - Features and functionality may not be fully stable
            - Not recommended for production use
            
            ### 📱 Compatibility
            - Minimum Android SDK: 21 (Android 5.0)
            - Target Android SDK: 33 (Android 13)
            
            > **Note:** Please report any issues or bugs through the GitHub issue tracker.
          files: |
            dist/flutter-apk/app-release.apk
            dist/bundle/release/app-release.aab
            dist/app-release.ipa
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload artifacts if not release
        if: startsWith(github.ref, 'refs/tags/') == false
        uses: actions/upload-artifact@v4
        with:
          name: everything
          path: dist/


================================================
FILE: .gitignore
================================================
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

# 添加以下内容
**/android/key.properties
**/android/app/upload-keystore.jks


================================================
FILE: .metadata
================================================
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
  revision: "2663184aa79047d0a33a14a3b607954f8fdd8730"
  channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
  platforms:
    - platform: root
      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
    - platform: android
      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
    - platform: ios
      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
    - platform: linux
      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
    - platform: macos
      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
    - platform: web
      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
    - platform: windows
      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730

  # User provided section

  # List of Local paths (relative to this file) that should be
  # ignored by the migrate tool.
  #
  # Files that are not part of the templates will be ignored by default.
  unmanaged_files:
    - 'lib/main.dart'
    - 'ios/Runner.xcodeproj/project.pbxproj'


================================================
FILE: LICENSE
================================================
# Creative Commons Attribution-NonCommercial-ShareAlike License (CC BY-NC-SA)

## License Summary

This license lets others remix, tweak, and build upon your work non-commercially, as long as they credit you and license their new creations under the identical terms.

## Full License

### 1. You are free to:

- Share — copy and redistribute the material in any medium or format.
- Adapt — remix, transform, and build upon the material.

### 2. Under the following terms:

- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
- NonCommercial — You may not use the material for commercial purposes.
- ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.

### 3. No additional restrictions:

You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.

### 4. Notices:

You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.

### 5. Other rights:

In no way are any of the following rights affected by the license:

- Your fair dealing or fair use rights;
- The rights of others to use the material for their own purposes;
- The rights of the licensor to use the material for their own purposes.

### 6. Disclaimer:

This license does not grant you any rights to use the material in a way that would infringe on the rights of others.

For more information, visit [Creative Commons](https://creativecommons.org/licenses/by-nc-sa/4.0/).

================================================
FILE: README.md
================================================
# Yuro

[English](README_en.md)

一个使用 Flutter 构建的 ASMR.ONE 客户端。

## 项目概述

Yuro 旨在通过精美的动画和现代化的用户界面,提供流畅愉悦的 ASMR 聆听体验。

## 特性

- 稳定的后台播放,再也不用担心杀后台了
- 精美的动画效果
- 流畅的播放体验
- 简洁的UI设计
- 全方位的智能缓存机制
  - 图片智能缓存:优化封面加载速度,告别重复加载
  - 字幕本地缓存:实现快速字幕匹配与加载
  - 音频文件缓存:减少重复下载,节省流量开销
- 为服务器减轻压力
  - 智能的缓存策略确保资源高效利用
  - 懒加载机制避免无效请求
  - 合理的缓存清理机制平衡本地存储

## 开发准则

我们维护了一套完整的开发准则以确保代码质量和一致性:
- [开发准则](docs/guidelines_zh.md)

## 项目结构

<pre>
lib/
├── core/                 # 核心功能
├── data/                # 数据层
├── domain/              # 领域层
├── presentation/        # 表现层
└── common/             # 通用功能
</pre>

## 开始使用

1. 克隆仓库
```bash
git clone [repository-url]
```

2. 安装依赖
```bash
flutter pub get
```

3. 运行应用
```bash
flutter run
```

## 功能特性

- 现代化UI设计
- 流畅的动画效果
- ASMR 播放控制
- 播放列表管理
- 搜索功能
- 收藏功能

## 贡献指南

在提交贡献之前,请阅读我们的[开发准则](docs/guidelines_zh.md)。

## 许可证

本项目采用 Creative Commons 非商业性使用-相同方式共享许可证 (CC BY-NC-SA) - 查看 [LICENSE](LICENSE) 文件了解详细信息。该许可证允许他人修改和分享您的作品,但禁止商业用途,要求保留署名,并要求对修改后的作品以相同的许可证发布。


================================================
FILE: README_en.md
================================================
# ASMR One App

[中文说明](README.md)

A beautiful and modern ASMR player application built with Flutter.

## Project Overview

ASMR One App is designed to provide a smooth and enjoyable ASMR listening experience with beautiful animations and a modern user interface.

## Development Guidelines

We maintain a comprehensive set of development guidelines to ensure code quality and consistency:
- [Development Guidelines](docs/guidelines_en.md)

## Project Structure

<pre>
lib/
├── core/                 # Core functionality
├── data/                # Data layer
├── domain/              # Domain layer
├── presentation/        # Presentation layer
└── common/             # Common functionality
</pre>

## Getting Started

1. Clone the repository
```bash
git clone [repository-url]
```

2. Install dependencies
```bash
flutter pub get
```

3. Run the app
```bash
flutter run
```

## Features

- Modern UI design
- Smooth animations
- ASMR playback control
- Playlist management
- Search functionality
- Favorites collection

## Contributing

Please read our [Development Guidelines](docs/guidelines_en.md) before making a contribution.

## License

This project is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License (CC BY-NC-SA) - see the [LICENSE](LICENSE) file for details. This license allows others to remix, tweak, and build upon your work non-commercially, as long as they credit you and license their new creations under the identical terms. 

================================================
FILE: analysis_options.yaml
================================================
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
  # The lint rules applied to this project can be customized in the
  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
  # included above or to enable additional rules. A list of all available lints
  # and their documentation is published at https://dart.dev/lints.
  #
  # Instead of disabling a lint rule for the entire project in the
  # section below, it can also be suppressed for a single line of code
  # or a specific dart file by using the `// ignore: name_of_lint` and
  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
  # producing the lint.
  rules:
    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
analyzer:
  exclude:
    - "**/*.g.dart"
    - "**/*.freezed.dart"
  errors:
    invalid_annotation_target: ignore

================================================
FILE: android/.gitignore
================================================
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java

# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks


================================================
FILE: android/app/build.gradle
================================================
plugins {
    id "com.android.application"
    id "kotlin-android"
    // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
    id "dev.flutter.flutter-gradle-plugin"
}

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    namespace = "one.asmr.yuro"
    compileSdk = flutter.compileSdkVersion
    ndkVersion = flutter.ndkVersion

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId = "one.asmr.yuro"
        // You can update the following values to match your application needs.
        // For more information, see: https://flutter.dev/to/review-gradle-config.
        minSdk = flutter.minSdkVersion
        targetSdk = flutter.targetSdkVersion
        versionCode = flutter.versionCode
        versionName = flutter.versionName
    }

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
            // 如果还有问题,可以临时禁用混淆
            minifyEnabled true  // 改为 false 可以禁用混淆
            shrinkResources true  // 改为 false 可以禁用资源压缩
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

flutter {
    source = "../.."
}


================================================
FILE: android/app/proguard-rules.pro
================================================
## Flutter wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.**  { *; }
-keep class io.flutter.util.**  { *; }
-keep class io.flutter.view.**  { *; }
-keep class io.flutter.**  { *; }
-keep class io.flutter.plugins.**  { *; }
-keep class io.flutter.plugin.editing.** { *; }
-dontwarn io.flutter.embedding.**
-keepattributes Signature
-keepattributes *Annotation*

## Gson rules
-keepattributes Signature
-keepattributes *Annotation*
-dontwarn sun.misc.**

## audio_service plugin
-keep class com.ryanheise.audioservice.** { *; }

## Fix Play Store Split
-keep class com.google.android.play.core.splitcompat.** { *; }
-dontwarn com.google.android.play.core.splitcompat.SplitCompatApplication

## Fix for all Android classes that might be accessed via reflection
-keep class androidx.lifecycle.DefaultLifecycleObserver
-keep class androidx.lifecycle.LifecycleOwner
-keepnames class androidx.lifecycle.LifecycleOwner

## Just Audio
-keep class com.google.android.exoplayer2.** { *; }
-dontwarn com.google.android.exoplayer2.**

## Cached network image
-keep class com.bumptech.glide.** { *; }

================================================
FILE: android/app/src/debug/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- The INTERNET permission is required for development. Specifically,
         the Flutter tool needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>


================================================
FILE: android/app/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <application
        android:label="Yuro"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher"
        android:enableOnBackInvokedCallback="true"
        android:networkSecurityConfig="@xml/network_security_config">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
        <service
          android:name="com.ryanheise.audioservice.AudioService"
          android:foregroundServiceType="mediaPlayback"
          android:exported="true">
          <intent-filter>
            <action android:name="android.media.browse.MediaBrowserService" />
          </intent-filter>
        </service>

        <receiver
          android:name="com.ryanheise.audioservice.MediaButtonReceiver"
          android:exported="true">
          <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
          </intent-filter>
        </receiver>
        <service
            android:name=".lyric.LyricOverlayService"
            android:enabled="true"
            android:exported="false" />
    </application>
    <!-- Required to query activities that can process text, see:
         https://developer.android.com/training/package-visibility and
         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.

         In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT"/>
            <data android:mimeType="text/plain"/>
        </intent>
    </queries>
</manifest>


================================================
FILE: android/app/src/main/kotlin/com/example/asmrapp/MainActivity.kt
================================================
package com.example.asmrapp

import io.flutter.embedding.android.FlutterActivity
import com.ryanheise.audioservice.AudioServiceActivity

class MainActivity: AudioServiceActivity()


================================================
FILE: android/app/src/main/kotlin/one/asmr/yuro/MainActivity.kt
================================================
package one.asmr.yuro

import io.flutter.embedding.android.FlutterActivity
import com.ryanheise.audioservice.AudioServiceActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import one.asmr.yuro.lyric.LyricOverlayPlugin

class MainActivity: AudioServiceActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        
        MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            "one.asmr.yuro/lyric_overlay"
        ).setMethodCallHandler(LyricOverlayPlugin(this))
    }
} 

================================================
FILE: android/app/src/main/kotlin/one/asmr/yuro/lyric/LyricOverlayPlugin.kt
================================================
package one.asmr.yuro.lyric

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

class LyricOverlayPlugin(private val context: Context) : MethodCallHandler {
    private var service: LyricOverlayService? = null
    private val serviceIntent by lazy { Intent(context, LyricOverlayService::class.java) }
    private val serviceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
            service = (binder as? LyricOverlayService.LocalBinder)?.service
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            service = null
        }
    }

    override fun onMethodCall(call: MethodCall, result: Result) {
        when (call.method) {
            "initialize" -> {
                try {
                    context.startService(serviceIntent)
                    context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)
                    result.success(null)
                } catch (e: Exception) {
                    result.error(
                        "SERVICE_START_ERROR",
                        e.message,
                        e.toString()
                    )
                }
            }
            "show" -> {
                service?.showLyric("")
                result.success(null)
            }
            "hide" -> {
                service?.hideLyric()
                result.success(null)
            }
            "updateLyric" -> {
                val arguments = call.arguments as? Map<*, *>
                val text = arguments?.get("text") as? String ?: "无字幕"
                service?.showLyric(text)
                result.success(null)
            }
            "dispose" -> {
                context.unbindService(serviceConnection)
                context.stopService(serviceIntent)
                service = null
                result.success(null)
            }
            "isShowing" -> {
                result.success(service?.isShowing() ?: false)
            }
            else -> result.notImplemented()
        }
    }
} 

================================================
FILE: android/app/src/main/kotlin/one/asmr/yuro/lyric/LyricOverlayService.kt
================================================
package one.asmr.yuro.lyric

import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Binder
import android.os.IBinder
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.widget.TextView
import one.asmr.yuro.R
import android.view.Gravity

class LyricOverlayService : Service() {
    private var windowManager: WindowManager? = null
    private var lyricView: View? = null
    private var params: WindowManager.LayoutParams? = null
    private var initialX = 0
    private var initialY = 0
    private var initialTouchX = 0f
    private var initialTouchY = 0f
    private val binder = LocalBinder()
    
    companion object {
        private const val PREFS_NAME = "LyricOverlayPrefs"
        private const val KEY_X = "window_x"
        private const val KEY_Y = "window_y"
        private const val KEY_SHOWING = "is_showing"
    }
    
    inner class LocalBinder : Binder() {
        val service: LyricOverlayService
            get() = this@LyricOverlayService
    }
    
    override fun onBind(intent: Intent?): IBinder = binder
    
    override fun onCreate() {
        super.onCreate()
        windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
    }
    
    fun showLyric(text: String) {
        if (lyricView == null) {
            createLyricView()
        }
        (lyricView as? TextView)?.text = text
        getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
            .edit()
            .putBoolean(KEY_SHOWING, true)
            .apply()
    }
    
    private fun createLyricView() {
        lyricView = LayoutInflater.from(this).inflate(R.layout.lyric_overlay, null)
        
        // 获取屏幕高度
        val displayMetrics = resources.displayMetrics
        val screenHeight = displayMetrics.heightPixels
        
        // 读取保存的位置,默认位置设在屏幕2/3处
        val prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
        val savedX = prefs.getInt(KEY_X, 50)  // 距离右边50dp
        val savedY = prefs.getInt(KEY_Y, (screenHeight * 2 / 3))  // 屏幕高度的2/3处
        
        params = WindowManager.LayoutParams(
            360.dpToPx(),
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
            WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
            PixelFormat.TRANSLUCENT
        ).apply {
            gravity = Gravity.TOP or Gravity.END
            x = savedX
            y = savedY
            windowAnimations = 0
        }
        
        lyricView?.setOnTouchListener { _, event ->
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    initialX = params?.x ?: 0
                    initialY = params?.y ?: 0
                    initialTouchX = event.rawX
                    initialTouchY = event.rawY
                }
                MotionEvent.ACTION_MOVE -> {
                    val dx = (event.rawX - initialTouchX).toInt()
                    val dy = (event.rawY - initialTouchY).toInt()
                    
                    params?.x = initialX - dx
                    params?.y = initialY + dy
                    params?.let { windowManager?.updateViewLayout(lyricView, it) }
                }
                MotionEvent.ACTION_UP -> {
                    // 保存新位置
                    params?.let { params ->
                        getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
                            .edit()
                            .putInt(KEY_X, params.x)
                            .putInt(KEY_Y, params.y)
                            .apply()
                    }
                }
            }
            true
        }
        
        windowManager?.addView(lyricView, params)
    }
    
    private fun Int.dpToPx(): Int {
        val scale = resources.displayMetrics.density
        return (this * scale + 0.5f).toInt()
    }
    
    fun hideLyric() {
        try {
            if (lyricView != null) {
                windowManager?.removeView(lyricView)
                lyricView = null
                getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
                    .edit()
                    .putBoolean(KEY_SHOWING, false)
                    .apply()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        hideLyric()
    }
    
    fun isShowing(): Boolean {
        if (lyricView == null) {
            return getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
                .getBoolean(KEY_SHOWING, false)
        }
        return true
    }
} 

================================================
FILE: android/app/src/main/res/drawable/launch_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/white" />

    <!-- You can insert your own image assets here -->
    <!-- <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/launch_image" />
    </item> -->
</layer-list>


================================================
FILE: android/app/src/main/res/drawable-v21/launch_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="?android:colorBackground" />

    <!-- You can insert your own image assets here -->
    <!-- <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/launch_image" />
    </item> -->
</layer-list>


================================================
FILE: android/app/src/main/res/layout/lyric_overlay.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/lyric_text"
    android:layout_width="360dp"
    android:layout_height="wrap_content"
    android:padding="12dp"
    android:gravity="center_horizontal"
    android:background="#33000000"
    android:textColor="#FFFFFF"
    android:textSize="20sp"
    android:fontFamily="sans-serif-medium"
    android:letterSpacing="0.05"
    android:elevation="4dp"/> 

================================================
FILE: android/app/src/main/res/values/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
    <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
        <!-- Show a splash screen on the activity. Automatically removed when
             the Flutter engine draws its first frame -->
        <item name="android:windowBackground">@drawable/launch_background</item>
    </style>
    <!-- Theme applied to the Android Window as soon as the process has started.
         This theme determines the color of the Android Window while your
         Flutter UI initializes, as well as behind your Flutter UI while its
         running.

         This Theme is only used starting with V2 of Flutter's Android embedding. -->
    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
        <item name="android:windowBackground">?android:colorBackground</item>
    </style>
</resources>


================================================
FILE: android/app/src/main/res/values-night/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
        <!-- Show a splash screen on the activity. Automatically removed when
             the Flutter engine draws its first frame -->
        <item name="android:windowBackground">@drawable/launch_background</item>
    </style>
    <!-- Theme applied to the Android Window as soon as the process has started.
         This theme determines the color of the Android Window while your
         Flutter UI initializes, as well as behind your Flutter UI while its
         running.

         This Theme is only used starting with V2 of Flutter's Android embedding. -->
    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
        <item name="android:windowBackground">?android:colorBackground</item>
    </style>
</resources>


================================================
FILE: android/app/src/main/res/xml/network_security_config.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="false">127.0.0.1</domain>
    </domain-config>
</network-security-config> 

================================================
FILE: android/app/src/profile/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- The INTERNET permission is required for development. Specifically,
         the Flutter tool needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>


================================================
FILE: android/build.gradle
================================================
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.buildDir = "../build"
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
    project.evaluationDependsOn(":app")
}

tasks.register("clean", Delete) {
    delete rootProject.buildDir
}


================================================
FILE: android/gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip


================================================
FILE: android/gradle.properties
================================================
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true


================================================
FILE: android/settings.gradle
================================================
pluginManagement {
    def flutterSdkPath = {
        def properties = new Properties()
        file("local.properties").withInputStream { properties.load(it) }
        def flutterSdkPath = properties.getProperty("flutter.sdk")
        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
        return flutterSdkPath
    }()

    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")

    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

plugins {
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
    id "com.android.application" version "8.1.0" apply false
    id "org.jetbrains.kotlin.android" version "1.8.22" apply false
}

include ":app"


================================================
FILE: devtools_options.yaml
================================================
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:


================================================
FILE: docs/architecture.md
================================================
# ASMR Music App 架构设计

## 目录结构

<pre>
lib/
├── main.dart              # 应用程序入口
├── screens/              # 页面
│   ├── home_screen.dart   # 主页(音乐列表)
│   ├── player_screen.dart # 播放页面
│   └── detail_screen.dart # 详情页面
├── widgets/              # 可重用组件
│   └── drawer_menu.dart   # 侧滑菜单
└── models/              # 数据模型(待添加)
    └── music.dart        # 音乐模型(待添加)
</pre>

## 主要功能模块

1. 主页 (HomeScreen)
   - 显示音乐列表
   - 搜索功能
   - 侧滑菜单访问

2. 播放页 (PlayerScreen)
   - 音乐播放控制
   - 进度条
   - 音量控制

3. 详情页 (DetailScreen)
   - 显示音乐详细信息
   - 评论功能(待实现)
   - 收藏功能(待实现)

4. 侧滑菜单 (DrawerMenu)
   - 主页导航
   - 收藏列表
   - 设置页面

## 技术栈

- Flutter SDK
- Material Design 3
- 路由管理: Flutter 内置导航
- 状态管理: 待定

## 开发计划

1. 第一阶段:基础框架搭建
   - [x] 创建基本页面结构
   - [x] 实现页面导航
   - [x] 设计侧滑菜单

2. 第二阶段:UI 实现
   - [ ] 设计并实现音乐列表
   - [ ] 设计并实现播放器界面
   - [ ] 设计并实现详情页面

3. 第三阶段:功能实现
   - [ ] 音乐播放功能
   - [ ] 搜索功能
   - [ ] 收藏功能

4. 第四阶段:优化
   - [ ] 性能优化
   - [ ] UI/UX 改进
   - [ ] 代码重构

## 注意事项

1. 代码规范
   - 使用 const 构造函数
   - 遵循 Flutter 官方代码风格
   - 添加必要的代码注释

2. 性能考虑
   - 合理使用 StatelessWidget 和 StatefulWidget
   - 避免不必要的重建
   - 图片资源优化

3. 用户体验
   - 添加加载状态提示
   - 错误处理和提示
   - 合理的动画过渡
</pre>

================================================
FILE: docs/audio_architecture.md
================================================
# ASMR One App 音频播放架构设计

## 1. 架构概述

本文档描述了 ASMR One App 音频播放功能的架构设计。遵循 Clean Architecture 原则,采用事件驱动架构,将音频播放功能分为核心层、数据层和表现层。

## 2. 目录结构

<pre>
lib/
├── core/
│   └── audio/                      # 音频核心功能
│       ├── audio_player_service.dart    # 音频服务实现
│       ├── i_audio_player_service.dart  # 音频服务接口
│       ├── controllers/                 # 控制器
│       │   └── playback_controller.dart # 播放控制器
│       ├── events/                      # 事件系统
│       │   ├── playback_event.dart     # 事件定义
│       │   └── playback_event_hub.dart # 事件中心
│       ├── models/                      # 数据模型
│       │   ├── audio_track_info.dart   # 音轨信息
│       │   └── playback_context.dart   # 播放上下文
│       ├── notification/                # 通知栏
│       │   └── audio_notification_service.dart
│       ├── state/                       # 状态管理
│       │   └── playback_state_manager.dart
│       ├── storage/                     # 状态持久化
│       │   ├── i_playback_state_repository.dart
│       │   └── playback_state_repository.dart
│       └── utils/                       # 工具类
│           ├── audio_error_handler.dart
│           ├── playlist_builder.dart
│           └── track_info_creator.dart
└── presentation/
    └── viewmodels/
        └── player_viewmodel.dart   # 播放器视图模型
</pre>

## 3. 核心组件设计

### 3.1 音频服务接口 (IAudioPlayerService)

<pre>
abstract class IAudioPlayerService {
  // 基础播放控制
  Future<void> pause();
  Future<void> resume();
  Future<void> stop();
  Future<void> seek(Duration position);
  Future<void> previous();
  Future<void> next();
  
  // 上下文管理
  Future<void> playWithContext(PlaybackContext context);
  
  // 状态访问
  AudioTrackInfo? get currentTrack;
  PlaybackContext? get currentContext;
  
  // 状态持久化
  Future<void> savePlaybackState();
  Future<void> restorePlaybackState();
}
</pre>

### 3.2 事件系统 (PlaybackEventHub)

<pre>
class PlaybackEventHub {
  // 主事件流
  final _eventSubject = PublishSubject<PlaybackEvent>();
  
  // 分类事件流
  late final Stream<PlaybackStateEvent> playbackState;
  late final Stream<TrackChangeEvent> trackChange;
  late final Stream<PlaybackContextEvent> contextChange;
  late final Stream<PlaybackProgressEvent> playbackProgress;
  late final Stream<PlaybackErrorEvent> errors;
  
  void emit(PlaybackEvent event);
}
</pre>

## 4. 事件模型

### 4.1 播放事件 (PlaybackEvent)

<pre>
abstract class PlaybackEvent {}

class PlaybackStateEvent extends PlaybackEvent {
  final PlayerState state;
  final Duration position;
  final Duration? duration;
}

class TrackChangeEvent extends PlaybackEvent {
  final AudioTrackInfo track;
  final Child file;
  final Work work;
}

// ... 其他事件定义
</pre>

## 5. 状态管理

### 5.1 播放状态管理器 (PlaybackStateManager)

<pre>
class PlaybackStateManager {
  final AudioPlayer _player;
  final PlaybackEventHub _eventHub;
  final IPlaybackStateRepository _stateRepository;
  
  void initStateListeners();
  void updateContext(PlaybackContext? context);
  void updateTrackInfo(AudioTrackInfo track);
  Future<void> saveState();
  Future<PlaybackState?> loadState();
}
</pre>

## 6. 通知栏集成

### 6.1 通知栏服务 (AudioNotificationService)

<pre>
class AudioNotificationService {
  final AudioPlayer _player;
  final PlaybackEventHub _eventHub;
  AudioHandler? _audioHandler;
  
  Future<void> init();
  void updateMetadata(AudioTrackInfo trackInfo);
}
</pre>

## 7. 技术实现细节

### 7.1 依赖注入

使用 GetIt 进行依赖管理:
- PlaybackEventHub 注册为单例
- AudioPlayerService 注册为懒加载单例
- 所有依赖通过构造函数注入

### 7.2 事件驱动

- 使用 RxDart 实现事件流
- 统一的事件中心管理所有播放相关事件
- 各组件通过事件通信,降低耦合

### 7.3 错误处理

- 统一的错误处理机制
- 错误事件通过 EventHub 传递
- 支持错误追踪和日志记录

## 8. 开发计划

1. 优化播放体验
   - 优化事件处理性能
   - 完善错误处理机制
   - 改进状态同步逻辑

2. 增强功能
   - 添加播放列表功能
   - 支持更多播放模式
   - 优化缓存策略

3. 改进架构
   - 进一步解耦组件
   - 优化依赖注入
   - 完善单元测试
</pre>

================================================
FILE: docs/guidelines.md
================================================
# ASMR Music App Development Guidelines
# ASMR Music App 开发准则

## Important Notice | 重要说明

These guidelines are living documents that will evolve with the project. Any changes in practice must be reflected in these guidelines, especially the architecture section which must stay consistent with the actual project structure.

本准则是一个动态文档,会随项目发展而演进。实践中的任何变更都必须更新到本准则中,特别是架构部分必须与实际项目结构保持一致。

## 1. Architecture Design Guidelines | 架构设计准则

### 1.1 Decoupling Principles | 解耦原则
- Follow SOLID principles strictly | 严格遵循 SOLID 原则
- Use dependency injection | 使用依赖注入管理组件依赖
- Implement BLoC pattern | 采用 BLoC 模式分离业务逻辑和UI
- Define interfaces for inter-module communication | 使用接口定义模块间通信契约

### 1.2 Modularization Principles | 模块化原则
- Divide modules by functionality | 按功能划分模块
- Follow single responsibility principle | 遵循单一职责原则
- Clear interface communication | 模块间通过清晰的接口通信
- Share common components | 共享组件放置在 common/shared 目录下

### 1.3 Code Organization | 代码组织
<pre>
lib/
├── core/                 # Core functionality | 核心功能
│   ├── di/              # Dependency injection | 依赖注入
│   ├── theme/           # Theme configuration | 主题配置
│   └── utils/           # Utilities | 工具类
├── data/                # Data layer | 数据层
│   ├── models/          # Data models | 数据模型
│   ├── repositories/    # Data repositories | 数据仓库
│   └── services/        # Service implementations | 服务实现
├── domain/              # Domain layer | 领域层
│   ├── entities/        # Business entities | 业务实体
│   └── repositories/    # Repository interfaces | 仓库接口
├── presentation/        # Presentation layer | 表现层
│   ├── blocs/          # State management | 状态管理
│   ├── screens/        # Pages | 页面
│   └── widgets/        # Components | 组件
└── common/             # Common functionality | 通用功能
    ├── constants/      # Constants | 常量
    └── extensions/     # Extensions | 扩展方法
</pre>

## 2. UI/UX Design Guidelines | UI/UX 设计准则

### 2.1 Interface Design | 界面设计
- Follow Material Design 3 | 遵循 Material Design 3 设计规范
- Consistent color theme and typography | 使用一致的颜色主题和字体
- Maintain visual hierarchy | 保持视觉层次感和空间布局的平衡
- Pixel-perfect alignment | 注重细节,保持像素级别的对齐

### 2.2 Animation Effects | 动画效果
- Use Flutter's built-in animation system | 使用 Flutter 内置动画系统
- Animation duration: 200-300ms | 所有动画持续时间保持在 200-300ms 之间
- Use curve animations (Curves.easeInOut) | 使用曲线动画
- Smooth page transitions | 实现平滑的页面转场效果
- Meaningful micro-interactions | 添加有意义的微交互动画

### 2.3 Performance Optimization | 性能优化
- Use const constructors | 使用 const 构造器
- Proper use of StatelessWidget | 合理使用 StatelessWidget
- Avoid complex calculations in build | 避免在 build 方法中进行复杂计算
- Use ListView.builder for long lists | 使用 ListView.builder 处理长列表
- Image compression and caching | 图片资源进行适当压缩和缓存

## 3. Code Quality Guidelines | 代码质量准则

### 3.1 Code Style | 代码风格
- Follow Dart style guide | 遵循 Dart 官方代码风格指南
- Use dartfmt | 使用 dartfmt 格式化代码
- Type safety | 类型安全,避��使用 dynamic
- Proper documentation | 添加必要的注释,特别是复杂业务逻辑

### 3.2 Testing Standards | 测试规范
- Unit test coverage > 80% | 单元测试覆盖率要求 > 80%
- Widget testing | 编写 Widget 测试验证UI行为
- Integration testing | 集成测试覆盖关键业务流程
- Dependency isolation | 使用 mock 进行依赖隔离

## 4. Version Control Guidelines | 版本控制准则

### 4.1 Git Standards | Git 规范
- Feature Branch workflow | 使用 Feature Branch 工作流
- Angular commit convention | commit 信息遵循 Angular 规范
- Regular code reviews | 定期进行代码审查
- Stable main branch | 保持 main 分支稳定可用

### 4.2 Release Standards | 发布规范
- Semantic versioning | 遵循语义化版本控制
- Clear changelog | 每次发布都要有清晰的更新日志
- Complete testing before release | 重要版本发布前进行完整测试
- Documentation updates | 保留每个版本的文档更新

## 5. Project Management Guidelines | 项目管理准则

### 5.1 Documentation Management | 文档管理
- API documentation | 及时更新 API 文档
- Clear README | 维护清晰的 README
- Design decisions | 记录重要的设计决策
- User and developer guides | 编写用户指南和开发指南

### 5.2 Issue Tracking | 问题追踪
- Track bugs and features | 使用 Issue 跟踪 bug 和新功能
- Proper labeling | 为每个 Issue 添加适当的标签
- Task traceability | 保持任务的可追踪性
- Regular status updates | 定期回顾和更新任务状态 

================================================
FILE: docs/guidelines_en.md
================================================
# ASMR One App Development Guidelines

[中文版本](guidelines_zh.md)

## Important Notice

These guidelines are living documents that will evolve with the project. Any changes in practice must be reflected in these guidelines, especially the architecture section which must stay consistent with the actual project structure.

## 1. Architecture Design Guidelines

### 1.1 Decoupling Principles
- Follow SOLID principles strictly
- Use dependency injection
- Implement BLoC pattern
- Define interfaces for inter-module communication

### 1.2 Modularization Principles
- Divide modules by functionality
- Follow single responsibility principle
- Clear interface communication
- Share common components

### 1.3 Code Organization
<pre>
lib/
├── core/                 # Core functionality
│   ├── di/              # Dependency injection
│   ├── theme/           # Theme configuration
│   └── utils/           # Utilities
├── data/                # Data layer
│   ├── models/          # Data models
│   ├── repositories/    # Data repositories
│   └── services/        # Service implementations
├── domain/              # Domain layer
│   ├── entities/        # Business entities
│   └── repositories/    # Repository interfaces
├── presentation/        # Presentation layer
│   ├── blocs/          # State management
│   ├── screens/        # Pages
│   └── widgets/        # Components
└── common/             # Common functionality
    ├── constants/      # Constants definitions
    └── extensions/     # Extensions
</pre>

### 1.4 String Management
- All text strings must be centrally defined in `lib/common/constants/strings.dart`
- No hardcoded strings allowed in the code
- String constants should be grouped by feature modules
- Prepared for future internationalization
- String names should clearly express their purpose

Example:
```dart
class Strings {
  // App
  static const String appName = 'asmr.one';
  
  // Common
  static const String loading = 'Loading...';
  
  // Feature specific
  static const String search = 'Search';
}
```

## 2. UI/UX Design Guidelines

### 2.1 Interface Design
- Follow Material Design 3
- Consistent color theme and typography
- Maintain visual hierarchy
- Pixel-perfect alignment

### 2.2 Animation Effects
- Use Flutter's built-in animation system
- Animation duration: 200-300ms
- Use curve animations (Curves.easeInOut)
- Smooth page transitions
- Meaningful micro-interactions

### 2.3 Performance Optimization
- Use const constructors
- Proper use of StatelessWidget
- Avoid complex calculations in build
- Use ListView.builder for long lists
- Image compression and caching

## 3. Code Quality Guidelines

### 3.1 Code Style
- Follow Dart style guide
- Use dartfmt
- Type safety
- Proper documentation

### 3.2 Testing Standards
- Unit test coverage > 80%
- Widget testing
- Integration testing
- Dependency isolation

## 4. Version Control Guidelines

### 4.1 Git Standards
- Feature Branch workflow
- Angular commit convention
- Regular code reviews
- Stable main branch

### 4.2 Release Standards
- Semantic versioning
- Clear changelog
- Complete testing before release
- Documentation updates

## 5. Project Management Guidelines

### 5.1 Documentation Management
- API documentation
- Clear README
- Design decisions
- User and developer guides

### 5.2 Issue Tracking
- Track bugs and features
- Proper labeling
- Task traceability
- Regular status updates 

================================================
FILE: docs/guidelines_zh.md
================================================
# ASMR One App 开发准则

[English Version](guidelines_en.md)

## 重要说明

本准则是一个动态文档,会随项目发展而演进。实践中的任何变更都必须更新到本准则中,特别是架构部分必须与实际项目结构保持一致。

## 1. 架构设计准则

### 1.1 解耦原则
- 严格遵循 SOLID 原则
- 使用依赖注入管理组件依赖
- 采用 BLoC 模式分离业务逻辑和UI
- 使用接口定义模块间通信契约

### 1.2 模块化原则
- 按功能划分模块
- 遵循单一职责原则
- 模块间通过清晰的接口通信
- 共享组件放置在 common/shared 目录下

### 1.3 代码组织
<pre>
lib/
├── core/                 # 核心功能
│   ├── di/              # 依赖注入
│   ├── theme/           # 主题配置
│   └── utils/           # 工具类
├── data/                # 数据层
│   ├── models/          # 数据模型
│   ├── repositories/    # 数据仓库
│   └── services/        # 服务实现
├── domain/              # 领域层
│   ├── entities/        # 业务实体
│   └── repositories/    # 仓库接口
├── presentation/        # 表现层
│   ├── blocs/          # 状态管理
│   ├── screens/        # 页面
│   └── widgets/        # 组件
└── common/             # 通用功能
    ├── constants/      # 常量定义
    └── extensions/     # 扩展方法
</pre>

### 1.4 字符串管理
- 所有文本字符串必须在 `lib/common/constants/strings.dart` 中集中定义
- 禁止在代码中使用硬编码的字符串
- 字符串常量按功能模块分组管理
- 为后续国际化做好准备
- 字符串命名应清晰表达其用途

示例:
```dart
class Strings {
  // App
  static const String appName = 'asmr.one';
  
  // Common
  static const String loading = '加载中...';
  
  // Feature specific
  static const String search = '搜索';
}
```

## 2. UI/UX 设计准则

### 2.1 界面设计
- 遵循 Material Design 3 设计规范
- 使用一致的颜色主题和字体
- 保持视觉层次感和空间布局的平衡
- 注重细节,保持像素级别的对齐

### 2.2 动画效果
- 使用 Flutter 内置动画系统
- 所有动画持续时间保持在 200-300ms 之间
- 使用曲线动画(推荐 Curves.easeInOut)
- 实现平滑的页面转场效果
- 添加有意义的微交互动画

### 2.3 性能优化
- 使用 const 构造器
- 合理使用 StatelessWidget
- 避免在 build 方法中进行复杂计算
- 使用 ListView.builder 处理长列表
- 图片资源进行适当压缩和缓存

## 3. 代码质量准则

### 3.1 代码风格
- 遵循 Dart 官方代码风格指南
- 使用 dartfmt 格式化代码
- 类型安全,避免使用 dynamic
- 添加必要的注释,特别是复杂业务逻辑

### 3.2 测试规范
- 单元测试覆盖率要求 > 80%
- 编写 Widget 测试验证UI行为
- 集成测试覆盖关键业务流程
- 使用 mock 进行依赖隔离

## 4. 版本控制准则

### 4.1 Git 规范
- 使用 Feature Branch 工作流
- commit 信息遵循 Angular 规范
- 定期进行代码审查
- 保持 main 分支稳定可用

### 4.2 发布规范
- 遵循语义化版本控制
- 每次发布都要有清晰的更新日志
- 重要版本发布前进行完整测试
- 保留���个版本的文档更新

## 5. 项目管理准则

### 5.1 文档管理
- 及时更新 API 文档
- 维护清晰的 README
- 记录重要的设计决策
- 编写用户指南和开发指南

### 5.2 问题追踪
- 使用 Issue 跟踪 bug 和新功能
- 为每个 Issue 添加适当的标签
- 保持任务的可追踪性
- 定期回顾和更新任务状态 

================================================
FILE: ios/.gitignore
================================================
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*

# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3


================================================
FILE: ios/Flutter/AppFrameworkInfo.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleDevelopmentRegion</key>
  <string>en</string>
  <key>CFBundleExecutable</key>
  <string>App</string>
  <key>CFBundleIdentifier</key>
  <string>io.flutter.flutter.app</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundleName</key>
  <string>App</string>
  <key>CFBundlePackageType</key>
  <string>FMWK</string>
  <key>CFBundleShortVersionString</key>
  <string>1.0</string>
  <key>CFBundleSignature</key>
  <string>????</string>
  <key>CFBundleVersion</key>
  <string>1.0</string>
  <key>MinimumOSVersion</key>
  <string>12.0</string>
</dict>
</plist>


================================================
FILE: ios/Flutter/Debug.xcconfig
================================================
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"


================================================
FILE: ios/Flutter/Release.xcconfig
================================================
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"


================================================
FILE: ios/Podfile
================================================
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
  'Debug' => :debug,
  'Profile' => :release,
  'Release' => :release,
}

def flutter_root
  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
  unless File.exist?(generated_xcode_build_settings_path)
    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
  end

  File.foreach(generated_xcode_build_settings_path) do |line|
    matches = line.match(/FLUTTER_ROOT\=(.*)/)
    return matches[1].strip if matches
  end
  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
  target 'RunnerTests' do
    inherit! :search_paths
  end
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
  end
end


================================================
FILE: ios/Runner/AppDelegate.swift
================================================
import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}


================================================
FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "size" : "20x20",
      "idiom" : "iphone",
      "filename" : "Icon-App-20x20@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "20x20",
      "idiom" : "iphone",
      "filename" : "Icon-App-20x20@3x.png",
      "scale" : "3x"
    },
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "Icon-App-29x29@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "Icon-App-29x29@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "Icon-App-29x29@3x.png",
      "scale" : "3x"
    },
    {
      "size" : "40x40",
      "idiom" : "iphone",
      "filename" : "Icon-App-40x40@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "40x40",
      "idiom" : "iphone",
      "filename" : "Icon-App-40x40@3x.png",
      "scale" : "3x"
    },
    {
      "size" : "60x60",
      "idiom" : "iphone",
      "filename" : "Icon-App-60x60@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "60x60",
      "idiom" : "iphone",
      "filename" : "Icon-App-60x60@3x.png",
      "scale" : "3x"
    },
    {
      "size" : "20x20",
      "idiom" : "ipad",
      "filename" : "Icon-App-20x20@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "20x20",
      "idiom" : "ipad",
      "filename" : "Icon-App-20x20@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "29x29",
      "idiom" : "ipad",
      "filename" : "Icon-App-29x29@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "29x29",
      "idiom" : "ipad",
      "filename" : "Icon-App-29x29@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "40x40",
      "idiom" : "ipad",
      "filename" : "Icon-App-40x40@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "40x40",
      "idiom" : "ipad",
      "filename" : "Icon-App-40x40@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "76x76",
      "idiom" : "ipad",
      "filename" : "Icon-App-76x76@1x.png",
      "scale" : "1x"
    },
    {
      "size" : "76x76",
      "idiom" : "ipad",
      "filename" : "Icon-App-76x76@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "83.5x83.5",
      "idiom" : "ipad",
      "filename" : "Icon-App-83.5x83.5@2x.png",
      "scale" : "2x"
    },
    {
      "size" : "1024x1024",
      "idiom" : "ios-marketing",
      "filename" : "Icon-App-1024x1024@1x.png",
      "scale" : "1x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}


================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "filename" : "LaunchImage.png",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "filename" : "LaunchImage@2x.png",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "filename" : "LaunchImage@3x.png",
      "scale" : "3x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}


================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
================================================
# Launch Screen Assets

You can customize the launch screen with your own desired assets by replacing the image files in this directory.

You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

================================================
FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="EHf-IW-A2E">
            <objects>
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
                        <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
                            </imageView>
                        </subviews>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
                        </constraints>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53" y="375"/>
        </scene>
    </scenes>
    <resources>
        <image name="LaunchImage" width="168" height="185"/>
    </resources>
</document>


================================================
FILE: ios/Runner/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
    </dependencies>
    <scenes>
        <!--Flutter View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
        </scene>
    </scenes>
</document>


================================================
FILE: ios/Runner/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleDisplayName</key>
	<string>Asmrapp</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>asmrapp</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>$(FLUTTER_BUILD_NAME)</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>$(FLUTTER_BUILD_NUMBER)</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIMainStoryboardFile</key>
	<string>Main</string>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>CADisableMinimumFrameDurationOnPhone</key>
	<true/>
	<key>UIApplicationSupportsIndirectInputEvents</key>
	<true/>
</dict>
</plist>


================================================
FILE: ios/Runner/Runner-Bridging-Header.h
================================================
#import "GeneratedPluginRegistrant.h"


================================================
FILE: ios/Runner.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 54;
	objects = {

/* Begin PBXBuildFile section */
		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
		331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
		9B54596A1AA247DB213D70D9 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DB3CD38BE4BEDD7984A4C53 /* Pods_RunnerTests.framework */; };
		FA9547A1CDFA8BA9285E4F0E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D991BFF7F7E1123F21EC9991 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 97C146E61CF9000F007C117D /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 97C146ED1CF9000F007C117D;
			remoteInfo = Runner;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = "";
			dstSubfolderSpec = 10;
			files = (
			);
			name = "Embed Frameworks";
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
		1864816EC7CE99BF3A03B425 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
		331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
		331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
		3BD433FFFD3DA36C18DC04D5 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
		7B17DE0D71E08E74104A6DA7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
		8DB3CD38BE4BEDD7984A4C53 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		9D845508FF1BDF0DBEC753A3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
		A315059E1F405C4EDE197F3C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
		A7561FF68870DE952DF640B5 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
		D991BFF7F7E1123F21EC9991 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		97C146EB1CF9000F007C117D /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				FA9547A1CDFA8BA9285E4F0E /* Pods_Runner.framework in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		C30863FD7117B3791F210A0B /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				9B54596A1AA247DB213D70D9 /* Pods_RunnerTests.framework in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		331C8082294A63A400263BE5 /* RunnerTests */ = {
			isa = PBXGroup;
			children = (
				331C807B294A618700263BE5 /* RunnerTests.swift */,
			);
			path = RunnerTests;
			sourceTree = "<group>";
		};
		575ABBC37CDFC79162ECE7F7 /* Pods */ = {
			isa = PBXGroup;
			children = (
				7B17DE0D71E08E74104A6DA7 /* Pods-Runner.debug.xcconfig */,
				9D845508FF1BDF0DBEC753A3 /* Pods-Runner.release.xcconfig */,
				3BD433FFFD3DA36C18DC04D5 /* Pods-Runner.profile.xcconfig */,
				A7561FF68870DE952DF640B5 /* Pods-RunnerTests.debug.xcconfig */,
				1864816EC7CE99BF3A03B425 /* Pods-RunnerTests.release.xcconfig */,
				A315059E1F405C4EDE197F3C /* Pods-RunnerTests.profile.xcconfig */,
			);
			name = Pods;
			path = Pods;
			sourceTree = "<group>";
		};
		78A3B507541DF7DDED4343BF /* Frameworks */ = {
			isa = PBXGroup;
			children = (
				D991BFF7F7E1123F21EC9991 /* Pods_Runner.framework */,
				8DB3CD38BE4BEDD7984A4C53 /* Pods_RunnerTests.framework */,
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
		9740EEB11CF90186004384FC /* Flutter */ = {
			isa = PBXGroup;
			children = (
				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
				9740EEB21CF90195004384FC /* Debug.xcconfig */,
				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
				9740EEB31CF90195004384FC /* Generated.xcconfig */,
			);
			name = Flutter;
			sourceTree = "<group>";
		};
		97C146E51CF9000F007C117D = {
			isa = PBXGroup;
			children = (
				9740EEB11CF90186004384FC /* Flutter */,
				97C146F01CF9000F007C117D /* Runner */,
				97C146EF1CF9000F007C117D /* Products */,
				331C8082294A63A400263BE5 /* RunnerTests */,
				575ABBC37CDFC79162ECE7F7 /* Pods */,
				78A3B507541DF7DDED4343BF /* Frameworks */,
			);
			sourceTree = "<group>";
		};
		97C146EF1CF9000F007C117D /* Products */ = {
			isa = PBXGroup;
			children = (
				97C146EE1CF9000F007C117D /* Runner.app */,
				331C8081294A63A400263BE5 /* RunnerTests.xctest */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		97C146F01CF9000F007C117D /* Runner */ = {
			isa = PBXGroup;
			children = (
				97C146FA1CF9000F007C117D /* Main.storyboard */,
				97C146FD1CF9000F007C117D /* Assets.xcassets */,
				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
				97C147021CF9000F007C117D /* Info.plist */,
				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
				74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
				74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
			);
			path = Runner;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		331C8080294A63A400263BE5 /* RunnerTests */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
			buildPhases = (
				60B65332F5596392DBC91AC5 /* [CP] Check Pods Manifest.lock */,
				331C807D294A63A400263BE5 /* Sources */,
				331C807F294A63A400263BE5 /* Resources */,
				C30863FD7117B3791F210A0B /* Frameworks */,
			);
			buildRules = (
			);
			dependencies = (
				331C8086294A63A400263BE5 /* PBXTargetDependency */,
			);
			name = RunnerTests;
			productName = RunnerTests;
			productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
			productType = "com.apple.product-type.bundle.unit-test";
		};
		97C146ED1CF9000F007C117D /* Runner */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
			buildPhases = (
				4C22EBC450685F3AE387A893 /* [CP] Check Pods Manifest.lock */,
				9740EEB61CF901F6004384FC /* Run Script */,
				97C146EA1CF9000F007C117D /* Sources */,
				97C146EB1CF9000F007C117D /* Frameworks */,
				97C146EC1CF9000F007C117D /* Resources */,
				9705A1C41CF9048500538489 /* Embed Frameworks */,
				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
				B62C690B77C8F1A1692748AD /* [CP] Embed Pods Frameworks */,
				AE562F71A72CF708023D2129 /* [CP] Copy Pods Resources */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = Runner;
			productName = Runner;
			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
			productType = "com.apple.product-type.application";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		97C146E61CF9000F007C117D /* Project object */ = {
			isa = PBXProject;
			attributes = {
				BuildIndependentTargetsInParallel = YES;
				LastUpgradeCheck = 1510;
				ORGANIZATIONNAME = "";
				TargetAttributes = {
					331C8080294A63A400263BE5 = {
						CreatedOnToolsVersion = 14.0;
						TestTargetID = 97C146ED1CF9000F007C117D;
					};
					97C146ED1CF9000F007C117D = {
						CreatedOnToolsVersion = 7.3.1;
						LastSwiftMigration = 1100;
					};
				};
			};
			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
			compatibilityVersion = "Xcode 9.3";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 97C146E51CF9000F007C117D;
			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				97C146ED1CF9000F007C117D /* Runner */,
				331C8080294A63A400263BE5 /* RunnerTests */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		331C807F294A63A400263BE5 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		97C146EC1CF9000F007C117D /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
			isa = PBXShellScriptBuildPhase;
			alwaysOutOfDate = 1;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
				"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
			);
			name = "Thin Binary";
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
		};
		4C22EBC450685F3AE387A893 /* [CP] Check Pods Manifest.lock */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
			);
			inputPaths = (
				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
				"${PODS_ROOT}/Manifest.lock",
			);
			name = "[CP] Check Pods Manifest.lock";
			outputFileListPaths = (
			);
			outputPaths = (
				"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
			showEnvVarsInLog = 0;
		};
		60B65332F5596392DBC91AC5 /* [CP] Check Pods Manifest.lock */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
			);
			inputPaths = (
				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
				"${PODS_ROOT}/Manifest.lock",
			);
			name = "[CP] Check Pods Manifest.lock";
			outputFileListPaths = (
			);
			outputPaths = (
				"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
			showEnvVarsInLog = 0;
		};
		9740EEB61CF901F6004384FC /* Run Script */ = {
			isa = PBXShellScriptBuildPhase;
			alwaysOutOfDate = 1;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
			);
			name = "Run Script";
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
		};
		AE562F71A72CF708023D2129 /* [CP] Copy Pods Resources */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
			);
			name = "[CP] Copy Pods Resources";
			outputFileListPaths = (
				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
			showEnvVarsInLog = 0;
		};
		B62C690B77C8F1A1692748AD /* [CP] Embed Pods Frameworks */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
			);
			name = "[CP] Embed Pods Frameworks";
			outputFileListPaths = (
				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
			showEnvVarsInLog = 0;
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		331C807D294A63A400263BE5 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		97C146EA1CF9000F007C117D /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 97C146ED1CF9000F007C117D /* Runner */;
			targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				97C146FB1CF9000F007C117D /* Base */,
			);
			name = Main.storyboard;
			sourceTree = "<group>";
		};
		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				97C147001CF9000F007C117D /* Base */,
			);
			name = LaunchScreen.storyboard;
			sourceTree = "<group>";
		};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
		249021D3217E4FDB00AE95B9 /* Profile */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_USER_SCRIPT_SANDBOXING = NO;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = iphoneos;
				SUPPORTED_PLATFORMS = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
			};
			name = Profile;
		};
		249021D4217E4FDB00AE95B9 /* Profile */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
				ENABLE_BITCODE = NO;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
				SWIFT_VERSION = 5.0;
				VERSIONING_SYSTEM = "apple-generic";
			};
			name = Profile;
		};
		331C8088294A63A400263BE5 /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = A7561FF68870DE952DF640B5 /* Pods-RunnerTests.debug.xcconfig */;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				GENERATE_INFOPLIST_FILE = YES;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp.RunnerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 5.0;
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
			};
			name = Debug;
		};
		331C8089294A63A400263BE5 /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 1864816EC7CE99BF3A03B425 /* Pods-RunnerTests.release.xcconfig */;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				GENERATE_INFOPLIST_FILE = YES;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp.RunnerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_VERSION = 5.0;
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
			};
			name = Release;
		};
		331C808A294A63A400263BE5 /* Profile */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = A315059E1F405C4EDE197F3C /* Pods-RunnerTests.profile.xcconfig */;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				GENERATE_INFOPLIST_FILE = YES;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp.RunnerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_VERSION = 5.0;
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
			};
			name = Profile;
		};
		97C147031CF9000F007C117D /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				ENABLE_USER_SCRIPT_SANDBOXING = NO;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
				MTL_ENABLE_DEBUG_INFO = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		97C147041CF9000F007C117D /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_USER_SCRIPT_SANDBOXING = NO;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = iphoneos;
				SUPPORTED_PLATFORMS = iphoneos;
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
			};
			name = Release;
		};
		97C147061CF9000F007C117D /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
				ENABLE_BITCODE = NO;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 5.0;
				VERSIONING_SYSTEM = "apple-generic";
			};
			name = Debug;
		};
		97C147071CF9000F007C117D /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
				ENABLE_BITCODE = NO;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
				SWIFT_VERSION = 5.0;
				VERSIONING_SYSTEM = "apple-generic";
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				331C8088294A63A400263BE5 /* Debug */,
				331C8089294A63A400263BE5 /* Release */,
				331C808A294A63A400263BE5 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				97C147031CF9000F007C117D /* Debug */,
				97C147041CF9000F007C117D /* Release */,
				249021D3217E4FDB00AE95B9 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				97C147061CF9000F007C117D /* Debug */,
				97C147071CF9000F007C117D /* Release */,
				249021D4217E4FDB00AE95B9 /* Profile */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = 97C146E61CF9000F007C117D /* Project object */;
}


================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>


================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>PreviewsEnabled</key>
	<false/>
</dict>
</plist>


================================================
FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1510"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "97C146ED1CF9000F007C117D"
               BuildableName = "Runner.app"
               BlueprintName = "Runner"
               ReferencedContainer = "container:Runner.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
            BuildableName = "Runner.app"
            BlueprintName = "Runner"
            ReferencedContainer = "container:Runner.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
      <Testables>
         <TestableReference
            skipped = "NO"
            parallelizable = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "331C8080294A63A400263BE5"
               BuildableName = "RunnerTests.xctest"
               BlueprintName = "RunnerTests"
               ReferencedContainer = "container:Runner.xcodeproj">
            </BuildableReference>
         </TestableReference>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
            BuildableName = "Runner.app"
            BlueprintName = "Runner"
            ReferencedContainer = "container:Runner.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Profile"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
            BuildableName = "Runner.app"
            BlueprintName = "Runner"
            ReferencedContainer = "container:Runner.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: ios/Runner.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:Runner.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:Pods/Pods.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


================================================
FILE: ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>PreviewsEnabled</key>
	<false/>
</dict>
</plist>


================================================
FILE: ios/RunnerTests/RunnerTests.swift
================================================
import Flutter
import UIKit
import XCTest

class RunnerTests: XCTestCase {

  func testExample() {
    // If you add code to the Runner application, consider adding tests here.
    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
  }

}


================================================
FILE: lib/common/constants/strings.dart
================================================
class Strings {
  // App
  static const String appName = 'asmr.one';

  // Common
  static const String loading = '加载中...';
  static const String error = '出错了';
  static const String retry = '重试';
  static const String cancel = '取消';
  static const String confirm = '确认';

  // Home
  static const String search = '搜索';
  static const String musicList = '音乐列表将在这里显示';

  // Player
  static const String nowPlaying = '正在播放';
  static const String playerPlaceholder = '播放器控件将在这里显示';

  // Detail
  static const String detail = '音乐详情';
  static const String detailPlaceholder = '音乐详细信息将在这里显示';

  // Drawer
  static const String home = '主页';
  static const String favorites = '我的收藏';
  static const String settings = '设置';
}


================================================
FILE: lib/core/audio/README.md
================================================
# 音频核心功能

## 当前架构

### 1. 事件驱动系统
- 基于 RxDart 的事件中心
- 统一的事件定义和处理
- 支持事件过滤和转换

### 2. 核心服务 (AudioPlayerService)
- 实现 IAudioPlayerService 接口
- 通过依赖注入管理依赖
- 负责协调各个组件

### 3. 状态管理
- PlaybackStateManager 负责状态维护
- 通过 EventHub 发送状态更新
- 支持状态持久化

### 4. 通知栏集成
- 基于 audio_service 包
- 响应系统媒体控制
- 支持后台播放

### 5. 依赖注入
通过 GetIt 管理所有依赖:
<pre>
void setupServiceLocator() {
  // 注册 EventHub
  getIt.registerLazySingleton(() => PlaybackEventHub());
  
  // 注册音频服务
  getIt.registerLazySingleton<IAudioPlayerService>(
    () => AudioPlayerService(
      eventHub: getIt(),
      stateRepository: getIt(),
    ),
  );
}
</pre>

## 注意事项

- 所有状态更新通过 EventHub 传递
- 避免组件间直接调用
- 优先使用依赖注入
- 保持组件职责单一
 

================================================
FILE: lib/core/audio/audio_player_handler.dart
================================================
import 'package:asmrapp/core/audio/events/playback_event_hub.dart';
import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';
import 'package:asmrapp/utils/logger.dart';

class AudioPlayerHandler extends BaseAudioHandler {
  final AudioPlayer _player;
  final PlaybackEventHub _eventHub;

  AudioPlayerHandler(this._player, this._eventHub) {
    AppLogger.debug('AudioPlayerHandler 初始化');
    
    // 改为监听 EventHub
    _eventHub.playbackState.listen((event) {
      final state = PlaybackState(
        controls: [
          MediaControl.skipToPrevious,
          event.state.playing ? MediaControl.pause : MediaControl.play,
          MediaControl.skipToNext,
        ],
        systemActions: const {
          MediaAction.seek,
          MediaAction.seekForward,
          MediaAction.seekBackward,
        },
        androidCompactActionIndices: const [0, 1, 2],
        processingState: const {
          ProcessingState.idle: AudioProcessingState.idle,
          ProcessingState.loading: AudioProcessingState.loading,
          ProcessingState.buffering: AudioProcessingState.buffering,
          ProcessingState.ready: AudioProcessingState.ready,
          ProcessingState.completed: AudioProcessingState.completed,
        }[event.state.processingState]!,
        playing: event.state.playing,
        updatePosition: event.position,
        bufferedPosition: _player.bufferedPosition,
        speed: _player.speed,
        queueIndex: 0,
      );
      playbackState.add(state);
    });
  }

  @override
  Future<void> play() async {
    AppLogger.debug('AudioHandler: 播放命令');
    _player.play();
  }

  @override
  Future<void> pause() async {
    AppLogger.debug('AudioHandler: 暂停命令');
    _player.pause();
  }

  @override
  Future<void> seek(Duration position) async {
    AppLogger.debug('AudioHandler: 跳转命令 position=$position');
    await _player.seek(position);
  }

  @override
  Future<void> stop() async {
    AppLogger.debug('AudioHandler: 停止命令');
    await _player.stop();
  }
}


================================================
FILE: lib/core/audio/audio_player_service.dart
================================================
import 'dart:async';
import 'package:asmrapp/utils/logger.dart';
import 'package:just_audio/just_audio.dart';
import 'package:audio_session/audio_session.dart';
import './i_audio_player_service.dart';
import './models/audio_track_info.dart';
import './models/playback_context.dart';
import './notification/audio_notification_service.dart';
import './storage/i_playback_state_repository.dart';
import './utils/audio_error_handler.dart';
import './state/playback_state_manager.dart';
import './controllers/playback_controller.dart';
import './events/playback_event_hub.dart';

class AudioPlayerService implements IAudioPlayerService {
  late final AudioPlayer _player;
  late final AudioNotificationService _notificationService;
  late final ConcatenatingAudioSource _playlist;
  late final PlaybackStateManager _stateManager;
  late final PlaybackController _playbackController;
  final PlaybackEventHub _eventHub;
  final IPlaybackStateRepository _stateRepository;

  AudioPlayerService._internal({
    required PlaybackEventHub eventHub,
    required IPlaybackStateRepository stateRepository,
  }) : _eventHub = eventHub,
       _stateRepository = stateRepository {
    _init();
  }

  static AudioPlayerService? _instance;
  
  factory AudioPlayerService({
    required PlaybackEventHub eventHub,
    required IPlaybackStateRepository stateRepository,
  }) {
    _instance ??= AudioPlayerService._internal(
      eventHub: eventHub,
      stateRepository: stateRepository,
    );
    return _instance!;
  }

  Future<void> _init() async {
    try {
      _player = AudioPlayer();
      _notificationService = AudioNotificationService(
        _player,
        _eventHub,
      );
      _playlist = ConcatenatingAudioSource(children: []);

      _stateManager = PlaybackStateManager(
        player: _player,
        stateRepository: _stateRepository,
        eventHub: _eventHub,
      );

      _playbackController = PlaybackController(
        player: _player,
        stateManager: _stateManager,
        playlist: _playlist,
      );

      final session = await AudioSession.instance;
      await session.configure(const AudioSessionConfiguration.music());
      await _notificationService.init();

      _stateManager.initStateListeners();
      await restorePlaybackState();
    } catch (e, stack) {
      AudioErrorHandler.handleError(
        AudioErrorType.init,
        '音频播放器初始化',
        e,
        stack,
      );
      AudioErrorHandler.throwError(
        AudioErrorType.init,
        '音频播放器初始化',
        e,
      );
    }
  }

  // 基础播放控制
  @override
  Future<void> pause() => _playbackController.pause();

  @override
  Future<void> resume() => _playbackController.play();

  @override
  Future<void> stop() async {
    await _playbackController.stop();
    _stateManager.clearState();
  }

  @override
  Future<void> seek(Duration position) => _playbackController.seek(position);

  @override
  Future<void> previous() => _playbackController.previous();

  @override
  Future<void> next() => _playbackController.next();

  // 上下文管理
  @override
  Future<void> playWithContext(PlaybackContext context) async {
    await _playbackController.setPlaybackContext(context);
    // 添加自动播放
    await resume();
  }

  // 状态访问
  @override
  AudioTrackInfo? get currentTrack => _stateManager.currentTrack;

  @override
  PlaybackContext? get currentContext => _stateManager.currentContext;

  // 状态持久化
  @override
  Future<void> savePlaybackState() => _stateManager.saveState();

  @override
  Future<void> restorePlaybackState() async {
    try {
      AppLogger.debug('开始恢复播放状态');
      final state = await _stateManager.loadState();
      
      if (state == null) {
        AppLogger.debug('没有可恢复的播放状态');
        return;
      }

      AppLogger.debug('已加载保存的状态: workId=${state.work.id}');
      AppLogger.debug('播放列表信息: 长度=${state.playlist.length}, 索引=${state.currentIndex}');

      if (state.playlist.isEmpty) {
        AppLogger.debug('保存的播放列表为空,跳过恢复');
        return;
      }

      final context = PlaybackContext(
        work: state.work,
        files: state.files,
        currentFile: state.currentFile,
        playMode: state.playMode,
      );

      try {
        await _playbackController.setPlaybackContext(
          context,
          initialPosition: Duration(milliseconds: state.position),
        );
        AppLogger.debug('播放状态恢复成功');
      } catch (e) {
        AppLogger.error('设置播放上下文失败,跳过状态恢复', e);
      }
    } catch (e, stack) {
      AudioErrorHandler.handleError(
        AudioErrorType.init,
        '恢复播放状态',
        e,
        stack,
      );
      rethrow;
    }
  }

  @override
  Future<void> dispose() async {
    _player.dispose();
    _notificationService.dispose();
  }
}


================================================
FILE: lib/core/audio/audio_service.dart
================================================
import 'package:just_audio/just_audio.dart';

abstract class AudioService {
  Future<void> play(String url);
  Future<void> pause();
  Future<void> resume();
  Future<void> stop();
  Future<void> dispose();

  Stream<PlayerState> get playerState;
}


================================================
FILE: lib/core/audio/cache/audio_cache_manager.dart
================================================
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:crypto/crypto.dart';
import 'dart:convert';
import 'package:just_audio/just_audio.dart';
import 'package:asmrapp/utils/logger.dart';

/// 音频缓存管理器
/// 负责管理音频文件的缓存,对外隐藏具体的缓存实现
class AudioCacheManager {
  static const int _maxCacheSize = 1024 * 1024 * 1024; // 总缓存限制 1024MB
  static const Duration _cacheExpiration = Duration(days: 30);

  /// 创建音频源
  /// 内部处理缓存逻辑,对外只返回 AudioSource
  static Future<AudioSource> createAudioSource(String url) async {
    try {
      final cacheFile = await _getCacheFile(url);
      final fileName = _generateFileName(url);
      AppLogger.debug('准备创建音频源 - URL: $url, 缓存文件名: $fileName');
      
      // 检查缓存文件是否存在且有效
      final isValid = await _isCacheValid(cacheFile, fileName);
      
      if (isValid) {
        AppLogger.debug('[$fileName] 使用已有缓存文件');
        return _createCachingSource(url, cacheFile);
      }

      AppLogger.debug('[$fileName] 创建新的缓存源');
      return _createCachingSource(url, cacheFile);
      
    } catch (e) {
      AppLogger.error('创建缓存音频源失败,使用非缓存源', e);
      return ProgressiveAudioSource(Uri.parse(url));
    }
  }

  /// 清理过期和超量的缓存
  static Future<void> cleanCache() async {
    try {
      final cacheDir = await _getCacheDir();
      final files = await cacheDir.list().toList();
      
      // 按修改时间排序
      files.sort((a, b) {
        return a.statSync().modified.compareTo(b.statSync().modified);
      });

      var totalSize = 0;
      for (var file in files) {
        if (file is File) {
          final stat = await file.stat();
          
          // 检查是否过期
          if (DateTime.now().difference(stat.modified) > _cacheExpiration) {
            await file.delete();
            continue;
          }

          totalSize += stat.size;
          
          // 如果总大小超过限制,删除最旧的文件
          if (totalSize > _maxCacheSize) {
            await file.delete();
          }
        }
      }
    } catch (e) {
      AppLogger.error('清理缓存失败', e);
    }
  }

  /// 获取缓存大小
  static Future<int> getCacheSize() async {
    try {
      final cacheDir = await _getCacheDir();
      final files = await cacheDir.list().toList();
      
      var totalSize = 0;
      for (var file in files) {
        if (file is File) {
          totalSize += (await file.stat()).size;
        }
      }
      return totalSize;
    } catch (e) {
      AppLogger.error('获取缓存大小失败', e);
      return 0;
    }
  }

  // 私有方法

  /// 创建缓存音频源
  static AudioSource _createCachingSource(String url, File cacheFile) {
    return LockCachingAudioSource(
      Uri.parse(url),
      cacheFile: cacheFile
    );
  }

  /// 检查缓存是否有效
  static Future<bool> _isCacheValid(File cacheFile, String fileName) async {
    final exists = await cacheFile.exists();
    if (!exists) {
      AppLogger.debug('[$fileName] 缓存验证: 文件不存在');
      return false;
    }

    try {
      final stat = await cacheFile.stat();
      final size = stat.size;
      final age = DateTime.now().difference(stat.modified);
      
      AppLogger.debug('[$fileName] 缓存验证: 大小=${size}bytes, 年龄=$age');
      
      // 移除单个文件大小检查,只保留过期检查
      if (age > _cacheExpiration) {
        AppLogger.debug('[$fileName] 缓存无效: 文件过期 ($age > $_cacheExpiration)');
        await cacheFile.delete();
        return false;
      }

      AppLogger.debug('[$fileName] 缓存验证: 有效');
      return true;
    } catch (e) {
      AppLogger.error('[$fileName] 检查缓存有效性失败', e);
      return false;
    }
  }

  /// 获取缓存文件
  static Future<File> _getCacheFile(String url) async {
    final cacheDir = await _getCacheDir();
    final fileName = _generateFileName(url);
    return File('${cacheDir.path}/$fileName');
  }

  /// 生成缓存文件名
  static String _generateFileName(String url) {
    final bytes = utf8.encode(url);
    final digest = md5.convert(bytes);
    return digest.toString();
  }

  /// 获取缓存目录
  static Future<Directory> _getCacheDir() async {
    final cacheDir = await getTemporaryDirectory();
    final audioCacheDir = Directory('${cacheDir.path}/audio_cache');
    if (!await audioCacheDir.exists()) {
      await audioCacheDir.create(recursive: true);
    }
    return audioCacheDir;
  }
}

================================================
FILE: lib/core/audio/controllers/playback_controller.dart
================================================
import 'package:asmrapp/utils/logger.dart';
import 'package:just_audio/just_audio.dart';
import '../models/playback_context.dart';
import '../state/playback_state_manager.dart';
import '../utils/playlist_builder.dart';
import '../utils/audio_error_handler.dart';
import 'package:asmrapp/data/models/files/child.dart';
import 'package:asmrapp/data/models/works/work.dart';


class PlaybackController {
  final AudioPlayer _player;
  final PlaybackStateManager _stateManager;
  final ConcatenatingAudioSource _playlist;

  PlaybackController({
    required AudioPlayer player,
    required PlaybackStateManager stateManager,
    required ConcatenatingAudioSource playlist,
  }) : _player = player,
       _stateManager = stateManager,
       _playlist = playlist;

  // 基础播放控制
  Future<void> play() => _player.play();
  Future<void> pause() => _player.pause();
  Future<void> stop() => _player.stop();
  Future<void> seek(Duration position, {int? index}) => _player.seek(position, index: index);
  
  // 播放列表控制
  Future<void> next() async {
    try {
      AppLogger.debug('尝试切换下一曲');
      if (_stateManager.currentContext == null) {
        AppLogger.debug('当前上下文为空,无法切换下一曲');
        return;
      }

      if (_player.hasNext) {
        AppLogger.debug('执行切换到下一曲');
        await _player.seekToNext();
      } else {
        AppLogger.debug('没有下一曲可切换');
      }
    } catch (e, stack) {
      AppLogger.error('切换下一曲失败', e, stack);
      AudioErrorHandler.handleError(
        AudioErrorType.playback,
        '切换下一曲',
        e,
        stack,
      );
    }
  }

  Future<void> previous() async {
    try {
      AppLogger.debug('尝试切换上一曲');
      if (_stateManager.currentContext == null) {
        AppLogger.debug('当前上下文为空,无法切换上一曲');
        return;
      }

      if (_player.hasPrevious) {
        final previousFile = _stateManager.currentContext!.getPreviousFile();
        AppLogger.debug('获取到上一个文件: ${previousFile?.title}');
        if (previousFile != null) {
          _updateTrackAndContext(
            previousFile,
            _stateManager.currentContext!.work
          );
          AppLogger.debug('执行切换到上一曲');
          await _player.seekToPrevious();
        }
      } else {
        AppLogger.debug('没有上一曲可切换');
      }
    } catch (e, stack) {
      AppLogger.error('切换上一曲失败', e, stack);
      AudioErrorHandler.handleError(
        AudioErrorType.playback,
        '切换上一曲',
        e,
        stack,
      );
    }
  }

  // 播放上下文设置
  Future<void> setPlaybackContext(PlaybackContext context, {Duration? initialPosition}) async {
    try {
      AppLogger.debug('准备设置播放上下文: workId=${context.work.id}, file=${context.currentFile.title}');
      AppLogger.debug('播放列表状态: 长度=${context.playlist.length}, 当前索引=${context.currentIndex}');
      
      // 验证上下文
      try {
        context.validate();
      } catch (e) {
        AppLogger.error('播放上下文验证失败', e);
        rethrow;
      }
      
      // 1. 先停止当前播放
      AppLogger.debug('停止当前播放');
      await _player.stop();
      
      // 2. 等待播放器就绪
      AppLogger.debug('暂停播放器');
      await _player.pause();
      
      // 3. 更新上下文
      AppLogger.debug('更新播放上下文');
      _stateManager.updateContext(context);
      
      // 4. 设置新的播放源
      AppLogger.debug('设置播放源: 初始位置=${initialPosition?.inMilliseconds}ms');
      try {
        await PlaylistBuilder.setPlaylistSource(
          player: _player,
          playlist: _playlist,
          files: context.playlist,
          initialIndex: context.currentIndex,
          initialPosition: initialPosition ?? Duration.zero,
        );
      } catch (e, stack) {
        AppLogger.error('设置播放源失败', e, stack);
        rethrow;
      }

      // 5. 等待播放器准备完成
      // 删掉,会导致播放器索引回到0
      // AppLogger.debug('等待播放器加载');
      // await _player.load();
      
      // 6. 更新轨道信息
      AppLogger.debug('更新轨道信息');
      _updateTrackAndContext(context.currentFile, context.work);
      
      AppLogger.debug('播放上下文设置完成');
    } catch (e, stack) {
      AppLogger.error('设置播放上下文失败', e, stack);
      AudioErrorHandler.handleError(
        AudioErrorType.context,
        '设置播放上下文',
        e,
        stack,
      );
      rethrow;
    }
  }

  // 私有辅助方法
  void _updateTrackAndContext(Child file, Work work) {
    AppLogger.debug('更新轨道和上下文: file=${file.title}');
    _stateManager.updateTrackAndContext(file, work);
  }
} 

================================================
FILE: lib/core/audio/events/playback_event.dart
================================================
import 'package:just_audio/just_audio.dart';
import '../models/audio_track_info.dart';
import '../models/playback_context.dart';
import 'package:asmrapp/data/models/files/child.dart';
import 'package:asmrapp/data/models/works/work.dart';

/// 播放事件基类
abstract class PlaybackEvent {}

/// 播放状态事件
class PlaybackStateEvent extends PlaybackEvent {
  final PlayerState state;
  final Duration position;
  final Duration? duration;
  PlaybackStateEvent(this.state, this.position, this.duration);
}

/// 播放上下文事件
class PlaybackContextEvent extends PlaybackEvent {
  final PlaybackContext context;
  PlaybackContextEvent(this.context);
}

/// 音轨变更事件
class TrackChangeEvent extends PlaybackEvent {
  final AudioTrackInfo track;
  final Child file;
  final Work work;
  TrackChangeEvent(this.track, this.file, this.work);
}

/// 播放错误事件
class PlaybackErrorEvent extends PlaybackEvent {
  final String operation;
  final dynamic error;
  final StackTrace? stackTrace;
  PlaybackErrorEvent(this.operation, this.error, [this.stackTrace]);
}

/// 播放完成事件
class PlaybackCompletedEvent extends PlaybackEvent {
  final PlaybackContext context;
  PlaybackCompletedEvent(this.context);
}

/// 播放进度事件
class PlaybackProgressEvent extends PlaybackEvent {
  final Duration position;
  final Duration? bufferedPosition;
  PlaybackProgressEvent(this.position, this.bufferedPosition);
}

/// 添加初始状态相关事件
class RequestInitialStateEvent extends PlaybackEvent {}

class InitialStateEvent extends PlaybackEvent {
  final AudioTrackInfo? track;
  final PlaybackContext? context;
  InitialStateEvent(this.track, this.context);
}

================================================
FILE: lib/core/audio/events/playback_event_hub.dart
================================================
import 'package:rxdart/rxdart.dart';
import './playback_event.dart';

class PlaybackEventHub {
  // 统一的事件流,处理所有类型的事件
  final _eventSubject = PublishSubject<PlaybackEvent>();

  // 分类后的特定事件流
  late final Stream<PlaybackStateEvent> playbackState = _eventSubject
      .whereType<PlaybackStateEvent>()
      .distinct();
      
  late final Stream<TrackChangeEvent> trackChange = _eventSubject
      .whereType<TrackChangeEvent>();
      
  late final Stream<PlaybackContextEvent> contextChange = _eventSubject
      .whereType<PlaybackContextEvent>();
      
  late final Stream<PlaybackProgressEvent> playbackProgress = _eventSubject
      .whereType<PlaybackProgressEvent>()
      .distinct((prev, next) => prev.position == next.position);
      
  late final Stream<PlaybackErrorEvent> errors = _eventSubject
      .whereType<PlaybackErrorEvent>();

  // 添加新的事件流
  late final Stream<InitialStateEvent> initialState = _eventSubject
      .whereType<InitialStateEvent>();
      
  late final Stream<RequestInitialStateEvent> requestInitialState = _eventSubject
      .whereType<RequestInitialStateEvent>();

  // 发送事件
  void emit(PlaybackEvent event) => _eventSubject.add(event);

  // 资源释放
  void dispose() => _eventSubject.close();
} 

================================================
FILE: lib/core/audio/i_audio_player_service.dart
================================================
import './models/audio_track_info.dart';
import './models/playback_context.dart';

abstract class IAudioPlayerService {
  // 基础播放控制
  Future<void> pause();
  Future<void> resume();
  Future<void> stop();
  Future<void> seek(Duration position);
  Future<void> previous();
  Future<void> next();
  Future<void> dispose();

  // 上下文管理
  Future<void> playWithContext(PlaybackContext context);
  
  // 状态访问
  AudioTrackInfo? get currentTrack;
  PlaybackContext? get currentContext;

  // 状态持久化
  Future<void> savePlaybackState();
  Future<void> restorePlaybackState();
}


================================================
FILE: lib/core/audio/models/audio_track_info.dart
================================================
class AudioTrackInfo {
  final String title;
  final String artist;
  final String coverUrl;
  final String url;
  final Duration? duration;

  AudioTrackInfo({
    required this.title,
    required this.artist,
    required this.coverUrl,
    required this.url,
    this.duration,
  });
}


================================================
FILE: lib/core/audio/models/file_path.dart
================================================
import 'package:asmrapp/data/models/files/files.dart';
import 'package:asmrapp/data/models/files/child.dart';
import 'package:asmrapp/utils/logger.dart';

/// 文件路径工具类
/// 用于在文件树中定位文件和获取同级文件
class FilePath {
  static const separator = '/';

  /// 获取文件的完整路径
  /// 返回类似 /folder1/folder2/file.mp3 的路径
  static String? getPath(Child targetFile, Files root) {
    AppLogger.debug('开始查找文件路径: ${targetFile.title}');
    final segments = _findPathSegments(root.children, targetFile);
    
    if (segments == null) {
      AppLogger.debug('未找到文件路径');
      return null;
    }

    final path = separator + segments.join(separator);
    AppLogger.debug('找到文件路径: $path');
    return path;
  }

  /// 递归查找文件路径段
  static List<String>? _findPathSegments(List<Child>? children, Child targetFile, [List<String> currentPath = const []]) {
    if (children == null) return null;

    for (final child in children) {
      if (child.title == targetFile.title && 
          child.mediaDownloadUrl == targetFile.mediaDownloadUrl && 
          child.type == targetFile.type &&
          child.size == targetFile.size) {  // size 作为额外验证
        return [...currentPath, child.title!];
      }

      if (child.type == 'folder' && child.children != null) {
        final result = _findPathSegments(
          child.children, 
          targetFile, 
          [...currentPath, child.title!]
        );
        if (result != null) return result;
      }
    }

    return null;
  }

  /// 获取同级文件列表
  /// 返回与目标文件在同一目录下的所有文件
  static List<Child> getSiblings(Child targetFile, Files root) {
    AppLogger.debug('开始获取同级文件: ${targetFile.title}');
    
    // 获取目标文件的路径
    final path = getPath(targetFile, root);
    if (path == null) {
      AppLogger.debug('无法获取文件路径,返回空列表');
      return [];
    }

    // 获取父目录路径
    final lastSeparator = path.lastIndexOf(separator);
    final parentPath = lastSeparator > 0 ? path.substring(0, lastSeparator) : separator;
    AppLogger.debug('父目录路径: $parentPath');

    // 查找父目录内容
    List<Child>? siblings;
    if (parentPath == separator) {
      // 如果是根目录,直接使用 root.children
      AppLogger.debug('文件位于根目录,使用根目录文件列表');
      siblings = root.children;
    } else {
      // 否则查找父目录
      siblings = _findDirectoryByPath(root.children, parentPath);
    }

    if (siblings == null) {
      AppLogger.debug('未找到父目录内容,返回空列表');
      return [];
    }

    AppLogger.debug('找到同级文件数量: ${siblings.length}');
    return siblings;
  }

  /// 根据路径查找目录内容
  static List<Child>? _findDirectoryByPath(List<Child>? children, String path) {
    if (children == null || path.isEmpty) return null;

    // 如果是根路径,直接返回
    if (path == separator) return children;

    // 分割路径
    final segments = path.split(separator)
      ..removeWhere((s) => s.isEmpty);
    
    List<Child>? current = children;
    
    // 逐级查找目录
    for (final segment in segments) {
      final nextDir = current?.firstWhere(
        (child) => child.title == segment && child.type == 'folder',
        orElse: () => Child(),
      );
      
      if (nextDir?.title == null) return null;
      current = nextDir?.children;
    }

    return current;
  }

  /// 查找第一个包含音频文件的目录路径
  /// 返回从根目录到目标目录的完整路径数组
  static List<String>? findFirstAudioFolderPath(
    List<Child>? children, {
    List<String> formats = const ['.mp3', '.wav'],
  }) {
    if (children == null) return null;

    List<String>? audioFolderPath;
    
    void findPath(Child folder, List<String> currentPath) {
      if (audioFolderPath != null) return;

      if (folder.children != null) {
        // 首先检查当前��录是否直接包含音频文件
        bool hasDirectAudio = folder.children!.any((child) {
          if (child.type != 'folder') {
            final fileName = child.title?.toLowerCase() ?? '';
            return formats.any((format) => fileName.endsWith(format));
          }
          return false;
        });

        // 如果当前目录包含音频文件,记录完整路径
        if (hasDirectAudio) {
          audioFolderPath = currentPath;
          return;
        }

        // 如果当前目录没有音频文件,递归检查子目录
        for (final child in folder.children!) {
          if (child.type == 'folder') {
            List<String> newPath = List.from(currentPath)..add(child.title ?? '');
            findPath(child, newPath);
          }
        }
      }
    }

    // 遍历根目录下的所有文件夹
    for (final child in children) {
      if (child.type == 'folder') {
        findPath(child, [child.title ?? '']);
        if (audioFolderPath != null) break;
      }
    }

    return audioFolderPath;
  }

  /// 检查路径是否包含指定的目录名
  /// 用于判断某个目录是否在音频文件夹的路径上
  static bool isInPath(List<String>? path, String? folderName) {
    if (path == null || folderName == null) return false;
    return path.contains(folderName);
  }
} 

================================================
FILE: lib/core/audio/models/play_mode.dart
================================================
enum PlayMode {
  single,     // 单曲循环
  loop,       // 列表循环
  sequence,   // 顺序播放
} 

================================================
FILE: lib/core/audio/models/playback_context.dart
================================================
import 'package:asmrapp/core/audio/utils/audio_error_handler.dart';
import 'package:asmrapp/data/models/works/work.dart';
import 'package:asmrapp/data/models/files/files.dart';
import 'package:asmrapp/data/models/files/child.dart';
import 'package:asmrapp/utils/logger.dart';
import 'package:asmrapp/core/audio/models/play_mode.dart';
import 'package:asmrapp/core/audio/models/file_path.dart';

class PlaybackContext {
  final Work work;
  final Files files;
  final Child currentFile;
  final List<Child> playlist;
  final int currentIndex;
  final PlayMode playMode;

  void validate() {
    if (playlist.isEmpty) {
      throw AudioError(
        AudioErrorType.state,
        '无效的播放列表状态:播放列表为空',
      );
    }
    
    if (currentIndex < 0 || currentIndex >= playlist.length) {
      throw AudioError(
        AudioErrorType.state,
        '无效的播放列表索引:$currentIndex,列表长度:${playlist.length}',
      );
    }

    if (!playlist.contains(currentFile)) {
      throw AudioError(
        AudioErrorType.state,
        '当前文件不在播放列表中',
      );
    }
  }

  // 私有构造函数
  const PlaybackContext._({
    required this.work,
    required this.files,
    required this.currentFile,
    required this.playlist,
    required this.currentIndex,
    this.playMode = PlayMode.sequence,
  });

  // 公开的工厂构造函数,只需要基本参数
  factory PlaybackContext({
    required Work work,
    required Files files,
    required Child currentFile,
    PlayMode playMode = PlayMode.sequence,
  }) {
    final playlist = _getPlaylistFromSameDirectory(currentFile, files);
    final currentIndex = playlist.indexWhere((file) => file.title == currentFile.title);
    
    return PlaybackContext._(
      work: work,
      files: files,
      currentFile: currentFile,
      playlist: playlist,
      currentIndex: currentIndex,
      playMode: playMode,
    );
  }

  // 获取同级文件列表
  static List<Child> _getPlaylistFromSameDirectory(Child currentFile, Files files) {
    // AppLogger.debug('开始获取播放列表...');
    // AppLogger.debug('当前文件: ${currentFile.title}');
    // AppLogger.debug('当前文件类型: ${currentFile.type}');

    // 获取当前文件的扩展名
    final extension = currentFile.title?.split('.').last.toLowerCase();
    // AppLogger.debug('当前文件扩展名: $extension');
    
    if (extension != 'mp3' && extension != 'wav') {
      AppLogger.debug('不支持的文件类型: $extension');
      return [];
    }

    // 使用 FilePath 获取同级文件
    final siblings = FilePath.getSiblings(currentFile, files);
    
    // 过滤出相同扩展名的文件
    final playlist = siblings.where((file) => 
      file.title?.toLowerCase().endsWith('.$extension') ?? false
    ).toList();
    
    // AppLogger.debug('找到 ${playlist.length} 个可播放文件:');
    // for (var file in playlist) {
    //   AppLogger.debug('- [${file.type}] ${file.title} (URL: ${file.mediaDownloadUrl != null ? '有' : '无'})');
    // }
    
    return playlist;
  }

  // 便捷方法:检查是否有下一曲
  bool get hasNext => currentIndex < playlist.length - 1;

  // 便捷方法:检查是否有上一曲
  bool get hasPrevious => currentIndex > 0;

  // 获取下一曲(考虑播放模式)
  Child? getNextFile() {
    if (playlist.isEmpty) return null;
    
    switch (playMode) {
      case PlayMode.single:
        return currentFile;  // 单曲循环返回当前文件
      case PlayMode.loop:
        // 列表循环:最后一首返回第一首,否则返回下一首
        return hasNext ? playlist[currentIndex + 1] : playlist[0];
      case PlayMode.sequence:
        // 顺序播放:有下一首则返回,否则返回null
        return hasNext ? playlist[currentIndex + 1] : null;
    }
  }

  // 获取上一曲
  Child? getPreviousFile() {
    if (playlist.isEmpty) return null;
    
    switch (playMode) {
      case PlayMode.single:
        return currentFile;
      case PlayMode.loop:
        // 列表循环:第一首返回最后一首,否则返回上一首
        return hasPrevious ? playlist[currentIndex - 1] : playlist[playlist.length - 1];
      case PlayMode.sequence:
        // 顺序播放:有上一首则返回,否则返回null
        return hasPrevious ? playlist[currentIndex - 1] : null;
    }
  }

  // 这两个方法 copy 的设计思路是遵循了"不可变对象"模式,
  // 通过创建新的实例而不是修改现有实例来更新状态。这种模式有以下好处:
  // 状态可预测
  // 线程安全
  // 便于调试
  // 符合函数式编程思想

  // 创建新的上下文(用于切换文件)
  PlaybackContext copyWithFile(Child newFile) {
    return PlaybackContext(
      work: work,
      files: files,
      currentFile: newFile,
      playMode: playMode,
    );
  }

  // 创建新的上下文(用于切换播放模式)
  PlaybackContext copyWithMode(PlayMode newMode) {
    return PlaybackContext(
      work: work,
      files: files,
      currentFile: currentFile,
      playMode: newMode,
    );
  }

  // 便捷方法:获取可播放文件列表
  List<Child> getPlayableFiles() {
    if (files.children == null) return [];
    return files.children!.where((file) => 
      file.mediaDownloadUrl != null && 
      file.type?.toLowerCase() != 'vtt'
    ).toList();
  }

  // 工具方法:获取文件名(不含扩展名)
  String? _getBaseName(String? filename) {
    if (filename == null) return null;
    return filename.replaceAll(RegExp(r'\.[^.]+$'), '');
  }
} 

================================================
FILE: lib/core/audio/models/subtitle.dart
================================================
import 'dart:math' as math;

enum SubtitleState {
  current,  // 当前播放的字幕
  waiting,  // 即将播放的字幕
  passed    // 已经播放过的字幕
}

class Subtitle {
  final Duration start;
  final Duration end;
  final String text;
  final int index;

  const Subtitle({
    required this.start,
    required this.end,
    required this.text,
    required this.index,
  });

  Subtitle? getNext(SubtitleList list) {
    if (index < list.subtitles.length - 1) {
      return list.subtitles[index + 1];
    }
    return null;
  }

  Subtitle? getPrevious(SubtitleList list) {
    if (index > 0) {
      return list.subtitles[index - 1];
    }
    return null;
  }

  @override
  String toString() => '$start --> $end: $text';
}

class SubtitleList {
  final List<Subtitle> subtitles;
  int _currentIndex = -1;

  SubtitleList(List<Subtitle> subtitles) 
    : subtitles = subtitles.asMap().entries.map(
        (entry) => Subtitle(
          start: entry.value.start,
          end: entry.value.end,
          text: entry.value.text,
          index: entry.key,
        )
      ).toList();

  SubtitleWithState? getCurrentSubtitle(Duration position) {
    if (subtitles.isEmpty) return null;

    // 如果位置在第一个字幕之前,仍然返回第一个字幕作为当前字幕
    if (position < subtitles.first.start) {
      return SubtitleWithState(subtitles.first, SubtitleState.current);
    }

    // 如果位置在最后一个字幕之后
    if (position > subtitles.last.end) {
      return SubtitleWithState(subtitles.last, SubtitleState.passed);
    }

    // 查找当前时间点对应的字幕
    for (int i = 0; i < subtitles.length; i++) {
      final subtitle = subtitles[i];
      // 如果在当前字幕的时间范围内
      if (position >= subtitle.start && position <= subtitle.end) {
        _currentIndex = i;
        return SubtitleWithState(subtitle, SubtitleState.current);
      }
      // 如果已经超过了当前字幕,但还没到下一个字幕
      if (position > subtitle.end && 
          (i == subtitles.length - 1 || position < subtitles[i + 1].start)) {
        return SubtitleWithState(subtitle, SubtitleState.passed);
      }
    }

    // 正常情况下不会到达这里,因为上面的逻辑已经覆盖了所有情况
    // 但为了安全起见,返回第一个字幕
    return SubtitleWithState(subtitles.first, SubtitleState.waiting);
  }

  List<Subtitle> getSubtitlesInRange(int start, int count) {
    if (start < 0 || start >= subtitles.length) return [];
    final end = math.min(start + count, subtitles.length);
    return subtitles.sublist(start, end);
  }

  (Subtitle?, Subtitle?, Subtitle?) getCurrentContext() {
    if (_currentIndex == -1) return (null, null, null);
    
    final previous = _currentIndex > 0 ? subtitles[_currentIndex - 1] : null;
    final current = subtitles[_currentIndex];
    final next = _currentIndex < subtitles.length - 1 ? subtitles[_currentIndex + 1] : null;
    
    return (previous, current, next);
  }

  static SubtitleList parse(String vttContent) {
    final lines = vttContent.split('\n');
    final subtitles = <Subtitle>[];
    
    int i = 0;
    while (i < lines.length && !lines[i].contains('-->')) {
      i++;
    }

    while (i < lines.length) {
      final line = lines[i].trim();
      
      if (line.contains('-->')) {
        final times = line.split('-->');
        if (times.length == 2) {
          final start = _parseTimestamp(times[0].trim());
          final end = _parseTimestamp(times[1].trim());
          
          i++;
          String text = '';
          while (i < lines.length && lines[i].trim().isNotEmpty) {
            if (text.isNotEmpty) text += '\n';
            text += lines[i].trim();
            i++;
          }
          
          if (start != null && end != null && text.isNotEmpty) {
            subtitles.add(Subtitle(
              start: start,
              end: end,
              text: text,
              index: subtitles.length,
            ));
          }
        }
      }
      i++;
    }

    return SubtitleList(subtitles);
  }

  static Duration? _parseTimestamp(String timestamp) {
    try {
      final parts = timestamp.split(':');
      if (parts.length == 3) {
        final seconds = parts[2].split('.');
        return Duration(
          hours: int.parse(parts[0]),
          minutes: int.parse(parts[1]),
          seconds: int.parse(seconds[0]),
          milliseconds: seconds.length > 1 ? int.parse(seconds[1].padRight(3, '0')) : 0,
        );
      }
    } catch (e) {
      return null;
    }
    return null;
  }
}

class SubtitleWithState {
  final Subtitle subtitle;
  final SubtitleState state;

  SubtitleWithState(this.subtitle, this.state);
} 

================================================
FILE: lib/core/audio/notification/audio_notification_service.dart
================================================
import 'package:asmrapp/core/audio/events/playback_event_hub.dart';
import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';
import 'package:asmrapp/utils/logger.dart';
import 'package:rxdart/rxdart.dart';
import '../models/audio_track_info.dart';
import '../audio_player_handler.dart';

class AudioNotificationService {
  final AudioPlayer _player;
  final PlaybackEventHub _eventHub;
  AudioHandler? _audioHandler;
  final _mediaItem = BehaviorSubject<MediaItem?>();

  AudioNotificationService(
    this._player,
    this._eventHub,
  );

  Future<void> init() async {
    try {
      _audioHandler = await AudioService.init(
        builder: () => AudioPlayerHandler(_player, _eventHub),
        config: const AudioServiceConfig(
          androidNotificationChannelId: 'com.asmrapp.audio',
          androidNotificationChannelName: 'ASMR One 播放器',
          androidNotificationOngoing: true,
          androidStopForegroundOnPause: true,
        ),
      );

      _setupEventListeners();
      AppLogger.debug('通知栏服务初始化成功');
    } catch (e) {
      AppLogger.error('通知栏服务初始化失败', e);
      rethrow;
    }
  }

  void _setupEventListeners() {
    // 监听轨道变更事件来更新媒体信息
    _eventHub.trackChange.listen((event) {
      updateMetadata(event.track);
    });
  }

  void updateMetadata(AudioTrackInfo trackInfo) {
    final mediaItem = MediaItem(
      id: trackInfo.url,
      title: trackInfo.title,
      artist: trackInfo.artist,
      artUri: Uri.parse(trackInfo.coverUrl),
      duration: trackInfo.duration,
    );

    _mediaItem.add(mediaItem);
    if (_audioHandler != null) {
      (_audioHandler as BaseAudioHandler).mediaItem.add(mediaItem);
    }
  }

  Future<void> dispose() async {
    await _audioHandler?.stop();
    await _mediaItem.close();
  }
}


================================================
FILE: lib/core/audio/state/playback_state_manager.dart
================================================
import 'dart:async';
import 'package:just_audio/just_audio.dart';
import '../models/audio_track_info.dart';
import '../models/playback_context.dart';
import '../utils/audio_error_handler.dart';
import '../utils/track_info_creator.dart';
import 'package:asmrapp/data/models/playback/playback_state.dart';
import '../storage/i_playback_state_repository.dart';
import '../events/playback_event.dart';
import '../events/playback_event_hub.dart';
import 'package:asmrapp/data/models/files/child.dart';
import 'package:asmrapp/data/models/works/work.dart';


class PlaybackStateManager {
  final AudioPlayer _player;
  final PlaybackEventHub _eventHub;
  final IPlaybackStateRepository _stateRepository;
  
  AudioTrackInfo? _currentTrack;
  PlaybackContext? _currentContext;

  final List<StreamSubscription> _subscriptions = [];

  PlaybackStateManager({
    required AudioPlayer player,
    required PlaybackEventHub eventHub,
    required IPlaybackStateRepository stateRepository,
  }) : _player = player,
       _eventHub = eventHub,
       _stateRepository = stateRepository;

  // 初始化状态监听
  void initStateListeners() {
    // 监听播放器索引变化
    _player.currentIndexStream.listen((index) {
      if (index != null && _currentContext != null) {
        final newFile = _currentContext!.playlist[index];
        updateTrackAndContext(newFile, _currentContext!.work);
      }
    });

    // 直接监听 AudioPlayer 的原始流
    _player.playerStateStream.listen((state) async {
      final position = _player.position;
      final duration = _player.duration;
      
      // 转换并发送到 EventHub
      _eventHub.emit(PlaybackStateEvent(state, position, duration));

      if (state.processingState == ProcessingState.completed) {
        _onPlaybackCompleted();
      }
      saveState();
    });

    _player.positionStream.listen((position) {
      _eventHub.emit(PlaybackProgressEvent(
        position,
        _player.bufferedPosition
      ));
    });
  }

  // 状态更新方法
  void updateContext(PlaybackContext? context) {
    _currentContext = context;
    if (context != null) {
      _eventHub.emit(PlaybackContextEvent(context));
    }
  }

  void updateTrackInfo(AudioTrackInfo track) {
    _currentTrack = track;
    _eventHub.emit(TrackChangeEvent(track, _currentContext!.currentFile, _currentContext!.work));
  }

  void updateTrackAndContext(Child file, Work work) {
    if (_currentContext != null) {
      final newContext = _currentContext!.copyWithFile(file);
      updateContext(newContext);
    }
    
    final trackInfo = TrackInfoCreator.createFromFile(file, work);
    updateTrackInfo(trackInfo);
  }

  void _onPlaybackCompleted() {
    if (_currentContext == null) return;
    _eventHub.emit(PlaybackCompletedEvent(_currentContext!));
  }

  // 状态访问
  AudioTrackInfo? get currentTrack => _currentTrack;
  PlaybackContext? get currentContext => _currentContext;

  void clearState() {
    _currentTrack = null;
    _currentContext = null;
    updateContext(null);
  }

  // 状态持久化
  Future<void> saveState() async {
    if (_currentContext == null) return;

    try {
      final state = PlaybackState(
        work: _currentContext!.work,
        files: _currentContext!.files,
        currentFile: _currentContext!.currentFile,
        playlist: _currentContext!.playlist,
        currentIndex: _currentContext!.currentIndex,
        playMode: _currentContext!.playMode,
        position: (_player.position).inMilliseconds,
        timestamp: DateTime.now().toIso8601String(),
      );
      
      await _stateRepository.saveState(state);
    } catch (e, stack) {
      AudioErrorHandler.handleError(
        AudioErrorType.state,
        '保存播放状态',
        e,
        stack,
      );
    }
  }

  Future<PlaybackState?> loadState() async {
    try {
      return await _stateRepository.loadState();
    } catch (e, stack) {
      AudioErrorHandler.handleError(
        AudioErrorType.state,
        '加载播放状态',
        e,
        stack,
      );
      return null;
    }
  }

  void _setupEventListeners() {
    // 处理初始状态请求
    _subscriptions.add(
      _eventHub.requestInitialState.listen((_) {
        _eventHub.emit(InitialStateEvent(
          _currentTrack,
          _currentContext
        ));
      }),
    );
  }

  void dispose() {
    for (var subscription in _subscriptions) {
      subscription.cancel();
    }
    _subscriptions.clear();
  }
} 

================================================
FILE: lib/core/audio/storage/i_playback_state_repository.dart
================================================
import 'package:asmrapp/data/models/playback/playback_state.dart';

abstract class IPlaybackStateRepository {
  Future<void> saveState(PlaybackState state);
  Future<PlaybackState?> loadState();
} 

================================================
FILE: lib/core/audio/storage/playback_state_repository.dart
================================================
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:asmrapp/utils/logger.dart';
import 'package:asmrapp/data/models/playback/playback_state.dart';
import 'i_playback_state_repository.dart';

class PlaybackStateRepository implements IPlaybackStateRepository {
  static const _key = 'last_playback_state';
  final SharedPreferences _prefs;

  PlaybackStateRepository(this._prefs);

  @override
  Future<void> saveState(PlaybackState state) async {
    try {
      final json = state.toJson();
      final data = jsonEncode(json);
      await _prefs.setString(_key, data);
      AppLogger.debug('播放状态已保存');
    } catch (e) {
      AppLogger.error('保存播放状态失败', e);
      rethrow;
    }
  }

  @override
  Future<PlaybackState?> loadState() async {
    try {
      final data = _prefs.getString(_key);
      if (data == null) {
        AppLogger.debug('没有找到保存的播放状态');
        return null;
      }

      final json = jsonDecode(data) as Map<String, dynamic>;
      final state = PlaybackState.fromJson(json);
      AppLogger.debug('播放状态已加载');
      return state;
    } catch (e) {
      AppLogger.error('加载播放状态失败', e);
      return null;
    }
  }
} 

================================================
FILE: lib/core/audio/utils/audio_error_handler.dart
================================================
import 'package:asmrapp/utils/logger.dart';

enum AudioErrorType {
  playback,    // 播放错误
  playlist,    // 播放列表错误
  state,       // 状态错误
  context,     // 上下文错误
  init,        // 初始化错误
}

class AudioError implements Exception {
  final AudioErrorType type;
  final String message;
  final dynamic originalError;

  AudioError(this.type, this.message, [this.originalError]);

  @override
  String toString() => '$message${originalError != null ? ': $originalError' : ''}';
}

class AudioErrorHandler {
  static void handleError(
    AudioErrorType type,
    String operation,
    dynamic error, [
    StackTrace? stack,
  ]) {
    final message = _getErrorMessage(type, operation);
    AppLogger.error(message, error, stack);
  }
  
  static Never throwError(
    AudioErrorType type,
    String operation,
    dynamic error,
  ) {
    final message = _getErrorMessage(type, operation);
    throw AudioError(type, message, error);
  }

  static String _getErrorMessage(AudioErrorType type, String operation) {
    switch (type) {
      case AudioErrorType.playback:
        return '播放操作失败: $operation';
      case AudioErrorType.playlist:
        return '播放列表操作失败: $operation';
      case AudioErrorType.state:
        return '状态操作失败: $operation';
      case AudioErrorType.context:
        return '上下文操作失败: $operation';
      case AudioErrorType.init:
        return '初始化失败: $operation';
    }
  }
} 

================================================
FILE: lib/core/audio/utils/playlist_builder.dart
================================================
import 'package:just_audio/just_audio.dart';
import 'package:asmrapp/data/models/files/child.dart';
import 'package:asmrapp/core/audio/cache/audio_cache_manager.dart';

class PlaylistBuilder {
  static Future<List<AudioSource>> buildAudioSources(List<Child> files) async {
    return await Future.wait(
      files.map((file) async {
        return AudioCacheManager.createAudioSource(file.mediaDownloadUrl!);
      })
    );
  }

  static Future<void> updatePlaylist(
    ConcatenatingAudioSource playlist,
    List<AudioSource> sources,
  ) async {
    await playlist.clear();
    await playlist.addAll(sources);
  }

  static Future<void> setPlaylistSource({
    required AudioPlayer player,
    required ConcatenatingAudioSource playlist,
    required List<Child> files,
    required int initialIndex,
    required Duration initialPosition,
  }) async {
    final sources = await buildAudioSources(files);
    await updatePlaylist(playlist, sources);
    
    await player.setAudioSource(
      playlist,
      initialIndex: initialIndex,
      initialPosition: initialPosition,
    );
  }
} 

================================================
FILE: lib/core/audio/utils/track_info_creator.dart
================================================
import 'package:asmrapp/core/audio/models/audio_track_info.dart';
import 'package:asmrapp/data/models/files/child.dart';
import 'package:asmrapp/data/models/works/work.dart';

class TrackInfoCreator {
  static AudioTrackInfo createTrackInfo({
    required String title,
    required String? artistName,
    required String? coverUrl,
    required String url,
  }) {
    return AudioTrackInfo(
      title: title,
      artist: artistName ?? '',
      coverUrl: coverUrl ?? '',
      url: url,
    );
  }
  
  static AudioTrackInfo createFromFile(Child file, Work work) {
    return createTrackInfo(
      title: file.title ?? '',
      artistName: work.circle?.name,
      coverUrl: work.mainCoverUrl,
      url: file.mediaDownloadUrl!,
    );
  }
} 

================================================
FILE: lib/core/cache/recommendation_cache_manager.dart
================================================
import 'dart:collection';
import 'package:asmrapp/data/services/api_service.dart';
import 'package:asmrapp/utils/logger.dart';

class RecommendationCacheManager {
  // 单例模式
  static final RecommendationCacheManager _instance = RecommendationCacheManager._internal();
  factory RecommendationCacheManager() => _instance;
  RecommendationCacheManager._internal();

  // 使用 LinkedHashMap 便于按访问顺序管理缓存
  final _cache = LinkedHashMap<String, _CacheItem>();
  
  // 缓存配置
  static const int _maxCacheSize = 1000; // 最大缓存条目数
  static const Duration _cacheDuration = Duration(hours: 24); // 缓存有效期

  /// 生成缓存键
  String _generateKey(String itemId, int page, int subtitle) {
    return '$itemId-$page-$subtitle';
  }

  /// 获取缓存数据
  WorksResponse? get(String itemId, int page, int subtitle) {
    final key = _generateKey(itemId, page, subtitle);
    final item = _cache[key];

    if (item == null) {
      return null;
    }

    // 检查是否过期
    if (item.isExpired) {
      _cache.remove(key);
      AppLogger.debug('缓存已过期: $key');
      return null;
    }

    AppLogger.debug('命中缓存: $key');
    return item.data;
  }

  /// 存储缓存数据
  void set(String itemId, int page, int subtitle, WorksResponse data) {
    final key = _generateKey(itemId, page, subtitle);
    
    // 检查缓存大小,如果达到上限则移除最早的条目
    if (_cache.length >= _maxCacheSize) {
      _cache.remove(_cache.keys.first);
    }

    _cache[key] = _CacheItem(data);
    AppLogger.debug('添加缓存: $key');
  }

  /// 清除所有缓存
  void clear() {
    _cache.clear();
    AppLogger.debug('清除所有推荐缓存');
  }

  /// 移除指定作品的缓存
  void remove(String itemId) {
    _cache.removeWhere((key, _) => key.startsWith('$itemId-'));
    AppLogger.debug('移除作品缓存: $itemId');
  }
}

/// 缓存条目包装类
class _CacheItem {
  final WorksResponse data;
  final DateTime timestamp;

  _CacheItem(this.data) : timestamp = DateTime.now();

  bool get isExpired => 
    DateTime.now().difference(timestamp) > RecommendationCacheManager._cacheDuration;
} 

================================================
FILE: lib/core/di/service_locator.dart
================================================
import 'dart:io';
import 'package:asmrapp/core/platform/dummy_lyric_overlay_controller.dart';
import 'package:get_it/get_it.dart';
import '../audio/i_audio_player_service.dart';
import '../audio/audio_player_service.dart';
import '../../data/services/api_service.dart';
import '../../presentation/viewmodels/player_viewmodel.dart';
import '../../data/services/auth_service.dart';
import '../../presentation/viewmodels/auth_viewmodel.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../data/repositories/auth_repository.dart';
import '../subtitle/i_subtitle_service.dart';
import '../subtitle/subtitle_service.dart';
import '../subtitle/subtitle_loader.dart';
import '../../core/audio/storage/i_playback_state_repository.dart';
import '../../core/audio/storage/playback_state_repository.dart';
import '../audio/events/playback_event_hub.dart';
import '../../core/theme/theme_controller.dart';
import '../../core/platform/i_lyric_overlay_controller.dart';
import '../../core/platform/lyric_overlay_controller.dart';
import '../../core/platform/lyric_overlay_manager.dart';
import '../../core/platform/wakelock_controller.dart';

final getIt = GetIt.instance;

Future<void> setupServiceLocator() async {
  final prefs = await SharedPreferences.getInstance();

  // 注册 EventHub
  getIt.registerLazySingleton(() => PlaybackEventHub());

  // 注册 SharedPreferences 实例
  getIt.registerSingleton<SharedPreferences>(prefs);

  // 注册 PlaybackStateRepository
  getIt.registerLazySingleton<IPlaybackStateRepository>(
    () => PlaybackStateRepository(getIt()),
  );

  // 核心服务
  getIt.registerLazySingleton<IAudioPlayerService>(
    () => AudioPlayerService(
      eventHub: getIt(),
      stateRepository: getIt(),
    ),
  );

  // 注册 PlayerViewModel
  getIt.registerLazySingleton<PlayerViewModel>(
    () => PlayerViewModel(
      audioService: getIt(),
      eventHub: getIt(),
      subtitleService: getIt(),
    ),
  );

  // API 服务
  getIt.registerLazySingleton<ApiService>(
    () => ApiService(),
  );

  // 添加 AuthService 注册
  getIt.registerLazySingleton<AuthService>(
    () => AuthService(),
  );

  // 添加 AuthRepository 注册
  getIt.registerLazySingleton<AuthRepository>(
    () => AuthRepository(prefs),
  );

  // 修改 AuthViewModel 注册
  getIt.registerSingleton<AuthViewModel>(
    AuthViewModel(
      authService: getIt<AuthService>(),
      authRepository: getIt<AuthRepository>(),
    ),
  );

  // 等待 AuthViewModel 完成初始化
  await getIt<AuthViewModel>().loadSavedAuth();

  // 添加字幕服务注册
  getIt.registerLazySingleton<ISubtitleService>(
    () => SubtitleService(),
  );

  await setupSubtitleServices();

  // 注册主题控制器
  getIt.registerLazySingleton<ThemeController>(
    () => ThemeController(prefs),
  );

  // 注册 WakeLockController
  getIt.registerLazySingleton(() => WakeLockController(prefs));
}

Future<void> setupSubtitleServices() async {
  getIt.registerLazySingleton<SubtitleLoader>(() => SubtitleLoader());
  if (Platform.isAndroid) {
    getIt.registerLazySingleton<ILyricOverlayController>(() => LyricOverlayController());
  } else {
    getIt.registerLazySingleton<ILyricOverlayController>(() => DummyLyricOverlayController());
  }
  getIt.registerLazySingleton(() => LyricOverlayManager(
    controller: getIt(),
    subtitleService: getIt(),
  ));

  // 初始化悬浮窗管理器
  await getIt<LyricOverlayManager>().initialize();
}


================================================
FILE: lib/core/platform/dummy_lyric_overlay_controller.dart
================================================
import 'package:asmrapp/utils/logger.dart';
import 'i_lyric_overlay_controller.dart';

class DummyLyricOverlayController implements ILyricOverlayController {
  static const _tag = 'LyricOverlay';

  @override
  Future<void> initialize() async {
  }

  @override
  Future<void> show() async {

  }

  @override
  Future<void> hide() async {

  }

  @override
  Future<void> updateLyric(String? text) async {

  }

  @override
  Future<bool> checkPermission() async {
    return true;
  }

  @override
  Future<bool> requestPermission() async {
    AppLogger.debug('[$_tag] 请求权限');
    return true;
  }

  @override
  Future<void> dispose() async {

  }

  @override
  Future<bool> isShowing() async {
    return false;
  }
}

================================================
FILE: lib/core/platform/i_lyric_overlay_controller.dart
================================================
abstract class ILyricOverlayController {
  /// 初始化悬浮窗
  Future<void> initialize();
  
  /// 显示悬浮窗
  Future<void> show();
  
  /// 隐藏悬浮窗
  Future<void> hide();
  
  /// 更新歌词内容
  Future<void> updateLyric(String? text);
  
  /// 检查悬浮窗权限
  Future<bool> checkPermission();
  
  /// 请求悬浮窗权限
  Future<bool> requestPermission();
  
  /// 释放资源
  Future<void> dispose();
  
  /// 获取悬浮窗当前显示状态
  Future<bool> isShowing();
} 

================================================
FILE: lib/core/platform/lyric_overlay_controller.dart
================================================
import 'package:flutter/services.dart';
import 'package:asmrapp/utils/logger.dart';
import 'package:permission_handler/permission_handler.dart';
import 'i_lyric_overlay_controller.dart';

class LyricOverlayController implements ILyricOverlayController {
  static const _tag = 'LyricOverlay';
  static const _channel = MethodChannel('one.asmr.yuro/lyric_overlay');
  
  @override
  Future<void> initialize() async {
    try {
      AppLogger.debug('[$_tag] 初始化');
      await _channel.invokeMethod('initialize');
    } catch (e) {
      AppLogger.error('[$_tag] 初始化失败', e);
      // 这里我们不抛出异常,而是静默失败
      // 因为这个错误不应该影响应用的主要功能
    }
  }
  
  @override
  Future<void> show() async {
    AppLogger.debug('[$_tag] 显示悬浮窗');
    await _channel.invokeMethod('show');
  }
  
  @override
  Future<void> hide() async {
    AppLogger.debug('[$_tag] 隐藏悬浮窗');
    await _channel.invokeMethod('hide');
  }
  
  @override
  Future<void> updateLyric(String? text) async {
    AppLogger.debug('[$_tag] 更新歌词: ${text ?? '<空>'}');
    await _channel.invokeMethod('updateLyric', {'text': text});
  }
  
  @override
  Future<bool> checkPermission() async {
    AppLogger.debug('[$_tag] 检查权限');
    return await Permission.systemAlertWindow.isGranted;
  }
  
  @override
  Future<bool> requestPermission() async {
    AppLogger.debug('[$_tag] 请求权限');
    final status = await Permission.systemAlertWindow.request();
    return status.isGranted;
  }
  
  @override
  Future<void> dispose() async {
    AppLogger.debug('[$_tag] 释放资源');
    await _channel.invokeMethod('dispose');
  }
  
  @override
  Future<bool> isShowing() async {
    final result = await _channel.invokeMethod<bool>('isShowing') ?? false;
    return result;
  }
} 

================================================
FILE: lib/core/platform/lyric_overlay_manager.dart
================================================
import 'dart:async';

import 'package:asmrapp/core/platform/i_lyric_overlay_controller.dart';
import 'package:asmrapp/core/subtitle/i_subtitle_service.dart';
import 'package:flutter/material.dart';

class LyricOverlayManager {
  final ILyricOverlayController _controller;
  final ISubtitleService _subtitleService;
  StreamSubscription? _subscription;
  bool _isShowing = false;
  
  LyricOverlayManager({
    required ILyricOverlayController controller,
    required ISubtitleService subtitleService,
  }) : _controller = controller,
       _subtitleService = subtitleService;

  Future<void> initialize() async {
    await _controller.initialize();
    _subscription = _subtitleService.currentSubtitleStream.listen((subtitle) {
      if (_isShowing) {
        _controller.updateLyric(subtitle?.text);
      }
    });
    
    _isShowing = await _controller.isShowing();
    
    if (_isShowing) {
      await show();
    }
  }
  
  Future<void> dispose() async {
    await _subscription?.cancel();
    await _controller.dispose();
  }
  
  Future<bool> checkPermission() async {
    return await _controller.checkPermission();
  }

  Future<bool> requestPermission() async {
    return await _controller.requestPermission();
  }

  Future<void> show() async {
    await _controller.show();
    _isShowing = true;
    final currentSubtitle = _subtitleService.currentSubtitleWithState;
    if (currentSubtitle != null) {
      await _controller.updateLyric(currentSubtitle.subtitle.text);
    }
  }

  Future<void> hide() async {
    await _controller.hide();
    _isShowing = false;
  }

  bool get isShowing => _isShowing;

  /// 处理显示悬浮歌词的完整流程
  Future<void> showWithPermissionCheck(BuildContext context) async {
    final hasPermission = await checkPermission();
    if (hasPermission) {
      await show();
      return;
    }

    if (!context.mounted) return;

    final shouldRequest = await _showPermissionDialog(context);
    if (shouldRequest && context.mounted) {
      final granted = await requestPermission();
      if (granted && context.mounted) {
        await show();
      }
    }
  }

  Future<bool> _showPermissionDialog(BuildContext context) async {
    return await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('开启悬浮歌词'),
        content: const Text('需要悬浮窗权限来显示歌词,是否授予权限?'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () => Navigator.pop(context, true),
            child: const Text('确定'),
          ),
        ],
      ),
    ) ?? false;
  }

  /// 切换显示/隐藏状态
  Future<void> toggle(BuildContext context) async {
    if (_isShowing) {
      await hide();
    } else {
      await showWithPermissionCheck(context);
    }
  }
  
  // 其他控制方法...

  Future<void> syncState() async {
    _isShowing = await _controller.isShowing();
  }
} 

================================================
FILE: lib/core/platform/wakelock_controller.dart
================================================
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:asmrapp/utils/logger.dart';

class WakeLockController extends ChangeNotifier {
  static const _tag = 'WakeLock';
  static const _wakeLockKey = 'wakelock_enabled';
  final SharedPreferences _prefs;
  bool _enabled = false;

  WakeLockController(this._prefs) {
    _loadState();
  }

  bool get enabled => _enabled;

  Future<void> _loadState() async {
    try {
      _enabled = _prefs.getBool(_wakeLockKey) ?? false;
      if (_enabled) {
        await WakelockPlus.enable();
      }
      notifyListeners();
    } catch (e) {
      AppLogger.error('[$_tag] 加载状态失败', e);
    }
  }

  Future<void> toggle() async {
    try {
      _enabled = !_enabled;
      if (_enabled) {
        await WakelockPlus.enable();
      } else {
        await WakelockPlus.disable();
      }
      await _prefs.setBool(_wakeLockKey, _enabled);
      notifyListeners();
    } catch (e) {
      AppLogger.error('[$_tag] 切换状态失败', e);
      // 恢复状态
      _enabled = !_enabled;
      notifyListeners();
    }
  }

  Future<void> dispose() async {
    try {
      await WakelockPlus.disable();
    } catch (e) {
      AppLogger.error('[$_tag] 释放失败', e);
    }
    super.dispose();
  }
} 

================================================
FILE: lib/core/subtitle/cache/subtitle_cache_manager.dart
================================================
import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:asmrapp/utils/logger.dart';

class SubtitleCacheManager {
  static const String key = 'subtitleCache';
  
  static final CacheManager instance = CacheManager(
    Config(
      key,
      stalePeriod: const Duration(days: 365), // 字幕文件不会变更,设置较长的有效期
      maxNrOfCacheObjects: 1000, // 最大缓存文件数
      repo: JsonCacheInfoRepository(databaseName: key),
      fileService: HttpFileService(),
    ),
  );

  /// 获取缓存的字幕内容
  static Future<String?> getCachedContent(String url) async {
    try {
      final file = await instance.getSingleFile(url);
      AppLogger.debug('使用字幕缓存: $url');
      return await file.readAsString();
    } catch (e) {
      AppLogger.error('读取字幕缓存失败', e);
      return null;
    }
  }

  /// 保存字幕内容到缓存
  static Future<void> cacheContent(String url, String content) async {
    try {
      await instance.putFile(
        url,
        Uint8List.fromList(utf8.encode(content)),
        fileExtension: 'txt',
      );
      AppLogger.debug('字幕已缓存: $url');
    } catch (e) {
      AppLogger.error('保存字幕缓存失败', e);
    }
  }

  /// 清理缓存
  static Future<void> clearCache() async {
    try {
      await instance.emptyCache();
      AppLogger.debug('字幕缓存已清空');
    } catch (e) {
      AppLogger.error('清理字幕缓存失败', e);
    }
  }

  /// 获取缓存大小
  static Future<int> getSize() async {
    try {
      return instance.store.getCacheSize();
    } catch (e) {
      AppLogger.error('获取字幕缓存大小失败', e);
      return 0;
    }
  }
} 

================================================
FILE: lib/core/subtitle/i_subtitle_service.dart
================================================
import 'package:asmrapp/core/audio/models/subtitle.dart';

abstract class ISubtitleService {
  // 字幕加载
  Future<void> loadSubtitle(String url);
  
  // 字幕状态流
  Stream<SubtitleList?> get subtitleStream;
  
  // 当前字幕流
  Stream<Subtitle?> get currentSubtitleStream;
  
  // 当前字幕
  Subtitle? get currentSubtitle;
  
  // 更新播放位置
  void updatePosition(Duration position);
  
  // 资源释放
  void dispose();
  
  // 添加这一行
  SubtitleList? get subtitleList;  // 获取当前字幕列表
  
  // 添加清除字幕的方法
  void clearSubtitle();
  
  Stream<SubtitleWithState?> get currentSubtitleWithStateStream;
  SubtitleWithState? get currentSubtitleWithState;
} 

================================================
FILE: lib/core/subtitle/managers/subtitle_state_manager.dart
================================================
import 'dart:async';
import 'package:asmrapp/core/audio/models/subtitle.dart';
import 'package:asmrapp/utils/logger.dart';

class SubtitleStateManager {
  SubtitleList? _subtitleList;
  Subtitle? _currentSubtitle;
  SubtitleWithState? _currentSubtitleWithState;

  final _subtitleController = StreamController<SubtitleList?>.broadcast();
  final _currentSubtitleController = StreamController<Subtitle?>.broadcast();
  final _currentSubtitleWithStateController = StreamController<SubtitleWithState?>.broadcast();

  Stream<SubtitleList?> get subtitleStream => _subtitleController.stream;
  Stream<Subtitle?> get currentSubtitleStream => _currentSubtitleController.stream;
  Stream<SubtitleWithState?> get currentSubtitleWithStateStream => 
      _currentSubtitleWithStateController.stream;

  Subtitle? get currentSubtitle => _currentSubtitle;
  SubtitleList? get subtitleList => _subtitleList;
  SubtitleWithState? get currentSubtitleWithState => _currentSubtitleWithState;

  void setSubtitleList(SubtitleList? subtitleList) {
    _subtitleList = subtitleList;
    _subtitleController.add(_subtitleList);
  }

  void updatePosition(Duration position) {
    if (_subtitleList != null) {
      final newSubtitleWithState = _subtitleList!.getCurrentSubtitle(position);
      if (newSubtitleWithState?.subtitle != _currentSubtitleWithState?.subtitle) {
        _currentSubtitleWithState = newSubtitleWithState;
        _currentSubtitle = newSubtitleWithState?.subtitle;
        AppLogger.debug('字幕更新: ${_currentSubtitle?.text ?? '无字幕'} (${newSubtitleWithState?.state})');
        _currentSubtitleWithStateController.add(newSubtitleWithState);
        _currentSubtitleController.add(_currentSubtitle);
      }
    }
  }

  void clear() {
    _subtitleList = null;
    _currentSubtitle = null;
    _currentSubtitleWithState = null;
    _subtitleController.add(null);
    _currentSubtitleController.add(null);
    _currentSubtitleWithStateController.add(null);
    AppLogger.debug('字幕状态已清除');
  }

  void dispose() {
    _subtitleController.close();
    _currentSubtitleController.close();
    _currentSubtitleWithStateController.close();
  }
} 

================================================
FILE: lib/core/subtitle/parsers/lrc_parser.dart
================================================
import 'package:asmrapp/core/audio/models/subtitle.dart';
import 'package:asmrapp/core/subtitle/parsers/subtitle_parser.dart';
import 'package:asmrapp/utils/logger.dart';

class LrcParser extends BaseSubtitleParser {
  static final _timeTagRegex = RegExp(r'\[(\d{2}):(\d{2})\.(\d{2})\]');
  static final _idTagRegex = RegExp(r'^\[(ar|ti|al|by|offset):(.+)\]$');
  
  @override
  bool canParse(String content) {
    final lines = content.trim().split('\n');
    return lines.any((line) => _timeTagRegex.hasMatch(line));
  }
  
  @override
  SubtitleList doParse(String content) {
    final lines = content.split('\n');
    final subtitles = <Subtitle>[];
    final metadata = <String, String>{};
    
    for (final line in lines) {
      final trimmedLine = line.trim();
      if (trimmedLine.isEmpty) continue;
      
      // 检查是否是ID标签
      final idMatch = _idTagRegex.firstMatch(trimmedLine);
      if (idMatch != null) {
        metadata[idMatch.group(1)!] = idMatch.group(2)!;
        continue;
      }
      
      // 解析时间标签和歌词
      final timeMatches = _timeTagRegex.allMatches(trimmedLine);
      if (timeMatches.isEmpty) continue;
      
      // 获取歌词内容 (移除所有时间标签)
      final text = trimmedLine.replaceAll(_timeTagRegex, '').trim();
      if (text.isEmpty) continue;
      
      // 一行可能有多个时间标签
      for (final match in timeMatches) {
        try {
          final timestamp = _parseTimestamp(
            minutes: match.group(1)!,
            seconds: match.group(2)!,
            milliseconds: match.group(3)!,
          );
          
          subtitles.add(Subtitle(
            start: timestamp,
            end: timestamp + const Duration(seconds: 5), // 默认持续5秒
            text: text,
            index: subtitles.length,
          ));
        } catch (e) {
          AppLogger.debug('解析LRC时间标签失败: $e');
          continue;
        }
      }
    }
    
    // 按时间排序
    subtitles.sort((a, b) => a.start.compareTo(b.start));
    
    // 设置正确的结束时间
    for (int i = 0; i < subtitles.length - 1; i++) {
      subtitles[i] = Subtitle(
        start: subtitles[i].start,
        end: subtitles[i + 1].start,
        text: subtitles[i].text,
        index: i,
      );
    }
    
    AppLogger.debug('LRC解析完成: ${subtitles.length}条字幕, ${metadata.length}个元数据');
    return SubtitleList(subtitles);
  }
  
  Duration _parseTimestamp({
    required String minutes,
    required String seconds,
    required String milliseconds,
  }) {
    return Duration(
      minutes: int.parse(minutes),
      seconds: int.parse(seconds),
      milliseconds: int.parse(milliseconds) * 10,
    );
  }
} 

================================================
FILE: lib/core/subtitle/parsers/subtitle_parser.dart
================================================
import 'package:asmrapp/core/audio/models/subtitle.dart';

/// 字幕解析器接口
abstract class SubtitleParser {
  /// 解析字幕内容
  SubtitleList parse(String content);
  
  /// 检查内容格式是否匹配
  bool canParse(String content);
}

/// 字幕解析器基类
abstract class BaseSubtitleParser implements SubtitleParser {
  @override
  SubtitleList parse(String content) {
    if (!canParse(content)) {
      throw FormatException('不支持的字幕格式');
    }
    return doParse(content);
  }
  
  /// 具体的解析实现
  SubtitleList doParse(String content);
} 

================================================
FILE: lib/core/subtitle/parsers/subtitle_parser_factory.dart
================================================
import 'package:asmrapp/core/subtitle/parsers/subtitle_parser.dart';
import 'package:asmrapp/core/subtitle/parsers/vtt_parser.dart';
import 'package:asmrapp/core/subtitle/parsers/lrc_parser.dart';
import 'package:asmrapp/utils/logger.dart';

class SubtitleParserFactory {
  static final List<SubtitleParser> _parsers = [
    VttParser(),
    LrcParser(),
  ];
  
  static SubtitleParser? getParser(String content) {
    try {
      return _parsers.firstWhere((parser) => parser.canParse(content));
    } catch (e) {
      AppLogger.debug('没有找到匹配的字幕解析器');
      return null;
    }
  }
} 

================================================
FILE: lib/core/subtitle/parsers/vtt_parser.dart
================================================
import 'package:asmrapp/core/audio/models/subtitle.dart';
import 'package:asmrapp/core/subtitle/parsers/subtitle_parser.dart';

class VttParser extends BaseSubtitleParser {
  static final _vttHeaderRegex = RegExp(r'^WEBVTT');
  
  @override
  bool canParse(String content) {
    return content.trim().startsWith(_vttHeaderRegex);
  }
  
  @override
  SubtitleList doParse(String content) {
    final lines = content.split('\n');
    final subtitles = <Subtitle>[];
    int index = 0;
    
    // 跳过WEBVTT头部
    while (index < lines.length && !lines[index].contains('-->')) {
      index++;
    }
    
    while (index < lines.length) {
      final timeLine = lines[index];
      if (timeLine.contains('-->')) {
        final times = timeLine.split('-->');
        if (times.length == 2) {
          final start = _parseTimeString(times[0].trim());
          final end = _parseTimeString(times[1].trim());
          
          // 收集字幕文本
          index++;
          String text = '';
          while (index < lines.length && lines[index].trim().isNotEmpty) {
            text += lines[index].trim() + '\n';
            index++;
          }
          
          if (text.isNotEmpty) {
            subtitles.add(Subtitle(
              start: start,
              end: end,
              text: text.trim(),
              index: subtitles.length,
            ));
          }
        }
      }
      index++;
    }
    
    return SubtitleList(subtitles);
  }
  
  Duration _parseTimeString(String timeString) {
    final parts = timeString.split(':');
    if (parts.length != 3) throw FormatException('Invalid time format');
    
    final seconds = parts[2].split('.');
    return Duration(
      hours: int.parse(parts[0]),
      minutes: int.parse(parts[1]),
      seconds: int.parse(seconds[0]),
      milliseconds: seconds.length > 1 ? int.parse(seconds[1].padRight(3, '0')) : 0,
    );
  }
} 

================================================
FILE: lib/core/subtitle/subtitle_loader.dart
================================================
import 'package:asmrapp/data/models/files/child.dart';
import 'package:asmrapp/data/models/files/files.dart';
import 'package:asmrapp/core/audio/models/file_path.dart';
import 'package:asmrapp/core/audio/models/subtitle.dart';
import 'package:dio/dio.dart';
import 'package:asmrapp/utils/logger.dart';
import 'package:asmrapp/core/subtitle/utils/subtitle_matcher.dart';
import 'package:asmrapp/core/subtitle/parsers/subtitle_parser_factory.dart';
import 'package:asmrapp/core/subtitle/cache/subtitle_cache_manager.dart';

class SubtitleLoader {
  final _dio = Dio();

  // 查找字幕文件
  Child? findSubtitleFile(Child audioFile, Files files) {
    if (files.children == null || audioFile.title == null) {
      AppLogger.debug('无法查找字幕文件: ${files.children == null ? '文件列表为空' : '当前文件名为空'}');
      return null;
    }

    AppLogger.debug('开始查找字幕文件...');
    
    // 使用 FilePath 获取同级文件
    final siblings = FilePath.getSiblings(audioFile, files);
    
    // 使用 SubtitleMatcher 查找匹配的字幕文件
    final subtitleFile = SubtitleMatcher.findMatchingSubtitle(
      audioFile.title!,
      siblings
    );
    
    if (subtitleFile != null) {
      AppLogger.debug('找到字幕文件: ${subtitleFile.title}, URL: ${subtitleFile.mediaDownloadUrl}');
    } else {
      AppLogger.debug('在当前目录中未找到字幕文件');
    }
    
    return subtitleFile;
  }

  // 修改: 加载字幕内容
  Future<SubtitleList?> loadSubtitleContent(String url) async {
    try {
      // 首先尝试从缓存加载
      final cachedContent = await SubtitleCacheManager.getCachedContent(url);
      if (cachedContent != null) {
        AppLogger.debug('从缓存加载字幕: $url');
        return _parseSubtitleContent(cachedContent);
      }

      // 缓存未命中,从网络加载
      AppLogger.debug('从网络加载字幕: $url');
      final response = await _dio.get(url);
      AppLogger.debug('字幕文件下载状态: ${response.statusCode}');
      
      if (response.statusCode == 200) {
        final content = response.data as String;
        
        // 保存到缓存
        await SubtitleCacheManager.cacheContent(url, content);
        
        return _parseSubtitleContent(content);
      } else {
        throw Exception('字幕下载失败: ${response.statusCode}');
      }
    } catch (e) {
      AppLogger.debug('字幕加载失败: $e');
      rethrow;
    }
  }

  // 新增: 解析字幕内容的私有方法
  SubtitleList? _parseSubtitleContent(String content) {
    AppLogger.debug('字幕文件内容预览: ${content.substring(0, content.length > 100 ? 100 : content.length)}...');
    
    final parser = SubtitleParserFactory.getParser(content);
    if (parser == null) {
      throw Exception('不支持的字幕格式');
    }
    
    final subtitleList = parser.parse(content);
    AppLogger.debug('字幕解析完成,字幕数量: ${subtitleList.subtitles.length}');
    
    return subtitleList;
  }
} 

================================================
FILE: lib/core/subtitle/subtitle_service.dart
================================================
import 'dart:async';
import 'package:asmrapp/utils/logger.dart';
import 'package:asmrapp/core/audio/models/subtitle.dart';
import 'package:asmrapp/core/subtitle/i_subtitle_service.dart';
import 'package:get_it/get_it.dart';
import 'package:asmrapp/core/subtitle/subtitle_loader.dart';
import 'package:asmrapp/core/subtitle/managers/subtitle_state_manager.dart';


class SubtitleService implements ISubtitleService {
  final _subtitleLoader = GetIt.I<SubtitleLoader>();
  final _stateManager = SubtitleStateManager();
  
  @override
  Stream<SubtitleList?> get subtitleStream => _stateManager.subtitleStream;
  
  @override
  Stream<Subtitle?> get currentSubtitleStream => _stateManager.currentSubtitleStream;
  
  @override
  Subtitle? get currentSubtitle => _stateManager.currentSubtitle;
  
  @override
  Future<void> loadSubtitle(String url) async {
    try {
      clearSubtitle();
      final subtitleList = await _subtitleLoader.loadSubtitleContent(url);
      _stateManager.setSubtitleList(subtitleList);
    } catch (e) {
      AppLogger.debug('字幕加载失败: $e');
      clearSubtitle();
      rethrow;
    }
  }
  
  @override
  void updatePosition(Duration position) {
    _stateManager.updatePosition(position);
  }

  @override
  void dispose() {
    _stateManager.dispose();
  }

  @override
  SubtitleList? get subtitleList => _stateManager.subtitleList;

  @override
  void clearSubtitle() {
    _stateManager.clear();
  }

  @override
  Stream<SubtitleWithState?> get currentSubtitleWithStateStream => 
      _stateManager.currentSubtitleWithStateStream;
  
  @override
  SubtitleWithState? get currentSubtitleWithState => 
      _stateManager.currentSubtitleWithState;
} 

================================================
FILE: lib/core/subtitle/utils/subtitle_matcher.dart
================================================
import 'package:asmrapp/data/models/files/child.dart';

class SubtitleMatcher {
  // 支持的字幕格式
  static const supportedFormats = ['.vtt', '.lrc'];
  
  // 检查文件是否为字幕文件
  static bool isSubtitleFile(String? fileName) {
    if (fileName == null) return false;
    return supportedFormats.any((format) => 
      fileName.toLowerCase().endsWith(format));
  }
  
  // 获取音频文件的可能的字幕文件名列表
  static List<String> getPossibleSubtitleNames(String audioFileName) {
    final names = <String>[];
    final baseName = _getBaseName(audioFileName);
    
    // 生成可能的字幕文件名
    for (final format in supportedFormats) {
      // 1. 直接替换扩展名: aaa.mp3 -> aaa.vtt
      names.add('$baseName$format');
      
      // 2. 保留原扩展名: aaa.mp3 -> aaa.mp3.vtt
      names.add('$audioFileName$format');
    }
    
    return names;
  }
  
  // 查找匹配的字幕文件
  static Child? findMatchingSubtitle(String audioFileName, List<Child> siblings) {
    final possibleNames = getPossibleSubtitleNames(audioFileName);
    
    // 遍历所有可能的字幕文件名
    for (final subtitleName in possibleNames) {
      try {
        final subtitleFile = siblings.firstWhere(
          (file) => file.title?.toLowerCase() == subtitleName.toLowerCase()
        );
        return subtitleFile;
      } catch (_) {
        // 继续查找下一个可能的文件名
        continue;
      }
    }
    
    return null;
  }
  
  // 获取不带扩展名的文件名
  static String _getBaseName(String fileName) {
    final lastDot = fileName.lastIndexOf('.');
    if (lastDot == -1) return fileName;
    return fileName.substring(0, lastDot);
  }
} 

================================================
FILE: lib/core/theme/app_colors.dart
================================================
import 'package:flutter/material.dart';

/// 应用颜色配置
class AppColors {
  // 禁止实例化
  const AppColors._();

  // 亮色主题颜色
  static const ColorScheme lightColorScheme = ColorScheme.light(
    // 基础色调
    primary: Color(0xFF6750A4),
    onPrimary: Colors.white,
    
    // 表面颜色
    surface: Colors.white,
    surfaceVariant: Color(0xFFF4F4F4),
    onSurface: Colors.black87,
    surfaceContainerHighest: Color(0xFFE6E6E6),
    
    // 背景颜色
    background: Colors.white,
    onBackground: Colors.black87,
    
    // 错误状态颜色
    error: Color(0xFFB3261E),
    errorContainer: Color(0xFFF9DEDC),
    onError: Colors.white,
  );

  // 暗色主题颜色
  static const ColorScheme darkColorScheme = ColorScheme.dark(
    // 基础色调
    primary: Color(0xFFD0BCFF),
    onPrimary: Color(0xFF381E72),
    
    // 表面颜色
    surface: Color(0xFF1C1B1F),
    surfaceVariant: Color(0xFF2B2930),
    onSurface: Colors.white,
    surfaceContainerHighest: Color(0xFF2B2B2B),
    
    // 背景颜色
    background: Color(0xFF1C1B1F),
    onBackground: Colors.white,
    
    // 错误状态颜色
    error: Color(0xFFF2B8B5),
    errorContainer: Color(0xFF8C1D18),
    onError: Color(0xFF601410),
  );
} 

================================================
FILE: lib/core/theme/app_theme.dart
================================================
import 'package:flutter/material.dart';
import 'app_colors.dart';

/// 应用主题配置
class AppTheme {
  // 禁止实例化
  const AppTheme._();

  // 亮色主题
  static ThemeData get light => ThemeData(
    useMaterial3: true,
    brightness: Brightness.light,
    colorScheme: AppColors.lightColorScheme,
    
    // Card主题
    cardTheme: const CardTheme(
      elevation: 0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.all(Radius.circular(12)),
      ),
    ),
    
    // AppBar主题
    appBarTheme: const AppBarTheme(
      centerTitle: true,
      elevation: 0,
      scrolledUnderElevation: 0,
    ),
  );

  // 暗色主题
  static ThemeData get dark => ThemeData(
    useMaterial3: true,
    brightness: Brightness.dark,
    colorScheme: AppColors.darkColorScheme,
    
    // Card主题
    cardTheme: const CardTheme(
      elevation: 0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.all(Radius.circular(12)),
      ),
    ),
    
    // AppBar主题
    appBarTheme: const AppBarTheme(
      centerTitle: true,
      elevation: 0,
      scrolledUnderElevation: 0,
    ),
  );
} 

================================================
FILE: lib/core/theme/theme_controller.dart
================================================
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class ThemeController extends ChangeNotifier {
  static const String _themeKey = 'theme_mode';
  final SharedPreferences _prefs;

  ThemeController(this._prefs) {
    // 从持久化存储加载主题模式
    final savedThemeMode = _prefs.getString(_themeKey);
    if (savedThemeMode != null) {
      _themeMode = ThemeMode.values.firstWhere(
        (mode) => mode.toString() == savedThemeMode,
        orElse: () => ThemeMode.system,
      );
    }
  }

  ThemeMode _themeMode = ThemeMode.system;
  
  ThemeMode get themeMode => _themeMode;

  // 切换主题模式
  Future<void> setThemeMode(ThemeMode mode) async {
    if (_themeMode == mode) return;
    
    _themeMode = mode;
    notifyListeners();
    
    // 保存到持久化存储
    await _prefs.setString(_themeKey, mode.toString());
  }

  // 切换到下一个主题模式
  Future<void> toggleThemeMode() async {
    final modes = ThemeMode.values;
    final currentIndex = modes.indexOf(_themeMode);
    final nextIndex = (currentIndex + 1) % modes.length;
    await setThemeMode(modes[nextIndex]);
  }
} 

================================================
FILE: lib/data/models/audio/README.md
================================================
# 音频数据模型

此目录包含所有音频相关的数据模型定义。

## 文件结构

- `audio_track.dart` - 音频轨道模型
- `playlist.dart` - 播放列表模型
- `audio_metadata.dart` - 音频元数据模型

## 说明

这些模型用于:
- 音频文件信息的封装
- 播放列表数据的组织
- 音频元数据的管理 

================================================
FILE: lib/data/models/auth/auth_resp/auth_resp.dart
================================================
import 'package:freezed_annotation/freezed_annotation.dart';

import 'user.dart';

part 'auth_resp.freezed.dart';
part 'auth_resp.g.dart';

@freezed
class AuthResp with _$AuthResp {
  factory AuthResp({
    User? user,
    String? token,
  }) = _AuthResp;

  factory AuthResp.fromJson(Map<String, dynamic> json) =>
      _$AuthRespFromJson(json);
}


================================================
FILE: lib/data/models/auth/auth_resp/auth_resp.freezed.dart
================================================
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark

part of 'auth_resp.dart';

// **************************************************************************
// FreezedGenerator
// **************************************************************************

T _$identity<T>(T value) => value;

final _privateConstructorUsedError = UnsupportedError(
    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');

AuthResp _$AuthRespFromJson(Map<String, dynamic> json) {
  return _AuthResp.fromJson(json);
}

/// @nodoc
mixin _$AuthResp {
  User? get user => throw _privateConstructorUsedError;
  String? get token => throw _privateConstructorUsedError;

  /// Serializes this AuthResp to a JSON map.
  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;

  /// Create a copy of AuthResp
  /// with the given fields replaced by the non-null parameter values.
  @JsonKey(includeFromJson: false, includeToJson: false)
  $AuthRespCopyWith<AuthResp> get copyWith =>
      throw _privateConstructorUsedError;
}

/// @nodoc
abstract class $AuthRespCopyWith<$Res> {
  factory $AuthRespCopyWith(AuthResp value, $Res Function(AuthResp) then) =
      _$AuthRespCopyWithImpl<$Res, AuthResp>;
  @useResult
  $Res call({User? user, String? token});

  $UserCopyWith<$Res>? get user;
}

/// @nodoc
class _$AuthRespCopyWithImpl<$Res, $Val extends AuthResp>
    implements $AuthRespCopyWith<$Res> {
  _$AuthRespCopyWithImpl(this._value, this._then);

  // ignore: unused_field
  final $Val _value;
  // ignore: unused_field
  final $Res Function($Val) _then;

  /// Create a copy of AuthResp
  /// with the given fields replaced by the non-null parameter values.
  @pragma('vm:prefer-inline')
  @override
  $Res call({
    Object? user = freezed,
    Object? token = freezed,
  }) {
    return _then(_value.copyWith(
      user: freezed == user
          ? _value.user
          : user // ignore: cast_nullable_to_non_nullable
              as User?,
      token: freezed == token
          ? _value.token
          : token // ignore: cast_nullable_to_non_nullable
              as String?,
    ) as $Val);
  }

  /// Create a copy of AuthResp
  /// with the given fields replaced by the non-null parameter values.
  @override
  @pragma('vm:prefer-inline')
  $UserCopyWith<$Res>? get user {
    if (_value.user == null) {
      return null;
    }

    return $UserCopyWith<$Res>(_value.user!, (value) {
      return _then(_value.copyWith(user: value) as $Val);
    });
  }
}

/// @nodoc
abstract class _$$AuthRespImplCopyWith<$Res>
    implements $AuthRespCopyWith<$Res> {
  factory _$$AuthRespImplCopyWith(
          _$AuthRespImpl value, $Res Function(_$AuthRespImpl) then) =
      __$$AuthRespImplCopyWithImpl<$Res>;
  @override
  @useResult
  $Res call({User? user, String? token});

  @override
  $UserCopyWith<$Res>? get user;
}

/// @nodoc
class __$$AuthRespImplCopyWithImpl<$Res>
    extends _$AuthRespCopyWithImpl<$Res, _$AuthRespImpl>
    implements _$$AuthRespImplCopyWith<$Res> {
  __$$AuthRespImplCopyWithImpl(
      _$AuthRespImpl _value, $Res Function(_$AuthRespImpl) _then)
      : super(_value, _then);

  /// Create a copy of AuthResp
  /// with the given fields replaced by the non-null parameter values.
  @pragma('vm:prefer-inline')
  @override
  $Res call({
    Object? user = freezed,
    Object? token = freezed,
  }) {
    return _then(_$AuthRespImpl(
      user: freezed == user
          ? _value.user
          : user // ignore: cast_nullable_to_non_nullable
              as User?,
      token: freezed == token
          ? _value.token
          : token // ignore: cast_nullable_to_non_nullable
              as String?,
    ));
  }
}

/// @nodoc
@JsonSerializable()
class _$AuthRespImpl implements _AuthResp {
  _$AuthRespImpl({this.user, this.token});

  factory _$AuthRespImpl.fromJson(Map<String, dynamic> json) =>
      _$$AuthRespImplFromJson(json);

  @override
  final User? user;
  @override
  final String? token;

  @override
  String toString() {
    return 'AuthResp(user: $user, token: $token)';
  }

  @override
  bool operator ==(Object other) {
    return identic
Download .txt
gitextract_mk7l9x6b/

├── .github/
│   ├── scripts/
│   │   └── process_commits.sh
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .metadata
├── LICENSE
├── README.md
├── README_en.md
├── analysis_options.yaml
├── android/
│   ├── .gitignore
│   ├── app/
│   │   ├── build.gradle
│   │   ├── proguard-rules.pro
│   │   └── src/
│   │       ├── debug/
│   │       │   └── AndroidManifest.xml
│   │       ├── main/
│   │       │   ├── AndroidManifest.xml
│   │       │   ├── kotlin/
│   │       │   │   ├── com/
│   │       │   │   │   └── example/
│   │       │   │   │       └── asmrapp/
│   │       │   │   │           └── MainActivity.kt
│   │       │   │   └── one/
│   │       │   │       └── asmr/
│   │       │   │           └── yuro/
│   │       │   │               ├── MainActivity.kt
│   │       │   │               └── lyric/
│   │       │   │                   ├── LyricOverlayPlugin.kt
│   │       │   │                   └── LyricOverlayService.kt
│   │       │   └── res/
│   │       │       ├── drawable/
│   │       │       │   └── launch_background.xml
│   │       │       ├── drawable-v21/
│   │       │       │   └── launch_background.xml
│   │       │       ├── layout/
│   │       │       │   └── lyric_overlay.xml
│   │       │       ├── values/
│   │       │       │   └── styles.xml
│   │       │       ├── values-night/
│   │       │       │   └── styles.xml
│   │       │       └── xml/
│   │       │           └── network_security_config.xml
│   │       └── profile/
│   │           └── AndroidManifest.xml
│   ├── build.gradle
│   ├── gradle/
│   │   └── wrapper/
│   │       └── gradle-wrapper.properties
│   ├── gradle.properties
│   └── settings.gradle
├── devtools_options.yaml
├── docs/
│   ├── architecture.md
│   ├── audio_architecture.md
│   ├── guidelines.md
│   ├── guidelines_en.md
│   └── guidelines_zh.md
├── ios/
│   ├── .gitignore
│   ├── Flutter/
│   │   ├── AppFrameworkInfo.plist
│   │   ├── Debug.xcconfig
│   │   └── Release.xcconfig
│   ├── Podfile
│   ├── Runner/
│   │   ├── AppDelegate.swift
│   │   ├── Assets.xcassets/
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   └── Contents.json
│   │   │   └── LaunchImage.imageset/
│   │   │       ├── Contents.json
│   │   │       └── README.md
│   │   ├── Base.lproj/
│   │   │   ├── LaunchScreen.storyboard
│   │   │   └── Main.storyboard
│   │   ├── Info.plist
│   │   └── Runner-Bridging-Header.h
│   ├── Runner.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   ├── contents.xcworkspacedata
│   │   │   └── xcshareddata/
│   │   │       ├── IDEWorkspaceChecks.plist
│   │   │       └── WorkspaceSettings.xcsettings
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── Runner.xcscheme
│   ├── Runner.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       ├── IDEWorkspaceChecks.plist
│   │       └── WorkspaceSettings.xcsettings
│   └── RunnerTests/
│       └── RunnerTests.swift
├── lib/
│   ├── common/
│   │   └── constants/
│   │       └── strings.dart
│   ├── core/
│   │   ├── audio/
│   │   │   ├── README.md
│   │   │   ├── audio_player_handler.dart
│   │   │   ├── audio_player_service.dart
│   │   │   ├── audio_service.dart
│   │   │   ├── cache/
│   │   │   │   └── audio_cache_manager.dart
│   │   │   ├── controllers/
│   │   │   │   └── playback_controller.dart
│   │   │   ├── events/
│   │   │   │   ├── playback_event.dart
│   │   │   │   └── playback_event_hub.dart
│   │   │   ├── i_audio_player_service.dart
│   │   │   ├── models/
│   │   │   │   ├── audio_track_info.dart
│   │   │   │   ├── file_path.dart
│   │   │   │   ├── play_mode.dart
│   │   │   │   ├── playback_context.dart
│   │   │   │   └── subtitle.dart
│   │   │   ├── notification/
│   │   │   │   └── audio_notification_service.dart
│   │   │   ├── state/
│   │   │   │   └── playback_state_manager.dart
│   │   │   ├── storage/
│   │   │   │   ├── i_playback_state_repository.dart
│   │   │   │   └── playback_state_repository.dart
│   │   │   └── utils/
│   │   │       ├── audio_error_handler.dart
│   │   │       ├── playlist_builder.dart
│   │   │       └── track_info_creator.dart
│   │   ├── cache/
│   │   │   └── recommendation_cache_manager.dart
│   │   ├── di/
│   │   │   └── service_locator.dart
│   │   ├── platform/
│   │   │   ├── dummy_lyric_overlay_controller.dart
│   │   │   ├── i_lyric_overlay_controller.dart
│   │   │   ├── lyric_overlay_controller.dart
│   │   │   ├── lyric_overlay_manager.dart
│   │   │   └── wakelock_controller.dart
│   │   ├── subtitle/
│   │   │   ├── cache/
│   │   │   │   └── subtitle_cache_manager.dart
│   │   │   ├── i_subtitle_service.dart
│   │   │   ├── managers/
│   │   │   │   └── subtitle_state_manager.dart
│   │   │   ├── parsers/
│   │   │   │   ├── lrc_parser.dart
│   │   │   │   ├── subtitle_parser.dart
│   │   │   │   ├── subtitle_parser_factory.dart
│   │   │   │   └── vtt_parser.dart
│   │   │   ├── subtitle_loader.dart
│   │   │   ├── subtitle_service.dart
│   │   │   └── utils/
│   │   │       └── subtitle_matcher.dart
│   │   └── theme/
│   │       ├── app_colors.dart
│   │       ├── app_theme.dart
│   │       └── theme_controller.dart
│   ├── data/
│   │   ├── models/
│   │   │   ├── audio/
│   │   │   │   └── README.md
│   │   │   ├── auth/
│   │   │   │   └── auth_resp/
│   │   │   │       ├── auth_resp.dart
│   │   │   │       ├── auth_resp.freezed.dart
│   │   │   │       ├── auth_resp.g.dart
│   │   │   │       ├── user.dart
│   │   │   │       ├── user.freezed.dart
│   │   │   │       └── user.g.dart
│   │   │   ├── files/
│   │   │   │   ├── child.dart
│   │   │   │   ├── child.freezed.dart
│   │   │   │   ├── child.g.dart
│   │   │   │   ├── files.dart
│   │   │   │   ├── files.freezed.dart
│   │   │   │   ├── files.g.dart
│   │   │   │   ├── work.dart
│   │   │   │   ├── work.freezed.dart
│   │   │   │   └── work.g.dart
│   │   │   ├── mark_lists/
│   │   │   │   ├── mark_lists.dart
│   │   │   │   ├── mark_lists.freezed.dart
│   │   │   │   ├── mark_lists.g.dart
│   │   │   │   ├── pagination.dart
│   │   │   │   ├── pagination.freezed.dart
│   │   │   │   ├── pagination.g.dart
│   │   │   │   ├── playlist.dart
│   │   │   │   ├── playlist.freezed.dart
│   │   │   │   └── playlist.g.dart
│   │   │   ├── mark_status.dart
│   │   │   ├── my_lists/
│   │   │   │   ├── README.md
│   │   │   │   └── my_playlists/
│   │   │   │       ├── my_playlists.dart
│   │   │   │       ├── my_playlists.freezed.dart
│   │   │   │       ├── my_playlists.g.dart
│   │   │   │       ├── pagination.dart
│   │   │   │       ├── pagination.freezed.dart
│   │   │   │       ├── pagination.g.dart
│   │   │   │       ├── playlist.dart
│   │   │   │       ├── playlist.freezed.dart
│   │   │   │       └── playlist.g.dart
│   │   │   ├── playback/
│   │   │   │   ├── playback_state.dart
│   │   │   │   ├── playback_state.freezed.dart
│   │   │   │   └── playback_state.g.dart
│   │   │   ├── playlists_with_exist_statu/
│   │   │   │   ├── pagination.dart
│   │   │   │   ├── pagination.freezed.dart
│   │   │   │   ├── pagination.g.dart
│   │   │   │   ├── playlist.dart
│   │   │   │   ├── playlist.freezed.dart
│   │   │   │   ├── playlist.g.dart
│   │   │   │   ├── playlists_with_exist_statu.dart
│   │   │   │   ├── playlists_with_exist_statu.freezed.dart
│   │   │   │   └── playlists_with_exist_statu.g.dart
│   │   │   └── works/
│   │   │       ├── circle.dart
│   │   │       ├── circle.freezed.dart
│   │   │       ├── circle.g.dart
│   │   │       ├── en_us.dart
│   │   │       ├── en_us.freezed.dart
│   │   │       ├── en_us.g.dart
│   │   │       ├── i18n.dart
│   │   │       ├── i18n.freezed.dart
│   │   │       ├── i18n.g.dart
│   │   │       ├── ja_jp.dart
│   │   │       ├── ja_jp.freezed.dart
│   │   │       ├── ja_jp.g.dart
│   │   │       ├── language_edition.dart
│   │   │       ├── language_edition.freezed.dart
│   │   │       ├── language_edition.g.dart
│   │   │       ├── other_language_editions_in_db.dart
│   │   │       ├── other_language_editions_in_db.freezed.dart
│   │   │       ├── other_language_editions_in_db.g.dart
│   │   │       ├── pagination.dart
│   │   │       ├── pagination.freezed.dart
│   │   │       ├── pagination.g.dart
│   │   │       ├── tag.dart
│   │   │       ├── tag.freezed.dart
│   │   │       ├── tag.g.dart
│   │   │       ├── translation_bonus_lang.dart
│   │   │       ├── translation_bonus_lang.freezed.dart
│   │   │       ├── translation_bonus_lang.g.dart
│   │   │       ├── translation_info.dart
│   │   │       ├── translation_info.freezed.dart
│   │   │       ├── translation_info.g.dart
│   │   │       ├── work.dart
│   │   │       ├── work.freezed.dart
│   │   │       ├── work.g.dart
│   │   │       ├── works.dart
│   │   │       ├── works.freezed.dart
│   │   │       ├── works.g.dart
│   │   │       ├── zh_cn.dart
│   │   │       ├── zh_cn.freezed.dart
│   │   │       └── zh_cn.g.dart
│   │   ├── repositories/
│   │   │   ├── audio/
│   │   │   │   └── README.md
│   │   │   └── auth_repository.dart
│   │   └── services/
│   │       ├── api_service.dart
│   │       ├── auth_service.dart
│   │       └── interceptors/
│   │           └── auth_interceptor.dart
│   ├── main.dart
│   ├── presentation/
│   │   ├── layouts/
│   │   │   ├── work_layout_config.dart
│   │   │   └── work_layout_strategy.dart
│   │   ├── models/
│   │   │   └── filter_state.dart
│   │   ├── viewmodels/
│   │   │   ├── auth_viewmodel.dart
│   │   │   ├── base/
│   │   │   │   └── paginated_works_viewmodel.dart
│   │   │   ├── detail_viewmodel.dart
│   │   │   ├── favorites_viewmodel.dart
│   │   │   ├── home_viewmodel.dart
│   │   │   ├── player_viewmodel.dart
│   │   │   ├── playlist_works_viewmodel.dart
│   │   │   ├── playlists_viewmodel.dart
│   │   │   ├── popular_viewmodel.dart
│   │   │   ├── recommend_viewmodel.dart
│   │   │   ├── search_viewmodel.dart
│   │   │   ├── settings/
│   │   │   │   └── cache_manager_viewmodel.dart
│   │   │   └── similar_works_viewmodel.dart
│   │   └── widgets/
│   │       └── auth/
│   │           └── login_dialog.dart
│   ├── screens/
│   │   ├── contents/
│   │   │   ├── home_content.dart
│   │   │   ├── playlists/
│   │   │   │   ├── playlist_works_view.dart
│   │   │   │   └── playlists_list_view.dart
│   │   │   ├── playlists_content.dart
│   │   │   ├── popular_content.dart
│   │   │   └── recommend_content.dart
│   │   ├── detail_screen.dart
│   │   ├── docs/
│   │   │   └── main_screen.md
│   │   ├── favorites_screen.dart
│   │   ├── main_screen.dart
│   │   ├── player_screen.dart
│   │   ├── search_screen.dart
│   │   ├── settings/
│   │   │   └── cache_manager_screen.dart
│   │   └── similar_works_screen.dart
│   ├── utils/
│   │   ├── file_size_formatter.dart
│   │   └── logger.dart
│   └── widgets/
│       ├── common/
│       │   └── tag_chip.dart
│       ├── detail/
│       │   ├── mark_selection_dialog.dart
│       │   ├── playlist_selection_dialog.dart
│       │   ├── work_action_buttons.dart
│       │   ├── work_cover.dart
│       │   ├── work_file_item.dart
│       │   ├── work_files_list.dart
│       │   ├── work_files_skeleton.dart
│       │   ├── work_folder_item.dart
│       │   ├── work_info.dart
│       │   ├── work_info_header.dart
│       │   └── work_stats_info.dart
│       ├── drawer_menu.dart
│       ├── filter/
│       │   ├── filter_panel.dart
│       │   └── filter_with_keyword.dart
│       ├── lyrics/
│       │   └── components/
│       │       ├── lyric_line.dart
│       │       └── player_lyric_view.dart
│       ├── mini_player/
│       │   ├── mini_player.dart
│       │   ├── mini_player_controls.dart
│       │   ├── mini_player_cover.dart
│       │   └── mini_player_progress.dart
│       ├── pagination_controls.dart
│       ├── player/
│       │   ├── player_controls.dart
│       │   ├── player_cover.dart
│       │   ├── player_progress.dart
│       │   ├── player_seek_controls.dart
│       │   └── player_work_info.dart
│       ├── work_card/
│       │   ├── components/
│       │   │   ├── work_cover_image.dart
│       │   │   ├── work_footer.dart
│       │   │   ├── work_info_section.dart
│       │   │   ├── work_tags_panel.dart
│       │   │   └── work_title.dart
│       │   └── work_card.dart
│       ├── work_grid/
│       │   ├── components/
│       │   │   ├── grid_content.dart
│       │   │   ├── grid_empty.dart
│       │   │   ├── grid_error.dart
│       │   │   └── grid_loading.dart
│       │   ├── enhanced_work_grid_view.dart
│       │   └── models/
│       │       └── grid_config.dart
│       ├── work_grid.dart
│       ├── work_grid_view.dart
│       └── work_row.dart
├── linux/
│   ├── .gitignore
│   ├── CMakeLists.txt
│   ├── flutter/
│   │   ├── CMakeLists.txt
│   │   ├── generated_plugin_registrant.cc
│   │   ├── generated_plugin_registrant.h
│   │   └── generated_plugins.cmake
│   ├── main.cc
│   ├── my_application.cc
│   └── my_application.h
├── macos/
│   ├── .gitignore
│   ├── Flutter/
│   │   ├── Flutter-Debug.xcconfig
│   │   ├── Flutter-Release.xcconfig
│   │   └── GeneratedPluginRegistrant.swift
│   ├── Podfile
│   ├── Runner/
│   │   ├── AppDelegate.swift
│   │   ├── Assets.xcassets/
│   │   │   └── AppIcon.appiconset/
│   │   │       └── Contents.json
│   │   ├── Base.lproj/
│   │   │   └── MainMenu.xib
│   │   ├── Configs/
│   │   │   ├── AppInfo.xcconfig
│   │   │   ├── Debug.xcconfig
│   │   │   ├── Release.xcconfig
│   │   │   └── Warnings.xcconfig
│   │   ├── DebugProfile.entitlements
│   │   ├── Info.plist
│   │   ├── MainFlutterWindow.swift
│   │   └── Release.entitlements
│   ├── Runner.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   └── xcshareddata/
│   │   │       └── IDEWorkspaceChecks.plist
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── Runner.xcscheme
│   ├── Runner.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   └── RunnerTests/
│       └── RunnerTests.swift
├── pubspec.yaml
├── test/
│   └── widget_test.dart
├── web/
│   ├── index.html
│   └── manifest.json
└── windows/
    ├── .gitignore
    ├── CMakeLists.txt
    ├── flutter/
    │   ├── CMakeLists.txt
    │   ├── generated_plugin_registrant.cc
    │   ├── generated_plugin_registrant.h
    │   └── generated_plugins.cmake
    └── runner/
        ├── CMakeLists.txt
        ├── Runner.rc
        ├── flutter_window.cpp
        ├── flutter_window.h
        ├── main.cpp
        ├── resource.h
        ├── runner.exe.manifest
        ├── utils.cpp
        ├── utils.h
        ├── win32_window.cpp
        └── win32_window.h
Download .txt
SYMBOL INDEX (1131 symbols across 216 files)

FILE: lib/common/constants/strings.dart
  class Strings (line 1) | class Strings {

FILE: lib/core/audio/audio_player_handler.dart
  class AudioPlayerHandler (line 6) | class AudioPlayerHandler extends BaseAudioHandler {
    method play (line 45) | Future<void> play()
    method pause (line 51) | Future<void> pause()
    method seek (line 57) | Future<void> seek(Duration position)
    method stop (line 63) | Future<void> stop()

FILE: lib/core/audio/audio_player_service.dart
  class AudioPlayerService (line 15) | class AudioPlayerService implements IAudioPlayerService {
    method _init (line 45) | Future<void> _init()
    method pause (line 89) | Future<void> pause()
    method resume (line 92) | Future<void> resume()
    method stop (line 95) | Future<void> stop()
    method seek (line 101) | Future<void> seek(Duration position)
    method previous (line 104) | Future<void> previous()
    method next (line 107) | Future<void> next()
    method playWithContext (line 111) | Future<void> playWithContext(PlaybackContext context)
    method savePlaybackState (line 126) | Future<void> savePlaybackState()
    method restorePlaybackState (line 129) | Future<void> restorePlaybackState()
    method dispose (line 175) | Future<void> dispose()

FILE: lib/core/audio/audio_service.dart
  class AudioService (line 3) | abstract class AudioService {
    method play (line 4) | Future<void> play(String url)
    method pause (line 5) | Future<void> pause()
    method resume (line 6) | Future<void> resume()
    method stop (line 7) | Future<void> stop()
    method dispose (line 8) | Future<void> dispose()

FILE: lib/core/audio/cache/audio_cache_manager.dart
  class AudioCacheManager (line 10) | class AudioCacheManager {
    method createAudioSource (line 16) | Future<AudioSource> createAudioSource(String url)
    method cleanCache (line 40) | Future<void> cleanCache()
    method getCacheSize (line 75) | Future<int> getCacheSize()
    method _createCachingSource (line 96) | AudioSource _createCachingSource(String url, File cacheFile)
    method _isCacheValid (line 104) | Future<bool> _isCacheValid(File cacheFile, String fileName)
    method _getCacheFile (line 134) | Future<File> _getCacheFile(String url)
    method _generateFileName (line 141) | String _generateFileName(String url)
    method _getCacheDir (line 148) | Future<Directory> _getCacheDir()

FILE: lib/core/audio/controllers/playback_controller.dart
  class PlaybackController (line 11) | class PlaybackController {
    method play (line 25) | Future<void> play()
    method pause (line 26) | Future<void> pause()
    method stop (line 27) | Future<void> stop()
    method seek (line 28) | Future<void> seek(Duration position, {int? index})
    method next (line 31) | Future<void> next()
    method previous (line 56) | Future<void> previous()
    method setPlaybackContext (line 90) | Future<void> setPlaybackContext(PlaybackContext context, {Duration? in...
    method _updateTrackAndContext (line 153) | void _updateTrackAndContext(Child file, Work work)

FILE: lib/core/audio/events/playback_event.dart
  class PlaybackEvent (line 8) | abstract class PlaybackEvent {}
  class PlaybackStateEvent (line 11) | class PlaybackStateEvent extends PlaybackEvent {
  class PlaybackContextEvent (line 19) | class PlaybackContextEvent extends PlaybackEvent {
  class TrackChangeEvent (line 25) | class TrackChangeEvent extends PlaybackEvent {
  class PlaybackErrorEvent (line 33) | class PlaybackErrorEvent extends PlaybackEvent {
  class PlaybackCompletedEvent (line 41) | class PlaybackCompletedEvent extends PlaybackEvent {
  class PlaybackProgressEvent (line 47) | class PlaybackProgressEvent extends PlaybackEvent {
  class RequestInitialStateEvent (line 54) | class RequestInitialStateEvent extends PlaybackEvent {}
  class InitialStateEvent (line 56) | class InitialStateEvent extends PlaybackEvent {

FILE: lib/core/audio/events/playback_event_hub.dart
  class PlaybackEventHub (line 4) | class PlaybackEventHub {
    method emit (line 34) | void emit(PlaybackEvent event)
    method dispose (line 37) | void dispose()

FILE: lib/core/audio/i_audio_player_service.dart
  class IAudioPlayerService (line 4) | abstract class IAudioPlayerService {
    method pause (line 6) | Future<void> pause()
    method resume (line 7) | Future<void> resume()
    method stop (line 8) | Future<void> stop()
    method seek (line 9) | Future<void> seek(Duration position)
    method previous (line 10) | Future<void> previous()
    method next (line 11) | Future<void> next()
    method dispose (line 12) | Future<void> dispose()
    method playWithContext (line 15) | Future<void> playWithContext(PlaybackContext context)
    method savePlaybackState (line 22) | Future<void> savePlaybackState()
    method restorePlaybackState (line 23) | Future<void> restorePlaybackState()

FILE: lib/core/audio/models/audio_track_info.dart
  class AudioTrackInfo (line 1) | class AudioTrackInfo {

FILE: lib/core/audio/models/file_path.dart
  class FilePath (line 7) | class FilePath {
    method getPath (line 12) | String? getPath(Child targetFile, Files root)
    method _findPathSegments (line 27) | List<String>? _findPathSegments(List<Child>? children, Child targetFil...
    method getSiblings (line 53) | List<Child> getSiblings(Child targetFile, Files root)
    method _findDirectoryByPath (line 89) | List<Child>? _findDirectoryByPath(List<Child>? children, String path)
    method findFirstAudioFolderPath (line 117) | List<String>? findFirstAudioFolderPath(
    method findPath (line 125) | void findPath(Child folder, List<String> currentPath)
    method isInPath (line 167) | bool isInPath(List<String>? path, String? folderName)

FILE: lib/core/audio/models/play_mode.dart
  type PlayMode (line 1) | enum PlayMode {

FILE: lib/core/audio/models/playback_context.dart
  class PlaybackContext (line 9) | class PlaybackContext {
    method validate (line 17) | void validate()
    method _getPlaylistFromSameDirectory (line 71) | List<Child> _getPlaylistFromSameDirectory(Child currentFile, Files files)
    method getNextFile (line 108) | Child? getNextFile()
    method getPreviousFile (line 124) | Child? getPreviousFile()
    method copyWithFile (line 147) | PlaybackContext copyWithFile(Child newFile)
    method copyWithMode (line 157) | PlaybackContext copyWithMode(PlayMode newMode)
    method getPlayableFiles (line 167) | List<Child> getPlayableFiles()
    method _getBaseName (line 176) | String? _getBaseName(String? filename)

FILE: lib/core/audio/models/subtitle.dart
  type SubtitleState (line 3) | enum SubtitleState {
  class Subtitle (line 9) | class Subtitle {
    method getNext (line 22) | Subtitle? getNext(SubtitleList list)
    method getPrevious (line 29) | Subtitle? getPrevious(SubtitleList list)
    method toString (line 37) | String toString()
  class SubtitleList (line 40) | class SubtitleList {
    method getCurrentSubtitle (line 54) | SubtitleWithState? getCurrentSubtitle(Duration position)
    method getSubtitlesInRange (line 87) | List<Subtitle> getSubtitlesInRange(int start, int count)
    method getCurrentContext (line 93) | (Subtitle?, Subtitle?, Subtitle?) getCurrentContext()
    method parse (line 103) | SubtitleList parse(String vttContent)
    method _parseTimestamp (line 145) | Duration? _parseTimestamp(String timestamp)
  class SubtitleWithState (line 164) | class SubtitleWithState {

FILE: lib/core/audio/notification/audio_notification_service.dart
  class AudioNotificationService (line 9) | class AudioNotificationService {
    method init (line 20) | Future<void> init()
    method _setupEventListeners (line 40) | void _setupEventListeners()
    method updateMetadata (line 47) | void updateMetadata(AudioTrackInfo trackInfo)
    method dispose (line 62) | Future<void> dispose()

FILE: lib/core/audio/state/playback_state_manager.dart
  class PlaybackStateManager (line 15) | class PlaybackStateManager {
    method initStateListeners (line 34) | void initStateListeners()
    method updateContext (line 66) | void updateContext(PlaybackContext? context)
    method updateTrackInfo (line 73) | void updateTrackInfo(AudioTrackInfo track)
    method updateTrackAndContext (line 78) | void updateTrackAndContext(Child file, Work work)
    method _onPlaybackCompleted (line 88) | void _onPlaybackCompleted()
    method clearState (line 97) | void clearState()
    method saveState (line 104) | Future<void> saveState()
    method loadState (line 130) | Future<PlaybackState?> loadState()
    method _setupEventListeners (line 144) | void _setupEventListeners()
    method dispose (line 156) | void dispose()

FILE: lib/core/audio/storage/i_playback_state_repository.dart
  class IPlaybackStateRepository (line 3) | abstract class IPlaybackStateRepository {
    method saveState (line 4) | Future<void> saveState(PlaybackState state)
    method loadState (line 5) | Future<PlaybackState?> loadState()

FILE: lib/core/audio/storage/playback_state_repository.dart
  class PlaybackStateRepository (line 7) | class PlaybackStateRepository implements IPlaybackStateRepository {
    method saveState (line 14) | Future<void> saveState(PlaybackState state)
    method loadState (line 27) | Future<PlaybackState?> loadState()

FILE: lib/core/audio/utils/audio_error_handler.dart
  type AudioErrorType (line 3) | enum AudioErrorType {
  class AudioError (line 11) | class AudioError implements Exception {
    method toString (line 19) | String toString()
  class AudioErrorHandler (line 22) | class AudioErrorHandler {
    method handleError (line 23) | void handleError(
    method throwError (line 33) | Never throwError(
    method _getErrorMessage (line 42) | String _getErrorMessage(AudioErrorType type, String operation)

FILE: lib/core/audio/utils/playlist_builder.dart
  class PlaylistBuilder (line 5) | class PlaylistBuilder {
    method buildAudioSources (line 6) | Future<List<AudioSource>> buildAudioSources(List<Child> files)
    method updatePlaylist (line 14) | Future<void> updatePlaylist(
    method setPlaylistSource (line 22) | Future<void> setPlaylistSource({

FILE: lib/core/audio/utils/track_info_creator.dart
  class TrackInfoCreator (line 5) | class TrackInfoCreator {
    method createTrackInfo (line 6) | AudioTrackInfo createTrackInfo({
    method createFromFile (line 20) | AudioTrackInfo createFromFile(Child file, Work work)

FILE: lib/core/cache/recommendation_cache_manager.dart
  class RecommendationCacheManager (line 5) | class RecommendationCacheManager {
    method _generateKey (line 19) | String _generateKey(String itemId, int page, int subtitle)
    method get (line 24) | WorksResponse? get(String itemId, int page, int subtitle)
    method set (line 44) | void set(String itemId, int page, int subtitle, WorksResponse data)
    method clear (line 57) | void clear()
    method remove (line 63) | void remove(String itemId)
  class _CacheItem (line 70) | class _CacheItem {

FILE: lib/core/di/service_locator.dart
  function setupServiceLocator (line 26) | Future<void> setupServiceLocator()
  function setupSubtitleServices (line 99) | Future<void> setupSubtitleServices()

FILE: lib/core/platform/dummy_lyric_overlay_controller.dart
  class DummyLyricOverlayController (line 4) | class DummyLyricOverlayController implements ILyricOverlayController {
    method initialize (line 8) | Future<void> initialize()
    method show (line 12) | Future<void> show()
    method hide (line 17) | Future<void> hide()
    method updateLyric (line 22) | Future<void> updateLyric(String? text)
    method checkPermission (line 27) | Future<bool> checkPermission()
    method requestPermission (line 32) | Future<bool> requestPermission()
    method dispose (line 38) | Future<void> dispose()
    method isShowing (line 43) | Future<bool> isShowing()

FILE: lib/core/platform/i_lyric_overlay_controller.dart
  class ILyricOverlayController (line 1) | abstract class ILyricOverlayController {
    method initialize (line 3) | Future<void> initialize()
    method show (line 6) | Future<void> show()
    method hide (line 9) | Future<void> hide()
    method updateLyric (line 12) | Future<void> updateLyric(String? text)
    method checkPermission (line 15) | Future<bool> checkPermission()
    method requestPermission (line 18) | Future<bool> requestPermission()
    method dispose (line 21) | Future<void> dispose()
    method isShowing (line 24) | Future<bool> isShowing()

FILE: lib/core/platform/lyric_overlay_controller.dart
  class LyricOverlayController (line 6) | class LyricOverlayController implements ILyricOverlayController {
    method initialize (line 11) | Future<void> initialize()
    method show (line 23) | Future<void> show()
    method hide (line 29) | Future<void> hide()
    method updateLyric (line 35) | Future<void> updateLyric(String? text)
    method checkPermission (line 41) | Future<bool> checkPermission()
    method requestPermission (line 47) | Future<bool> requestPermission()
    method dispose (line 54) | Future<void> dispose()
    method isShowing (line 60) | Future<bool> isShowing()

FILE: lib/core/platform/lyric_overlay_manager.dart
  class LyricOverlayManager (line 7) | class LyricOverlayManager {
    method initialize (line 19) | Future<void> initialize()
    method dispose (line 34) | Future<void> dispose()
    method checkPermission (line 39) | Future<bool> checkPermission()
    method requestPermission (line 43) | Future<bool> requestPermission()
    method show (line 47) | Future<void> show()
    method hide (line 56) | Future<void> hide()
    method showWithPermissionCheck (line 64) | Future<void> showWithPermissionCheck(BuildContext context)
    method _showPermissionDialog (line 82) | Future<bool> _showPermissionDialog(BuildContext context)
    method toggle (line 103) | Future<void> toggle(BuildContext context)
    method syncState (line 113) | Future<void> syncState()

FILE: lib/core/platform/wakelock_controller.dart
  class WakeLockController (line 7) | class WakeLockController extends ChangeNotifier {
    method _loadState (line 19) | Future<void> _loadState()
    method toggle (line 31) | Future<void> toggle()
    method dispose (line 49) | Future<void> dispose()

FILE: lib/core/subtitle/cache/subtitle_cache_manager.dart
  class SubtitleCacheManager (line 7) | class SubtitleCacheManager {
    method getCachedContent (line 21) | Future<String?> getCachedContent(String url)
    method cacheContent (line 33) | Future<void> cacheContent(String url, String content)
    method clearCache (line 47) | Future<void> clearCache()
    method getSize (line 57) | Future<int> getSize()

FILE: lib/core/subtitle/i_subtitle_service.dart
  class ISubtitleService (line 3) | abstract class ISubtitleService {
    method loadSubtitle (line 5) | Future<void> loadSubtitle(String url)
    method updatePosition (line 17) | void updatePosition(Duration position)
    method dispose (line 20) | void dispose()
    method clearSubtitle (line 26) | void clearSubtitle()

FILE: lib/core/subtitle/managers/subtitle_state_manager.dart
  class SubtitleStateManager (line 5) | class SubtitleStateManager {
    method setSubtitleList (line 23) | void setSubtitleList(SubtitleList? subtitleList)
    method updatePosition (line 28) | void updatePosition(Duration position)
    method clear (line 41) | void clear()
    method dispose (line 51) | void dispose()

FILE: lib/core/subtitle/parsers/lrc_parser.dart
  class LrcParser (line 5) | class LrcParser extends BaseSubtitleParser {
    method canParse (line 10) | bool canParse(String content)
    method doParse (line 16) | SubtitleList doParse(String content)
    method _parseTimestamp (line 79) | Duration _parseTimestamp({

FILE: lib/core/subtitle/parsers/subtitle_parser.dart
  class SubtitleParser (line 4) | abstract class SubtitleParser {
    method parse (line 6) | SubtitleList parse(String content)
    method canParse (line 9) | bool canParse(String content)
  class BaseSubtitleParser (line 13) | abstract class BaseSubtitleParser implements SubtitleParser {
    method parse (line 15) | SubtitleList parse(String content)
    method doParse (line 23) | SubtitleList doParse(String content)

FILE: lib/core/subtitle/parsers/subtitle_parser_factory.dart
  class SubtitleParserFactory (line 6) | class SubtitleParserFactory {
    method getParser (line 12) | SubtitleParser? getParser(String content)

FILE: lib/core/subtitle/parsers/vtt_parser.dart
  class VttParser (line 4) | class VttParser extends BaseSubtitleParser {
    method canParse (line 8) | bool canParse(String content)
    method doParse (line 13) | SubtitleList doParse(String content)
    method _parseTimeString (line 55) | Duration _parseTimeString(String timeString)

FILE: lib/core/subtitle/subtitle_loader.dart
  class SubtitleLoader (line 11) | class SubtitleLoader {
    method findSubtitleFile (line 15) | Child? findSubtitleFile(Child audioFile, Files files)
    method loadSubtitleContent (line 42) | Future<SubtitleList?> loadSubtitleContent(String url)
    method _parseSubtitleContent (line 73) | SubtitleList? _parseSubtitleContent(String content)

FILE: lib/core/subtitle/subtitle_service.dart
  class SubtitleService (line 10) | class SubtitleService implements ISubtitleService {
    method loadSubtitle (line 24) | Future<void> loadSubtitle(String url)
    method updatePosition (line 37) | void updatePosition(Duration position)
    method dispose (line 42) | void dispose()
    method clearSubtitle (line 50) | void clearSubtitle()

FILE: lib/core/subtitle/utils/subtitle_matcher.dart
  class SubtitleMatcher (line 3) | class SubtitleMatcher {
    method isSubtitleFile (line 8) | bool isSubtitleFile(String? fileName)
    method getPossibleSubtitleNames (line 15) | List<String> getPossibleSubtitleNames(String audioFileName)
    method findMatchingSubtitle (line 32) | Child? findMatchingSubtitle(String audioFileName, List<Child> siblings)
    method _getBaseName (line 52) | String _getBaseName(String fileName)

FILE: lib/core/theme/app_colors.dart
  class AppColors (line 4) | class AppColors {

FILE: lib/core/theme/app_theme.dart
  class AppTheme (line 5) | class AppTheme {

FILE: lib/core/theme/theme_controller.dart
  class ThemeController (line 4) | class ThemeController extends ChangeNotifier {
    method setThemeMode (line 24) | Future<void> setThemeMode(ThemeMode mode)
    method toggleThemeMode (line 35) | Future<void> toggleThemeMode()

FILE: lib/data/models/auth/auth_resp/auth_resp.dart
  class AuthResp (line 8) | @freezed

FILE: lib/data/models/auth/auth_resp/auth_resp.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$AuthRespFromJson (line 17) | AuthResp _$AuthRespFromJson(Map<String, dynamic> json)
  function toJson (line 27) | Map<String, dynamic> toJson()
  class $AuthRespCopyWith (line 37) | abstract class $AuthRespCopyWith<$Res> {
    method call (line 41) | $Res call({User? user, String? token})
  class _$AuthRespCopyWithImpl (line 47) | class _$AuthRespCopyWithImpl<$Res, $Val extends AuthResp>
    method call (line 60) | $Res call({
  class _$$AuthRespImplCopyWith (line 92) | abstract class _$$AuthRespImplCopyWith<$Res>
    method call (line 99) | $Res call({User? user, String? token})
  class __$$AuthRespImplCopyWithImpl (line 106) | class __$$AuthRespImplCopyWithImpl<$Res>
    method call (line 117) | $Res call({
  class _$AuthRespImpl (line 135) | @JsonSerializable()
    method toString (line 148) | String toString()
    method toJson (line 174) | Map<String, dynamic> toJson()
  class _AuthResp (line 181) | abstract class _AuthResp implements AuthResp {

FILE: lib/data/models/auth/auth_resp/auth_resp.g.dart
  function _$$AuthRespImplFromJson (line 9) | _$AuthRespImpl _$$AuthRespImplFromJson(Map<String, dynamic> json)
  function _$$AuthRespImplToJson (line 17) | Map<String, dynamic> _$$AuthRespImplToJson(_$AuthRespImpl instance)

FILE: lib/data/models/auth/auth_resp/user.dart
  class User (line 6) | @freezed

FILE: lib/data/models/auth/auth_resp/user.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$UserFromJson (line 17) | User _$UserFromJson(Map<String, dynamic> json)
  function toJson (line 30) | Map<String, dynamic> toJson()
  class $UserCopyWith (line 39) | abstract class $UserCopyWith<$Res> {
    method call (line 43) | $Res call(
  class _$UserCopyWithImpl (line 52) | class _$UserCopyWithImpl<$Res, $Val extends User>
    method call (line 65) | $Res call({
  class _$$UserImplCopyWith (line 98) | abstract class _$$UserImplCopyWith<$Res> implements $UserCopyWith<$Res> {
    method call (line 104) | $Res call(
  class __$$UserImplCopyWithImpl (line 113) | class __$$UserImplCopyWithImpl<$Res>
    method call (line 123) | $Res call({
  class _$UserImpl (line 156) | @JsonSerializable()
    method toString (line 176) | String toString()
    method toJson (line 208) | Map<String, dynamic> toJson()
  class _User (line 215) | abstract class _User implements User {

FILE: lib/data/models/auth/auth_resp/user.g.dart
  function _$$UserImplFromJson (line 9) | _$UserImpl _$$UserImplFromJson(Map<String, dynamic> json)
  function _$$UserImplToJson (line 17) | Map<String, dynamic> _$$UserImplToJson(_$UserImpl instance)

FILE: lib/data/models/files/child.dart
  class Child (line 8) | @freezed

FILE: lib/data/models/files/child.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$ChildFromJson (line 17) | Child _$ChildFromJson(Map<String, dynamic> json)
  function toJson (line 34) | Map<String, dynamic> toJson()
  class $ChildCopyWith (line 43) | abstract class $ChildCopyWith<$Res> {
    method call (line 47) | $Res call(
  class _$ChildCopyWithImpl (line 62) | class _$ChildCopyWithImpl<$Res, $Val extends Child>
    method call (line 75) | $Res call({
  class _$$ChildImplCopyWith (line 142) | abstract class _$$ChildImplCopyWith<$Res> implements $ChildCopyWith<$Res> {
    method call (line 148) | $Res call(
  class __$$ChildImplCopyWithImpl (line 164) | class __$$ChildImplCopyWithImpl<$Res>
    method call (line 175) | $Res call({
  class _$ChildImpl (line 228) | @JsonSerializable()
    method toString (line 273) | String toString()
    method toJson (line 319) | Map<String, dynamic> toJson()
  class _Child (line 326) | abstract class _Child implements Child {

FILE: lib/data/models/files/child.g.dart
  function _$$ChildImplFromJson (line 9) | _$ChildImpl _$$ChildImplFromJson(Map<String, dynamic> json)
  function _$$ChildImplToJson (line 25) | Map<String, dynamic> _$$ChildImplToJson(_$ChildImpl instance)

FILE: lib/data/models/files/files.dart
  class Files (line 8) | @freezed

FILE: lib/data/models/files/files.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$FilesFromJson (line 17) | Files _$FilesFromJson(Map<String, dynamic> json)
  function toJson (line 28) | Map<String, dynamic> toJson()
  class $FilesCopyWith (line 37) | abstract class $FilesCopyWith<$Res> {
    method call (line 41) | $Res call({String? type, String? title, List<Child>? children})
  class _$FilesCopyWithImpl (line 45) | class _$FilesCopyWithImpl<$Res, $Val extends Files>
    method call (line 58) | $Res call({
  class _$$FilesImplCopyWith (line 81) | abstract class _$$FilesImplCopyWith<$Res> implements $FilesCopyWith<$Res> {
    method call (line 87) | $Res call({String? type, String? title, List<Child>? children})
  class __$$FilesImplCopyWithImpl (line 91) | class __$$FilesImplCopyWithImpl<$Res>
    method call (line 102) | $Res call({
  class _$FilesImpl (line 125) | @JsonSerializable()
    method toString (line 148) | String toString()
    method toJson (line 176) | Map<String, dynamic> toJson()
  class _Files (line 183) | abstract class _Files implements Files {

FILE: lib/data/models/files/files.g.dart
  function _$$FilesImplFromJson (line 9) | _$FilesImpl _$$FilesImplFromJson(Map<String, dynamic> json)
  function _$$FilesImplToJson (line 17) | Map<String, dynamic> _$$FilesImplToJson(_$FilesImpl instance)

FILE: lib/data/models/files/work.dart
  class Work (line 6) | @freezed

FILE: lib/data/models/files/work.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$WorkFromJson (line 17) | Work _$WorkFromJson(Map<String, dynamic> json)
  function toJson (line 30) | Map<String, dynamic> toJson()
  class $WorkCopyWith (line 39) | abstract class $WorkCopyWith<$Res> {
    method call (line 43) | $Res call(
  class _$WorkCopyWithImpl (line 50) | class _$WorkCopyWithImpl<$Res, $Val extends Work>
    method call (line 63) | $Res call({
  class _$$WorkImplCopyWith (line 86) | abstract class _$$WorkImplCopyWith<$Res> implements $WorkCopyWith<$Res> {
    method call (line 92) | $Res call(
  class __$$WorkImplCopyWithImpl (line 99) | class __$$WorkImplCopyWithImpl<$Res>
    method call (line 109) | $Res call({
  class _$WorkImpl (line 132) | @JsonSerializable()
    method toString (line 152) | String toString()
    method toJson (line 181) | Map<String, dynamic> toJson()
  class _Work (line 188) | abstract class _Work implements Work {

FILE: lib/data/models/files/work.g.dart
  function _$$WorkImplFromJson (line 9) | _$WorkImpl _$$WorkImplFromJson(Map<String, dynamic> json)
  function _$$WorkImplToJson (line 15) | Map<String, dynamic> _$$WorkImplToJson(_$WorkImpl instance)

FILE: lib/data/models/mark_lists/mark_lists.dart
  class MarkLists (line 9) | @freezed

FILE: lib/data/models/mark_lists/mark_lists.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$MarkListsFromJson (line 17) | MarkLists _$MarkListsFromJson(Map<String, dynamic> json)
  function toJson (line 27) | Map<String, dynamic> toJson()
  class $MarkListsCopyWith (line 37) | abstract class $MarkListsCopyWith<$Res> {
    method call (line 41) | $Res call({List<Playlist>? playlists, Pagination? pagination})
  class _$MarkListsCopyWithImpl (line 47) | class _$MarkListsCopyWithImpl<$Res, $Val extends MarkLists>
    method call (line 60) | $Res call({
  class _$$MarkListsImplCopyWith (line 92) | abstract class _$$MarkListsImplCopyWith<$Res>
    method call (line 99) | $Res call({List<Playlist>? playlists, Pagination? pagination})
  class __$$MarkListsImplCopyWithImpl (line 106) | class __$$MarkListsImplCopyWithImpl<$Res>
    method call (line 117) | $Res call({
  class _$MarkListsImpl (line 135) | @JsonSerializable()
    method toString (line 157) | String toString()
    method toJson (line 186) | Map<String, dynamic> toJson()
  class _MarkLists (line 193) | abstract class _MarkLists implements MarkLists {

FILE: lib/data/models/mark_lists/mark_lists.g.dart
  function _$$MarkListsImplFromJson (line 9) | _$MarkListsImpl _$$MarkListsImplFromJson(Map<String, dynamic> json)
  function _$$MarkListsImplToJson (line 19) | Map<String, dynamic> _$$MarkListsImplToJson(_$MarkListsImpl instance)

FILE: lib/data/models/mark_lists/pagination.dart
  class Pagination (line 6) | @freezed

FILE: lib/data/models/mark_lists/pagination.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$PaginationFromJson (line 17) | Pagination _$PaginationFromJson(Map<String, dynamic> json)
  function toJson (line 28) | Map<String, dynamic> toJson()
  class $PaginationCopyWith (line 38) | abstract class $PaginationCopyWith<$Res> {
    method call (line 43) | $Res call({int? page, int? pageSize, int? totalCount})
  class _$PaginationCopyWithImpl (line 47) | class _$PaginationCopyWithImpl<$Res, $Val extends Pagination>
    method call (line 60) | $Res call({
  class _$$PaginationImplCopyWith (line 83) | abstract class _$$PaginationImplCopyWith<$Res>
    method call (line 90) | $Res call({int? page, int? pageSize, int? totalCount})
  class __$$PaginationImplCopyWithImpl (line 94) | class __$$PaginationImplCopyWithImpl<$Res>
    method call (line 105) | $Res call({
  class _$PaginationImpl (line 128) | @JsonSerializable()
    method toString (line 143) | String toString()
    method toJson (line 172) | Map<String, dynamic> toJson()
  class _Pagination (line 179) | abstract class _Pagination implements Pagination {

FILE: lib/data/models/mark_lists/pagination.g.dart
  function _$$PaginationImplFromJson (line 9) | _$PaginationImpl _$$PaginationImplFromJson(Map<String, dynamic> json)
  function _$$PaginationImplToJson (line 16) | Map<String, dynamic> _$$PaginationImplToJson(_$PaginationImpl instance)

FILE: lib/data/models/mark_lists/playlist.dart
  class Playlist (line 6) | @freezed

FILE: lib/data/models/mark_lists/playlist.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$PlaylistFromJson (line 17) | Playlist _$PlaylistFromJson(Map<String, dynamic> json)
  function toJson (line 43) | Map<String, dynamic> toJson()
  class $PlaylistCopyWith (line 53) | abstract class $PlaylistCopyWith<$Res> {
    method call (line 57) | $Res call(
  class _$PlaylistCopyWithImpl (line 73) | class _$PlaylistCopyWithImpl<$Res, $Val extends Playlist>
    method call (line 86) | $Res call({
  class _$$PlaylistImplCopyWith (line 154) | abstract class _$$PlaylistImplCopyWith<$Res>
    method call (line 161) | $Res call(
  class __$$PlaylistImplCopyWithImpl (line 177) | class __$$PlaylistImplCopyWithImpl<$Res>
    method call (line 188) | $Res call({
  class _$PlaylistImpl (line 256) | @JsonSerializable()
    method toString (line 307) | String toString()
    method toJson (line 364) | Map<String, dynamic> toJson()
  class _Playlist (line 371) | abstract class _Playlist implements Playlist {

FILE: lib/data/models/mark_lists/playlist.g.dart
  function _$$PlaylistImplFromJson (line 9) | _$PlaylistImpl _$$PlaylistImplFromJson(Map<String, dynamic> json)
  function _$$PlaylistImplToJson (line 25) | Map<String, dynamic> _$$PlaylistImplToJson(_$PlaylistImpl instance)

FILE: lib/data/models/mark_status.dart
  type MarkStatus (line 1) | enum MarkStatus {

FILE: lib/data/models/my_lists/my_playlists/my_playlists.dart
  class MyPlaylists (line 9) | @freezed

FILE: lib/data/models/my_lists/my_playlists/my_playlists.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$MyPlaylistsFromJson (line 17) | MyPlaylists _$MyPlaylistsFromJson(Map<String, dynamic> json)
  function toJson (line 27) | Map<String, dynamic> toJson()
  class $MyPlaylistsCopyWith (line 37) | abstract class $MyPlaylistsCopyWith<$Res> {
    method call (line 42) | $Res call({List<Playlist>? playlists, Pagination? pagination})
  class _$MyPlaylistsCopyWithImpl (line 48) | class _$MyPlaylistsCopyWithImpl<$Res, $Val extends MyPlaylists>
    method call (line 61) | $Res call({
  class _$$MyPlaylistsImplCopyWith (line 93) | abstract class _$$MyPlaylistsImplCopyWith<$Res>
    method call (line 100) | $Res call({List<Playlist>? playlists, Pagination? pagination})
  class __$$MyPlaylistsImplCopyWithImpl (line 107) | class __$$MyPlaylistsImplCopyWithImpl<$Res>
    method call (line 118) | $Res call({
  class _$MyPlaylistsImpl (line 136) | @JsonSerializable()
    method toString (line 158) | String toString()
    method toJson (line 187) | Map<String, dynamic> toJson()
  class _MyPlaylists (line 194) | abstract class _MyPlaylists implements MyPlaylists {

FILE: lib/data/models/my_lists/my_playlists/my_playlists.g.dart
  function _$$MyPlaylistsImplFromJson (line 9) | _$MyPlaylistsImpl _$$MyPlaylistsImplFromJson(Map<String, dynamic> json)
  function _$$MyPlaylistsImplToJson (line 19) | Map<String, dynamic> _$$MyPlaylistsImplToJson(_$MyPlaylistsImpl instance)

FILE: lib/data/models/my_lists/my_playlists/pagination.dart
  class Pagination (line 6) | @freezed

FILE: lib/data/models/my_lists/my_playlists/pagination.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$PaginationFromJson (line 17) | Pagination _$PaginationFromJson(Map<String, dynamic> json)
  function toJson (line 28) | Map<String, dynamic> toJson()
  class $PaginationCopyWith (line 38) | abstract class $PaginationCopyWith<$Res> {
    method call (line 43) | $Res call({int? page, int? pageSize, int? totalCount})
  class _$PaginationCopyWithImpl (line 47) | class _$PaginationCopyWithImpl<$Res, $Val extends Pagination>
    method call (line 60) | $Res call({
  class _$$PaginationImplCopyWith (line 83) | abstract class _$$PaginationImplCopyWith<$Res>
    method call (line 90) | $Res call({int? page, int? pageSize, int? totalCount})
  class __$$PaginationImplCopyWithImpl (line 94) | class __$$PaginationImplCopyWithImpl<$Res>
    method call (line 105) | $Res call({
  class _$PaginationImpl (line 128) | @JsonSerializable()
    method toString (line 143) | String toString()
    method toJson (line 172) | Map<String, dynamic> toJson()
  class _Pagination (line 179) | abstract class _Pagination implements Pagination {

FILE: lib/data/models/my_lists/my_playlists/pagination.g.dart
  function _$$PaginationImplFromJson (line 9) | _$PaginationImpl _$$PaginationImplFromJson(Map<String, dynamic> json)
  function _$$PaginationImplToJson (line 16) | Map<String, dynamic> _$$PaginationImplToJson(_$PaginationImpl instance)

FILE: lib/data/models/my_lists/my_playlists/playlist.dart
  class Playlist (line 6) | @freezed

FILE: lib/data/models/my_lists/my_playlists/playlist.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$PlaylistFromJson (line 17) | Playlist _$PlaylistFromJson(Map<String, dynamic> json)
  function toJson (line 43) | Map<String, dynamic> toJson()
  class $PlaylistCopyWith (line 53) | abstract class $PlaylistCopyWith<$Res> {
    method call (line 57) | $Res call(
  class _$PlaylistCopyWithImpl (line 73) | class _$PlaylistCopyWithImpl<$Res, $Val extends Playlist>
    method call (line 86) | $Res call({
  class _$$PlaylistImplCopyWith (line 154) | abstract class _$$PlaylistImplCopyWith<$Res>
    method call (line 161) | $Res call(
  class __$$PlaylistImplCopyWithImpl (line 177) | class __$$PlaylistImplCopyWithImpl<$Res>
    method call (line 188) | $Res call({
  class _$PlaylistImpl (line 256) | @JsonSerializable()
    method toString (line 307) | String toString()
    method toJson (line 364) | Map<String, dynamic> toJson()
  class _Playlist (line 371) | abstract class _Playlist implements Playlist {

FILE: lib/data/models/my_lists/my_playlists/playlist.g.dart
  function _$$PlaylistImplFromJson (line 9) | _$PlaylistImpl _$$PlaylistImplFromJson(Map<String, dynamic> json)
  function _$$PlaylistImplToJson (line 25) | Map<String, dynamic> _$$PlaylistImplToJson(_$PlaylistImpl instance)

FILE: lib/data/models/playback/playback_state.dart
  class PlaybackState (line 10) | @freezed

FILE: lib/data/models/playback/playback_state.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$PlaybackStateFromJson (line 17) | PlaybackState _$PlaybackStateFromJson(Map<String, dynamic> json)
  function toJson (line 33) | Map<String, dynamic> toJson()
  class $PlaybackStateCopyWith (line 43) | abstract class $PlaybackStateCopyWith<$Res> {
    method call (line 48) | $Res call(
  class _$PlaybackStateCopyWithImpl (line 64) | class _$PlaybackStateCopyWithImpl<$Res, $Val extends PlaybackState>
    method call (line 77) | $Res call({
  class _$$PlaybackStateImplCopyWith (line 155) | abstract class _$$PlaybackStateImplCopyWith<$Res>
    method call (line 162) | $Res call(
  class __$$PlaybackStateImplCopyWithImpl (line 181) | class __$$PlaybackStateImplCopyWithImpl<$Res>
    method call (line 192) | $Res call({
  class _$PlaybackStateImpl (line 240) | @JsonSerializable()
    method toString (line 281) | String toString()
    method toJson (line 327) | Map<String, dynamic> toJson()
  class _PlaybackState (line 334) | abstract class _PlaybackState implements PlaybackState {

FILE: lib/data/models/playback/playback_state.g.dart
  function _$$PlaybackStateImplFromJson (line 9) | _$PlaybackStateImpl _$$PlaybackStateImplFromJson(Map<String, dynamic> json)
  function _$$PlaybackStateImplToJson (line 23) | Map<String, dynamic> _$$PlaybackStateImplToJson(_$PlaybackStateImpl inst...

FILE: lib/data/models/playlists_with_exist_statu/pagination.dart
  class Pagination (line 6) | @freezed

FILE: lib/data/models/playlists_with_exist_statu/pagination.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$PaginationFromJson (line 17) | Pagination _$PaginationFromJson(Map<String, dynamic> json)
  function toJson (line 28) | Map<String, dynamic> toJson()
  class $PaginationCopyWith (line 38) | abstract class $PaginationCopyWith<$Res> {
    method call (line 43) | $Res call({int? page, int? pageSize, int? totalCount})
  class _$PaginationCopyWithImpl (line 47) | class _$PaginationCopyWithImpl<$Res, $Val extends Pagination>
    method call (line 60) | $Res call({
  class _$$PaginationImplCopyWith (line 83) | abstract class _$$PaginationImplCopyWith<$Res>
    method call (line 90) | $Res call({int? page, int? pageSize, int? totalCount})
  class __$$PaginationImplCopyWithImpl (line 94) | class __$$PaginationImplCopyWithImpl<$Res>
    method call (line 105) | $Res call({
  class _$PaginationImpl (line 128) | @JsonSerializable()
    method toString (line 143) | String toString()
    method toJson (line 172) | Map<String, dynamic> toJson()
  class _Pagination (line 179) | abstract class _Pagination implements Pagination {

FILE: lib/data/models/playlists_with_exist_statu/pagination.g.dart
  function _$$PaginationImplFromJson (line 9) | _$PaginationImpl _$$PaginationImplFromJson(Map<String, dynamic> json)
  function _$$PaginationImplToJson (line 16) | Map<String, dynamic> _$$PaginationImplToJson(_$PaginationImpl instance)

FILE: lib/data/models/playlists_with_exist_statu/playlist.dart
  class Playlist (line 6) | @freezed

FILE: lib/data/models/playlists_with_exist_statu/playlist.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$PlaylistFromJson (line 17) | Playlist _$PlaylistFromJson(Map<String, dynamic> json)
  function toJson (line 41) | Map<String, dynamic> toJson()
  class $PlaylistCopyWith (line 51) | abstract class $PlaylistCopyWith<$Res> {
    method call (line 55) | $Res call(
  class _$PlaylistCopyWithImpl (line 70) | class _$PlaylistCopyWithImpl<$Res, $Val extends Playlist>
    method call (line 83) | $Res call({
  class _$$PlaylistImplCopyWith (line 146) | abstract class _$$PlaylistImplCopyWith<$Res>
    method call (line 153) | $Res call(
  class __$$PlaylistImplCopyWithImpl (line 168) | class __$$PlaylistImplCopyWithImpl<$Res>
    method call (line 179) | $Res call({
  class _$PlaylistImpl (line 242) | @JsonSerializable()
    method toString (line 289) | String toString()
    method toJson (line 342) | Map<String, dynamic> toJson()
  class _Playlist (line 349) | abstract class _Playlist implements Playlist {

FILE: lib/data/models/playlists_with_exist_statu/playlist.g.dart
  function _$$PlaylistImplFromJson (line 9) | _$PlaylistImpl _$$PlaylistImplFromJson(Map<String, dynamic> json)
  function _$$PlaylistImplToJson (line 24) | Map<String, dynamic> _$$PlaylistImplToJson(_$PlaylistImpl instance)

FILE: lib/data/models/playlists_with_exist_statu/playlists_with_exist_statu.dart
  class PlaylistsWithExistStatu (line 9) | @freezed

FILE: lib/data/models/playlists_with_exist_statu/playlists_with_exist_statu.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$PlaylistsWithExistStatuFromJson (line 17) | PlaylistsWithExistStatu _$PlaylistsWithExistStatuFromJson(
  function toJson (line 28) | Map<String, dynamic> toJson()
  class $PlaylistsWithExistStatuCopyWith (line 38) | abstract class $PlaylistsWithExistStatuCopyWith<$Res> {
    method call (line 43) | $Res call({List<Playlist>? playlists, Pagination? pagination})
  class _$PlaylistsWithExistStatuCopyWithImpl (line 49) | class _$PlaylistsWithExistStatuCopyWithImpl<$Res,
    method call (line 63) | $Res call({
  class _$$PlaylistsWithExistStatuImplCopyWith (line 95) | abstract class _$$PlaylistsWithExistStatuImplCopyWith<$Res>
    method call (line 103) | $Res call({List<Playlist>? playlists, Pagination? pagination})
  class __$$PlaylistsWithExistStatuImplCopyWithImpl (line 110) | class __$$PlaylistsWithExistStatuImplCopyWithImpl<$Res>
    method call (line 123) | $Res call({
  class _$PlaylistsWithExistStatuImpl (line 141) | @JsonSerializable()
    method toString (line 164) | String toString()
    method toJson (line 194) | Map<String, dynamic> toJson()
  class _PlaylistsWithExistStatu (line 201) | abstract class _PlaylistsWithExistStatu implements PlaylistsWithExistSta...

FILE: lib/data/models/playlists_with_exist_statu/playlists_with_exist_statu.g.dart
  function _$$PlaylistsWithExistStatuImplFromJson (line 9) | _$PlaylistsWithExistStatuImpl _$$PlaylistsWithExistStatuImplFromJson(
  function _$$PlaylistsWithExistStatuImplToJson (line 20) | Map<String, dynamic> _$$PlaylistsWithExistStatuImplToJson(

FILE: lib/data/models/works/circle.dart
  class Circle (line 6) | @freezed

FILE: lib/data/models/works/circle.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$CircleFromJson (line 17) | Circle _$CircleFromJson(Map<String, dynamic> json)
  function toJson (line 31) | Map<String, dynamic> toJson()
  class $CircleCopyWith (line 40) | abstract class $CircleCopyWith<$Res> {
    method call (line 44) | $Res call(
  class _$CircleCopyWithImpl (line 52) | class _$CircleCopyWithImpl<$Res, $Val extends Circle>
    method call (line 65) | $Res call({
  class _$$CircleImplCopyWith (line 93) | abstract class _$$CircleImplCopyWith<$Res> implements $CircleCopyWith<$R...
    method call (line 99) | $Res call(
  class __$$CircleImplCopyWithImpl (line 107) | class __$$CircleImplCopyWithImpl<$Res>
    method call (line 118) | $Res call({
  class _$CircleImpl (line 146) | @JsonSerializable()
    method toString (line 169) | String toString()
    method toJson (line 199) | Map<String, dynamic> toJson()
  class _Circle (line 206) | abstract class _Circle implements Circle {

FILE: lib/data/models/works/circle.g.dart
  function _$$CircleImplFromJson (line 9) | _$CircleImpl _$$CircleImplFromJson(Map<String, dynamic> json)
  function _$$CircleImplToJson (line 16) | Map<String, dynamic> _$$CircleImplToJson(_$CircleImpl instance)

FILE: lib/data/models/works/en_us.dart
  class EnUs (line 6) | @freezed

FILE: lib/data/models/works/en_us.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$EnUsFromJson (line 17) | EnUs _$EnUsFromJson(Map<String, dynamic> json)
  function toJson (line 27) | Map<String, dynamic> toJson()
  class $EnUsCopyWith (line 36) | abstract class $EnUsCopyWith<$Res> {
    method call (line 40) | $Res call({String? name, List<dynamic>? history})
  class _$EnUsCopyWithImpl (line 44) | class _$EnUsCopyWithImpl<$Res, $Val extends EnUs>
    method call (line 57) | $Res call({
  class _$$EnUsImplCopyWith (line 75) | abstract class _$$EnUsImplCopyWith<$Res> implements $EnUsCopyWith<$Res> {
    method call (line 81) | $Res call({String? name, List<dynamic>? history})
  class __$$EnUsImplCopyWithImpl (line 85) | class __$$EnUsImplCopyWithImpl<$Res>
    method call (line 95) | $Res call({
  class _$EnUsImpl (line 113) | @JsonSerializable()
    method toString (line 133) | String toString()
    method toJson (line 160) | Map<String, dynamic> toJson()
  class _EnUs (line 167) | abstract class _EnUs implements EnUs {

FILE: lib/data/models/works/en_us.g.dart
  function _$$EnUsImplFromJson (line 9) | _$EnUsImpl _$$EnUsImplFromJson(Map<String, dynamic> json)
  function _$$EnUsImplToJson (line 14) | Map<String, dynamic> _$$EnUsImplToJson(_$EnUsImpl instance)

FILE: lib/data/models/works/i18n.dart
  class I18n (line 10) | @freezed

FILE: lib/data/models/works/i18n.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$I18nFromJson (line 17) | I18n _$I18nFromJson(Map<String, dynamic> json)
  function toJson (line 31) | Map<String, dynamic> toJson()
  class $I18nCopyWith (line 40) | abstract class $I18nCopyWith<$Res> {
    method call (line 44) | $Res call(
  class _$I18nCopyWithImpl (line 55) | class _$I18nCopyWithImpl<$Res, $Val extends I18n>
    method call (line 68) | $Res call({
  class _$$I18nImplCopyWith (line 133) | abstract class _$$I18nImplCopyWith<$Res> implements $I18nCopyWith<$Res> {
    method call (line 139) | $Res call(
  class __$$I18nImplCopyWithImpl (line 153) | class __$$I18nImplCopyWithImpl<$Res>
    method call (line 163) | $Res call({
  class _$I18nImpl (line 186) | @JsonSerializable()
    method toString (line 207) | String toString()
    method toJson (line 234) | Map<String, dynamic> toJson()
  class _I18n (line 241) | abstract class _I18n implements I18n {

FILE: lib/data/models/works/i18n.g.dart
  function _$$I18nImplFromJson (line 9) | _$I18nImpl _$$I18nImplFromJson(Map<String, dynamic> json)
  function _$$I18nImplToJson (line 21) | Map<String, dynamic> _$$I18nImplToJson(_$I18nImpl instance)

FILE: lib/data/models/works/ja_jp.dart
  class JaJp (line 6) | @freezed

FILE: lib/data/models/works/ja_jp.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$JaJpFromJson (line 17) | JaJp _$JaJpFromJson(Map<String, dynamic> json)
  function toJson (line 26) | Map<String, dynamic> toJson()
  class $JaJpCopyWith (line 35) | abstract class $JaJpCopyWith<$Res> {
    method call (line 39) | $Res call({String? name})
  class _$JaJpCopyWithImpl (line 43) | class _$JaJpCopyWithImpl<$Res, $Val extends JaJp>
    method call (line 56) | $Res call({
  class _$$JaJpImplCopyWith (line 69) | abstract class _$$JaJpImplCopyWith<$Res> implements $JaJpCopyWith<$Res> {
    method call (line 75) | $Res call({String? name})
  class __$$JaJpImplCopyWithImpl (line 79) | class __$$JaJpImplCopyWithImpl<$Res>
    method call (line 89) | $Res call({
  class _$JaJpImpl (line 102) | @JsonSerializable()
    method toString (line 113) | String toString()
    method toJson (line 138) | Map<String, dynamic> toJson()
  class _JaJp (line 145) | abstract class _JaJp implements JaJp {

FILE: lib/data/models/works/ja_jp.g.dart
  function _$$JaJpImplFromJson (line 9) | _$JaJpImpl _$$JaJpImplFromJson(Map<String, dynamic> json)
  function _$$JaJpImplToJson (line 13) | Map<String, dynamic> _$$JaJpImplToJson(_$JaJpImpl instance)

FILE: lib/data/models/works/language_edition.dart
  class LanguageEdition (line 6) | @freezed

FILE: lib/data/models/works/language_edition.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$LanguageEditionFromJson (line 17) | LanguageEdition _$LanguageEditionFromJson(Map<String, dynamic> json)
  function toJson (line 34) | Map<String, dynamic> toJson()
  class $LanguageEditionCopyWith (line 44) | abstract class $LanguageEditionCopyWith<$Res> {
    method call (line 49) | $Res call(
  class _$LanguageEditionCopyWithImpl (line 59) | class _$LanguageEditionCopyWithImpl<$Res, $Val extends LanguageEdition>
    method call (line 72) | $Res call({
  class _$$LanguageEditionImplCopyWith (line 110) | abstract class _$$LanguageEditionImplCopyWith<$Res>
    method call (line 117) | $Res call(
  class __$$LanguageEditionImplCopyWithImpl (line 127) | class __$$LanguageEditionImplCopyWithImpl<$Res>
    method call (line 138) | $Res call({
  class _$LanguageEditionImpl (line 176) | @JsonSerializable()
    method toString (line 206) | String toString()
    method toJson (line 241) | Map<String, dynamic> toJson()
  class _LanguageEdition (line 248) | abstract class _LanguageEdition implements LanguageEdition {

FILE: lib/data/models/works/language_edition.g.dart
  function _$$LanguageEditionImplFromJson (line 9) | _$LanguageEditionImpl _$$LanguageEditionImplFromJson(
  function _$$LanguageEditionImplToJson (line 20) | Map<String, dynamic> _$$LanguageEditionImplToJson(

FILE: lib/data/models/works/other_language_editions_in_db.dart
  class OtherLanguageEditionsInDb (line 6) | @freezed

FILE: lib/data/models/works/other_language_editions_in_db.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$OtherLanguageEditionsInDbFromJson (line 17) | OtherLanguageEditionsInDb _$OtherLanguageEditionsInDbFromJson(
  function toJson (line 35) | Map<String, dynamic> toJson()
  class $OtherLanguageEditionsInDbCopyWith (line 45) | abstract class $OtherLanguageEditionsInDbCopyWith<$Res> {
    method call (line 50) | $Res call(
  class _$OtherLanguageEditionsInDbCopyWithImpl (line 60) | class _$OtherLanguageEditionsInDbCopyWithImpl<$Res,
    method call (line 74) | $Res call({
  class _$$OtherLanguageEditionsInDbImplCopyWith (line 112) | abstract class _$$OtherLanguageEditionsInDbImplCopyWith<$Res>
    method call (line 120) | $Res call(
  class __$$OtherLanguageEditionsInDbImplCopyWithImpl (line 130) | class __$$OtherLanguageEditionsInDbImplCopyWithImpl<$Res>
    method call (line 143) | $Res call({
  class _$OtherLanguageEditionsInDbImpl (line 181) | @JsonSerializable()
    method toString (line 211) | String toString()
    method toJson (line 246) | Map<String, dynamic> toJson()
  class _OtherLanguageEditionsInDb (line 253) | abstract class _OtherLanguageEditionsInDb implements OtherLanguageEditio...

FILE: lib/data/models/works/other_language_editions_in_db.g.dart
  function _$$OtherLanguageEditionsInDbImplFromJson (line 9) | _$OtherLanguageEditionsInDbImpl _$$OtherLanguageEditionsInDbImplFromJson(
  function _$$OtherLanguageEditionsInDbImplToJson (line 20) | Map<String, dynamic> _$$OtherLanguageEditionsInDbImplToJson(

FILE: lib/data/models/works/pagination.dart
  class Pagination (line 6) | @freezed

FILE: lib/data/models/works/pagination.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$PaginationFromJson (line 17) | Pagination _$PaginationFromJson(Map<String, dynamic> json)
  function toJson (line 28) | Map<String, dynamic> toJson()
  class $PaginationCopyWith (line 38) | abstract class $PaginationCopyWith<$Res> {
    method call (line 43) | $Res call({int? currentPage, int? pageSize, int? totalCount})
  class _$PaginationCopyWithImpl (line 47) | class _$PaginationCopyWithImpl<$Res, $Val extends Pagination>
    method call (line 60) | $Res call({
  class _$$PaginationImplCopyWith (line 83) | abstract class _$$PaginationImplCopyWith<$Res>
    method call (line 90) | $Res call({int? currentPage, int? pageSize, int? totalCount})
  class __$$PaginationImplCopyWithImpl (line 94) | class __$$PaginationImplCopyWithImpl<$Res>
    method call (line 105) | $Res call({
  class _$PaginationImpl (line 128) | @JsonSerializable()
    method toString (line 143) | String toString()
    method toJson (line 174) | Map<String, dynamic> toJson()
  class _Pagination (line 181) | abstract class _Pagination implements Pagination {

FILE: lib/data/models/works/pagination.g.dart
  function _$$PaginationImplFromJson (line 9) | _$PaginationImpl _$$PaginationImplFromJson(Map<String, dynamic> json)
  function _$$PaginationImplToJson (line 16) | Map<String, dynamic> _$$PaginationImplToJson(_$PaginationImpl instance)

FILE: lib/data/models/works/tag.dart
  class Tag (line 8) | @freezed

FILE: lib/data/models/works/tag.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$TagFromJson (line 17) | Tag _$TagFromJson(Map<String, dynamic> json)
  function toJson (line 28) | Map<String, dynamic> toJson()
  class $TagCopyWith (line 37) | abstract class $TagCopyWith<$Res> {
    method call (line 41) | $Res call({int? id, I18n? i18n, String? name})
  class _$TagCopyWithImpl (line 47) | class _$TagCopyWithImpl<$Res, $Val extends Tag> implements $TagCopyWith<...
    method call (line 59) | $Res call({
  class _$$TagImplCopyWith (line 96) | abstract class _$$TagImplCopyWith<$Res> implements $TagCopyWith<$Res> {
    method call (line 101) | $Res call({int? id, I18n? i18n, String? name})
  class __$$TagImplCopyWithImpl (line 108) | class __$$TagImplCopyWithImpl<$Res> extends _$TagCopyWithImpl<$Res, _$Ta...
    method call (line 117) | $Res call({
  class _$TagImpl (line 140) | @JsonSerializable()
    method toString (line 155) | String toString()
    method toJson (line 182) | Map<String, dynamic> toJson()
  class _Tag (line 189) | abstract class _Tag implements Tag {

FILE: lib/data/models/works/tag.g.dart
  function _$$TagImplFromJson (line 9) | _$TagImpl _$$TagImplFromJson(Map<String, dynamic> json)
  function _$$TagImplToJson (line 17) | Map<String, dynamic> _$$TagImplToJson(_$TagImpl instance)

FILE: lib/data/models/works/translation_bonus_lang.dart
  class TranslationBonusLang (line 6) | @freezed

FILE: lib/data/models/works/translation_bonus_lang.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$TranslationBonusLangFromJson (line 17) | TranslationBonusLang _$TranslationBonusLangFromJson(Map<String, dynamic>...
  function toJson (line 37) | Map<String, dynamic> toJson()
  class $TranslationBonusLangCopyWith (line 47) | abstract class $TranslationBonusLangCopyWith<$Res> {
    method call (line 52) | $Res call(
  class _$TranslationBonusLangCopyWithImpl (line 64) | class _$TranslationBonusLangCopyWithImpl<$Res,
    method call (line 78) | $Res call({
  class _$$TranslationBonusLangImplCopyWith (line 121) | abstract class _$$TranslationBonusLangImplCopyWith<$Res>
    method call (line 128) | $Res call(
  class __$$TranslationBonusLangImplCopyWithImpl (line 140) | class __$$TranslationBonusLangImplCopyWithImpl<$Res>
    method call (line 151) | $Res call({
  class _$TranslationBonusLangImpl (line 194) | @JsonSerializable()
    method toString (line 230) | String toString()
    method toJson (line 270) | Map<String, dynamic> toJson()
  class _TranslationBonusLang (line 277) | abstract class _TranslationBonusLang implements TranslationBonusLang {

FILE: lib/data/models/works/translation_bonus_lang.g.dart
  function _$$TranslationBonusLangImplFromJson (line 9) | _$TranslationBonusLangImpl _$$TranslationBonusLangImplFromJson(
  function _$$TranslationBonusLangImplToJson (line 22) | Map<String, dynamic> _$$TranslationBonusLangImplToJson(

FILE: lib/data/models/works/translation_info.dart
  class TranslationInfo (line 7) | @freezed
  function _translationBonusLangsFromJson (line 33) | Map<String, TranslationBonusLang>? _translationBonusLangsFromJson(
  function _translationBonusLangsToJson (line 48) | dynamic _translationBonusLangsToJson(Map<String, TranslationBonusLang>? ...

FILE: lib/data/models/works/translation_info.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$TranslationInfoFromJson (line 17) | TranslationInfo _$TranslationInfoFromJson(Map<String, dynamic> json)
  function toJson (line 52) | Map<String, dynamic> toJson()
  class $TranslationInfoCopyWith (line 62) | abstract class $TranslationInfoCopyWith<$Res> {
    method call (line 67) | $Res call(
  class _$TranslationInfoCopyWithImpl (line 89) | class _$TranslationInfoCopyWithImpl<$Res, $Val extends TranslationInfo>
    method call (line 102) | $Res call({
  class _$$TranslationInfoImplCopyWith (line 170) | abstract class _$$TranslationInfoImplCopyWith<$Res>
    method call (line 177) | $Res call(
  class __$$TranslationInfoImplCopyWithImpl (line 199) | class __$$TranslationInfoImplCopyWithImpl<$Res>
    method call (line 210) | $Res call({
  class _$TranslationInfoImpl (line 278) | @JsonSerializable()
    method toString (line 361) | String toString()
    method toJson (line 423) | Map<String, dynamic> toJson()
  class _TranslationInfo (line 430) | abstract class _TranslationInfo implements TranslationInfo {

FILE: lib/data/models/works/translation_info.g.dart
  function _$$TranslationInfoImplFromJson (line 9) | _$TranslationInfoImpl _$$TranslationInfoImplFromJson(
  function _$$TranslationInfoImplToJson (line 28) | Map<String, dynamic> _$$TranslationInfoImplToJson(

FILE: lib/data/models/works/work.dart
  class Work (line 12) | @freezed

FILE: lib/data/models/works/work.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$WorkFromJson (line 17) | Work _$WorkFromJson(Map<String, dynamic> json)
  function toJson (line 76) | Map<String, dynamic> toJson()
  class $WorkCopyWith (line 85) | abstract class $WorkCopyWith<$Res> {
    method call (line 89) | $Res call(
  class _$WorkCopyWithImpl (line 130) | class _$WorkCopyWithImpl<$Res, $Val extends Work>
    method call (line 143) | $Res call({
  class _$$WorkImplCopyWith (line 339) | abstract class _$$WorkImplCopyWith<$Res> implements $WorkCopyWith<$Res> {
    method call (line 345) | $Res call(
  class __$$WorkImplCopyWithImpl (line 388) | class __$$WorkImplCopyWithImpl<$Res>
    method call (line 398) | $Res call({
  class _$WorkImpl (line 566) | @JsonSerializable()
    method toString (line 737) | String toString()
    method toJson (line 848) | Map<String, dynamic> toJson()
  class _Work (line 855) | abstract class _Work implements Work {

FILE: lib/data/models/works/work.g.dart
  function _$$WorkImplFromJson (line 9) | _$WorkImpl _$$WorkImplFromJson(Map<String, dynamic> json)
  function _$$WorkImplToJson (line 57) | Map<String, dynamic> _$$WorkImplToJson(_$WorkImpl instance)

FILE: lib/data/models/works/works.dart
  class Works (line 9) | @freezed

FILE: lib/data/models/works/works.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$WorksFromJson (line 17) | Works _$WorksFromJson(Map<String, dynamic> json)
  function toJson (line 27) | Map<String, dynamic> toJson()
  class $WorksCopyWith (line 36) | abstract class $WorksCopyWith<$Res> {
    method call (line 40) | $Res call({List<Work>? works, Pagination? pagination})
  class _$WorksCopyWithImpl (line 46) | class _$WorksCopyWithImpl<$Res, $Val extends Works>
    method call (line 59) | $Res call({
  class _$$WorksImplCopyWith (line 91) | abstract class _$$WorksImplCopyWith<$Res> implements $WorksCopyWith<$Res> {
    method call (line 97) | $Res call({List<Work>? works, Pagination? pagination})
  class __$$WorksImplCopyWithImpl (line 104) | class __$$WorksImplCopyWithImpl<$Res>
    method call (line 115) | $Res call({
  class _$WorksImpl (line 133) | @JsonSerializable()
    method toString (line 154) | String toString()
    method toJson (line 182) | Map<String, dynamic> toJson()
  class _Works (line 189) | abstract class _Works implements Works {

FILE: lib/data/models/works/works.g.dart
  function _$$WorksImplFromJson (line 9) | _$WorksImpl _$$WorksImplFromJson(Map<String, dynamic> json)
  function _$$WorksImplToJson (line 18) | Map<String, dynamic> _$$WorksImplToJson(_$WorksImpl instance)

FILE: lib/data/models/works/zh_cn.dart
  class ZhCn (line 6) | @freezed

FILE: lib/data/models/works/zh_cn.freezed.dart
  function _$identity (line 12) | T _$identity<T>(T value)
  function _$ZhCnFromJson (line 17) | ZhCn _$ZhCnFromJson(Map<String, dynamic> json)
  function toJson (line 27) | Map<String, dynamic> toJson()
  class $ZhCnCopyWith (line 36) | abstract class $ZhCnCopyWith<$Res> {
    method call (line 40) | $Res call({String? name, List<dynamic>? history})
  class _$ZhCnCopyWithImpl (line 44) | class _$ZhCnCopyWithImpl<$Res, $Val extends ZhCn>
    method call (line 57) | $Res call({
  class _$$ZhCnImplCopyWith (line 75) | abstract class _$$ZhCnImplCopyWith<$Res> implements $ZhCnCopyWith<$Res> {
    method call (line 81) | $Res call({String? name, List<dynamic>? history})
  class __$$ZhCnImplCopyWithImpl (line 85) | class __$$ZhCnImplCopyWithImpl<$Res>
    method call (line 95) | $Res call({
  class _$ZhCnImpl (line 113) | @JsonSerializable()
    method toString (line 133) | String toString()
    method toJson (line 160) | Map<String, dynamic> toJson()
  class _ZhCn (line 167) | abstract class _ZhCn implements ZhCn {

FILE: lib/data/models/works/zh_cn.g.dart
  function _$$ZhCnImplFromJson (line 9) | _$ZhCnImpl _$$ZhCnImplFromJson(Map<String, dynamic> json)
  function _$$ZhCnImplToJson (line 14) | Map<String, dynamic> _$$ZhCnImplToJson(_$ZhCnImpl instance)

FILE: lib/data/repositories/auth_repository.dart
  class AuthRepository (line 6) | class AuthRepository {
    method saveAuthData (line 12) | Future<void> saveAuthData(AuthResp authData)
    method getAuthData (line 23) | Future<AuthResp?> getAuthData()
    method clearAuthData (line 37) | Future<void> clearAuthData()

FILE: lib/data/services/api_service.dart
  class WorksResponse (line 14) | class WorksResponse {
  class ApiService (line 21) | class ApiService {
    method getWorkFiles (line 33) | Future<Files> getWorkFiles(String workId, {CancelToken? cancelToken})
    method getWorks (line 64) | Future<WorksResponse> getWorks({
    method searchWorks (line 110) | Future<WorksResponse> searchWorks({
    method getFavorites (line 151) | Future<WorksResponse> getFavorites({int page = 1})
    method getRecommendations (line 180) | Future<WorksResponse> getRecommendations({
    method getPopular (line 219) | Future<WorksResponse> getPopular({
    method getItemNeighbors (line 256) | Future<WorksResponse> getItemNeighbors({
    method getWorkExistStatusInPlaylists (line 307) | Future<PlaylistsWithExistStatu> getWorkExistStatusInPlaylists({
    method addWorkToPlaylist (line 336) | Future<void> addWorkToPlaylist({
    method removeWorkFromPlaylist (line 358) | Future<void> removeWorkFromPlaylist({
    method updateWorkMarkStatus (line 380) | Future<void> updateWorkMarkStatus(String workId, String status)
    method convertMarkStatusToApi (line 400) | String convertMarkStatusToApi(MarkStatus status)
    method getDefaultMarkTargetPlaylist (line 416) | Future<Playlist> getDefaultMarkTargetPlaylist()
    method getMyPlaylists (line 437) | Future<MyPlaylists> getMyPlaylists({int page = 1})
    method getPlaylistWorks (line 463) | Future<WorksResponse> getPlaylistWorks({

FILE: lib/data/services/auth_service.dart
  class AuthService (line 5) | class AuthService {
    method login (line 13) | Future<AuthResp> login(String name, String password)

FILE: lib/data/services/interceptors/auth_interceptor.dart
  class AuthInterceptor (line 6) | class AuthInterceptor extends Interceptor {
    method onRequest (line 8) | Future<void> onRequest(

FILE: lib/main.dart
  function main (line 11) | void main()
  class MyApp (line 20) | class MyApp extends StatelessWidget {
    method build (line 24) | Widget build(BuildContext context)

FILE: lib/presentation/layouts/work_layout_config.dart
  type DeviceType (line 4) | enum DeviceType {
  class WorkLayoutConfig (line 18) | class WorkLayoutConfig {
    method getColumnsCount (line 41) | int getColumnsCount(DeviceType deviceType)
    method getSpacing (line 53) | double getSpacing(DeviceType deviceType)
    method getPadding (line 65) | EdgeInsets getPadding(DeviceType deviceType)

FILE: lib/presentation/layouts/work_layout_strategy.dart
  class WorkLayoutStrategy (line 6) | class WorkLayoutStrategy {
    method _getDeviceType (line 10) | DeviceType _getDeviceType(BuildContext context)
    method getColumnsCount (line 15) | int getColumnsCount(BuildContext context)
    method getRowSpacing (line 20) | double getRowSpacing(BuildContext context)
    method getColumnSpacing (line 25) | double getColumnSpacing(BuildContext context)
    method getPadding (line 30) | EdgeInsets getPadding(BuildContext context)
    method groupWorksIntoRows (line 35) | List<List<Work>> groupWorksIntoRows(List<Work> works, int columnsCount)

FILE: lib/presentation/models/filter_state.dart
  class FilterState (line 1) | class FilterState {
    method copyWith (line 14) | FilterState copyWith({
    method toJson (line 25) | Map<String, dynamic> toJson()

FILE: lib/presentation/viewmodels/auth_viewmodel.dart
  class AuthViewModel (line 7) | class AuthViewModel extends ChangeNotifier {
    method _loadSavedAuth (line 22) | Future<void> _loadSavedAuth()
    method login (line 30) | Future<void> login(String name, String password)
    method logout (line 64) | Future<void> logout()
    method loadSavedAuth (line 87) | Future<void> loadSavedAuth()

FILE: lib/presentation/viewmodels/base/paginated_works_viewmodel.dart
  class PaginatedWorksViewModel (line 7) | abstract class PaginatedWorksViewModel extends ChangeNotifier {
    method _init (line 20) | Future<void> _init()
    method onInit (line 26) | Future<void> onInit()
    method fetchPage (line 41) | Future<WorksResponse> fetchPage(int page)
    method loadPage (line 47) | Future<void> loadPage(int page)
    method refresh (line 72) | Future<void> refresh()
    method dispose (line 78) | void dispose()

FILE: lib/presentation/viewmodels/detail_viewmodel.dart
  class DetailViewModel (line 17) | class DetailViewModel extends ChangeNotifier {
    method _checkRecommendations (line 71) | Future<void> _checkRecommendations()
    method loadFiles (line 92) | Future<void> loadFiles()
    method playFile (line 119) | Future<void> playFile(Child file, BuildContext context)
    method loadPlaylists (line 149) | Future<void> loadPlaylists({int page = 1})
    method showPlaylistsDialog (line 174) | Future<void> showPlaylistsDialog(BuildContext context)
    method togglePlaylistWork (line 212) | Future<void> togglePlaylistWork(Playlist playlist)
    method updateMarkStatus (line 242) | Future<void> updateMarkStatus(MarkStatus status)
    method showMarkDialog (line 263) | void showMarkDialog(BuildContext context)
    method dispose (line 294) | void dispose()

FILE: lib/presentation/viewmodels/favorites_viewmodel.dart
  class FavoritesViewModel (line 8) | class FavoritesViewModel extends ChangeNotifier {
    method loadPage (line 28) | Future<void> loadPage(int page)
    method loadFavorites (line 52) | Future<void> loadFavorites({bool refresh = false})

FILE: lib/presentation/viewmodels/home_viewmodel.dart
  class HomeViewModel (line 9) | class HomeViewModel extends PaginatedWorksViewModel {
    method onInit (line 24) | Future<void> onInit()
    method _loadFilterState (line 29) | Future<void> _loadFilterState()
    method _loadSubtitleFilter (line 42) | Future<void> _loadSubtitleFilter()
    method _saveSubtitleFilter (line 52) | Future<void> _saveSubtitleFilter()
    method _saveFilterState (line 61) | Future<void> _saveFilterState()
    method toggleFilterPanel (line 70) | void toggleFilterPanel()
    method updateSubtitle (line 75) | void updateSubtitle(bool value)
    method updateOrderField (line 82) | void updateOrderField(String value)
    method updateSortDirection (line 94) | void updateSortDirection(bool isDescending)
    method closeFilterPanel (line 102) | void closeFilterPanel()
    method fetchPage (line 113) | Future<WorksResponse> fetchPage(int page)
    method dispose (line 123) | void dispose()

FILE: lib/presentation/viewmodels/player_viewmodel.dart
  class PlayerViewModel (line 13) | class PlayerViewModel extends ChangeNotifier {
    method _initStreams (line 39) | void _initStreams()
    method _initSubtitleStreams (line 109) | void _initSubtitleStreams()
    method playPause (line 135) | Future<void> playPause()
    method seek (line 143) | Future<void> seek(Duration position)
    method previous (line 147) | Future<void> previous()
    method next (line 151) | Future<void> next()
    method stop (line 155) | Future<void> stop()
    method dispose (line 162) | void dispose()
    method _requestInitialState (line 171) | void _requestInitialState()
    method _loadSubtitleIfAvailable (line 176) | Future<void> _loadSubtitleIfAvailable(PlaybackContext context)
    method seekToNextLyric (line 192) | Future<void> seekToNextLyric()
    method seekToPreviousLyric (line 204) | Future<void> seekToPreviousLyric()

FILE: lib/presentation/viewmodels/playlist_works_viewmodel.dart
  class PlaylistWorksViewModel (line 9) | class PlaylistWorksViewModel extends ChangeNotifier {
    method loadWorks (line 29) | Future<void> loadWorks({int page = 1})
    method refresh (line 56) | Future<void> refresh()

FILE: lib/presentation/viewmodels/playlists_viewmodel.dart
  class PlaylistsViewModel (line 10) | class PlaylistsViewModel extends ChangeNotifier {
    method loadPlaylists (line 49) | Future<void> loadPlaylists({int page = 1})
    method refresh (line 73) | Future<void> refresh()
    method selectPlaylist (line 78) | Future<void> selectPlaylist(Playlist playlist)
    method clearSelectedPlaylist (line 90) | void clearSelectedPlaylist()
    method loadPlaylistWorks (line 100) | Future<void> loadPlaylistWorks({int page = 1})
    method refreshWorks (line 128) | Future<void> refreshWorks()
    method getDisplayName (line 131) | String getDisplayName(String? name)
    method dispose (line 143) | void dispose()

FILE: lib/presentation/viewmodels/popular_viewmodel.dart
  class PopularViewModel (line 7) | class PopularViewModel extends PaginatedWorksViewModel {
    method onInit (line 17) | Future<void> onInit()
    method _loadSubtitleFilter (line 21) | Future<void> _loadSubtitleFilter()
    method _loadFilterState (line 31) | Future<void> _loadFilterState()
    method _saveFilterState (line 41) | Future<void> _saveFilterState()
    method toggleSubtitleFilter (line 53) | void toggleSubtitleFilter()
    method toggleFilterPanel (line 60) | void toggleFilterPanel()
    method closeFilterPanel (line 65) | void closeFilterPanel()
    method fetchPage (line 76) | Future<WorksResponse> fetchPage(int page)
    method loadPopular (line 84) | Future<void> loadPopular({bool refresh = false})
    method dispose (line 88) | void dispose()

FILE: lib/presentation/viewmodels/recommend_viewmodel.dart
  class RecommendViewModel (line 10) | class RecommendViewModel extends ChangeNotifier {
    method _loadFilterState (line 27) | Future<void> _loadFilterState()
    method _saveFilterState (line 40) | Future<void> _saveFilterState()
    method toggleSubtitleFilter (line 64) | void toggleSubtitleFilter()
    method toggleFilterPanel (line 71) | void toggleFilterPanel()
    method closeFilterPanel (line 76) | void closeFilterPanel()
    method loadPage (line 84) | Future<void> loadPage(int page)
    method loadRecommendations (line 120) | Future<void> loadRecommendations({bool refresh = false})
    method dispose (line 125) | void dispose()

FILE: lib/presentation/viewmodels/search_viewmodel.dart
  class SearchViewModel (line 8) | class SearchViewModel extends ChangeNotifier {
    method toggleSubtitle (line 40) | void toggleSubtitle()
    method setOrder (line 48) | void setOrder(String order, String sort)
    method search (line 58) | Future<void> search(String keyword, {int page = 1})
    method loadPage (line 90) | Future<void> loadPage(int page)
    method clear (line 96) | void clear()

FILE: lib/presentation/viewmodels/settings/cache_manager_viewmodel.dart
  class CacheManagerViewModel (line 6) | class CacheManagerViewModel extends ChangeNotifier {
    method _formatSize (line 19) | String _formatSize(int size)
    method loadCacheSize (line 30) | Future<void> loadCacheSize()
    method clearAudioCache (line 51) | Future<void> clearAudioCache()
    method clearSubtitleCache (line 69) | Future<void> clearSubtitleCache()
    method clearAllCache (line 87) | Future<void> clearAllCache()

FILE: lib/presentation/viewmodels/similar_works_viewmodel.dart
  class SimilarWorksViewModel (line 9) | class SimilarWorksViewModel extends ChangeNotifier {
    method _loadFilterState (line 38) | Future<void> _loadFilterState()
    method _saveFilterState (line 51) | Future<void> _saveFilterState()
    method toggleSubtitleFilter (line 61) | void toggleSubtitleFilter()
    method toggleFilterPanel (line 68) | void toggleFilterPanel()
    method closeFilterPanel (line 73) | void closeFilterPanel()
    method loadPage (line 81) | Future<void> loadPage(int page)
    method loadSimilarWorks (line 109) | Future<void> loadSimilarWorks({bool refresh = false})
    method dispose (line 114) | void dispose()

FILE: lib/presentation/widgets/auth/login_dialog.dart
  class LoginDialog (line 6) | class LoginDialog extends StatefulWidget {
    method createState (line 10) | State<LoginDialog> createState()
  class _LoginDialogState (line 13) | class _LoginDialogState extends State<LoginDialog> {
    method dispose (line 19) | void dispose()
    method _handleLogin (line 25) | Future<void> _handleLogin()
    method build (line 43) | Widget build(BuildContext context)

FILE: lib/screens/contents/home_content.dart
  class HomeContent (line 8) | class HomeContent extends StatefulWidget {
    method createState (line 12) | State<HomeContent> createState()
  class _HomeContentState (line 15) | class _HomeContentState extends State<HomeContent>
    method initState (line 24) | void initState()
    method dispose (line 31) | void dispose()
    method _onScroll (line 37) | void _onScroll()
    method build (line 49) | Widget build(BuildContext context)

FILE: lib/screens/contents/playlists/playlist_works_view.dart
  class PlaylistWorksView (line 9) | class PlaylistWorksView extends StatelessWidget {
    method build (line 21) | Widget build(BuildContext context)

FILE: lib/screens/contents/playlists/playlists_list_view.dart
  class PlaylistsListView (line 7) | class PlaylistsListView extends StatelessWidget {
    method build (line 16) | Widget build(BuildContext context)

FILE: lib/screens/contents/playlists_content.dart
  class PlaylistsContent (line 8) | class PlaylistsContent extends StatefulWidget {
    method createState (line 12) | State<PlaylistsContent> createState()
  class _PlaylistsContentState (line 15) | class _PlaylistsContentState extends State<PlaylistsContent> with Automa...
    method _handlePlaylistSelected (line 21) | void _handlePlaylistSelected(Playlist playlist)
    method _handleBack (line 27) | void _handleBack()
    method _onWillPop (line 33) | Future<bool> _onWillPop()
    method build (line 42) | Widget build(BuildContext context)

FILE: lib/screens/contents/popular_content.dart
  class PopularContent (line 8) | class PopularContent extends StatefulWidget {
    method createState (line 12) | State<PopularContent> createState()
  class _PopularContentState (line 15) | class _PopularContentState extends State<PopularContent> with AutomaticK...
    method initState (line 23) | void initState()
    method dispose (line 29) | void dispose()
    method _onScroll (line 34) | void _onScroll()
    method build (line 44) | Widget build(BuildContext context)

FILE: lib/screens/contents/recommend_content.dart
  class RecommendContent (line 9) | class RecommendContent extends StatefulWidget {
    method createState (line 13) | State<RecommendContent> createState()
  class _RecommendContentState (line 16) | class _RecommendContentState extends State<RecommendContent> with Automa...
    method _onScroll (line 23) | void _onScroll()
    method initState (line 33) | void initState()
    method dispose (line 43) | void dispose()
    method build (line 49) | Widget build(BuildContext context)

FILE: lib/screens/detail_screen.dart
  class DetailScreen (line 13) | class DetailScreen extends StatelessWidget {
    method build (line 24) | Widget build(BuildContext context)

FILE: lib/screens/favorites_screen.dart
  class FavoritesScreen (line 9) | class FavoritesScreen extends StatefulWidget {
    method createState (line 13) | State<FavoritesScreen> createState()
  class _FavoritesScreenState (line 16) | class _FavoritesScreenState extends State<FavoritesScreen> {
    method initState (line 22) | void initState()
    method dispose (line 29) | void dispose()
    method _onPageChanged (line 34) | void _onPageChanged(int page)
    method build (line 46) | Widget build(BuildContext context)

FILE: lib/screens/main_screen.dart
  class MainScreen (line 23) | class MainScreen extends StatefulWidget {
    method createState (line 27) | State<MainScreen> createState()
  class _MainScreenState (line 30) | class _MainScreenState extends State<MainScreen> {
    method initState (line 54) | void initState()
    method _onPageChanged (line 66) | void _onPageChanged(int index)
    method _onTabTapped (line 72) | void _onTabTapped(int index)
    method dispose (line 81) | void dispose()
    method build (line 92) | Widget build(BuildContext context)

FILE: lib/screens/player_screen.dart
  class PlayerScreen (line 13) | class PlayerScreen extends StatefulWidget {
    method createState (line 17) | State<PlayerScreen> createState()
  class _PlayerScreenState (line 20) | class _PlayerScreenState extends State<PlayerScreen> {
    method initState (line 26) | void initState()
    method _buildContent (line 31) | Widget _buildContent()
    method build (line 138) | Widget build(BuildContext context)

FILE: lib/screens/search_screen.dart
  class SearchScreen (line 9) | class SearchScreen extends StatelessWidget {
    method build (line 18) | Widget build(BuildContext context)
  class SearchScreenContent (line 26) | class SearchScreenContent extends StatefulWidget {
    method createState (line 35) | State<SearchScreenContent> createState()
  class _SearchScreenContentState (line 38) | class _SearchScreenContentState extends State<SearchScreenContent> {
    method initState (line 44) | void initState()
    method dispose (line 57) | void dispose()
    method _onSearch (line 63) | void _onSearch()
    method _onPageChanged (line 71) | void _onPageChanged(int page)
    method _getOrderText (line 83) | String _getOrderText(String order, String sort)
    method build (line 107) | Widget build(BuildContext context)

FILE: lib/screens/settings/cache_manager_screen.dart
  class CacheManagerScreen (line 5) | class CacheManagerScreen extends StatelessWidget {
    method build (line 9) | Widget build(BuildContext context)

FILE: lib/screens/similar_works_screen.dart
  class SimilarWorksScreen (line 10) | class SimilarWorksScreen extends StatefulWidget {
    method createState (line 19) | State<SimilarWorksScreen> createState()
  class _SimilarWorksScreenState (line 22) | class _SimilarWorksScreenState extends State<SimilarWorksScreen> {
    method initState (line 28) | void initState()
    method dispose (line 35) | void dispose()
    method _onScroll (line 41) | void _onScroll()
    method _scrollToTop (line 49) | void _scrollToTop()
    method build (line 60) | Widget build(BuildContext context)

FILE: lib/utils/file_size_formatter.dart
  class FileSizeFormatter (line 1) | class FileSizeFormatter {
    method format (line 2) | String format(int? size)

FILE: lib/utils/logger.dart
  class AppLogger (line 3) | class AppLogger {
    method init (line 15) | void init()
    method debug (line 19) | void debug(String message)
    method info (line 20) | void info(String message)
    method warning (line 21) | void warning(String message)
    method error (line 22) | void error(String message, [Object? error, StackTrace? stackTrace])

FILE: lib/widgets/common/tag_chip.dart
  class TagChip (line 3) | class TagChip extends StatelessWidget {
    method build (line 18) | Widget build(BuildContext context)

FILE: lib/widgets/detail/mark_selection_dialog.dart
  class MarkSelectionDialog (line 4) | class MarkSelectionDialog extends StatelessWidget {
    method build (line 17) | Widget build(BuildContext context)

FILE: lib/widgets/detail/playlist_selection_dialog.dart
  class PlaylistSelectionDialog (line 4) | class PlaylistSelectionDialog extends StatefulWidget {
    method createState (line 21) | State<PlaylistSelectionDialog> createState()
  class _PlaylistSelectionDialogState (line 24) | class _PlaylistSelectionDialogState extends State<PlaylistSelectionDialo...
    method initState (line 28) | void initState()
    method didUpdateWidget (line 34) | void didUpdateWidget(PlaylistSelectionDialog oldWidget)
    method _updateItemStates (line 41) | void _updateItemStates()
    method build (line 56) | Widget build(BuildContext context)
    method _buildContent (line 84) | Widget _buildContent()
    method _handlePlaylistTap (line 129) | Future<void> _handlePlaylistTap(_PlaylistItemState state)
    method _getDisplayName (line 176) | String _getDisplayName(String? name)
  class _PlaylistItem (line 188) | class _PlaylistItem extends StatelessWidget {
    method _getDisplayName (line 197) | String _getDisplayName(String? name)
    method build (line 209) | Widget build(BuildContext context)
  class _PlaylistItemState (line 230) | class _PlaylistItemState {

FILE: lib/widgets/detail/work_action_buttons.dart
  class WorkActionButtons (line 4) | class WorkActionButtons extends StatelessWidget {
    method build (line 27) | Widget build(BuildContext context)
  class _ActionButton (line 64) | class _ActionButton extends StatelessWidget {
    method build (line 78) | Widget build(BuildContext context)

FILE: lib/widgets/detail/work_cover.dart
  class WorkCover (line 4) | class WorkCover extends StatelessWidget {
    method build (line 22) | Widget build(BuildContext context)

FILE: lib/widgets/detail/work_file_item.dart
  class WorkFileItem (line 6) | class WorkFileItem extends StatelessWidget {
    method build (line 19) | Widget build(BuildContext context)

FILE: lib/widgets/detail/work_files_list.dart
  class WorkFilesList (line 7) | class WorkFilesList extends StatelessWidget {
    method build (line 18) | Widget build(BuildContext context)

FILE: lib/widgets/detail/work_files_skeleton.dart
  class WorkFilesSkeleton (line 4) | class WorkFilesSkeleton extends StatelessWidget {
    method _buildShimmerItem (line 7) | Widget _buildShimmerItem()
    method build (line 53) | Widget build(BuildContext context)

FILE: lib/widgets/detail/work_folder_item.dart
  class WorkFolderItem (line 7) | class WorkFolderItem extends StatelessWidget {
    method resetExpandState (line 19) | void resetExpandState()
    method _shouldExpandFolder (line 30) | bool _shouldExpandFolder(Child folder)
    method build (line 42) | Widget build(BuildContext context)

FILE: lib/widgets/detail/work_info.dart
  class WorkInfo (line 8) | class WorkInfo extends StatelessWidget {
    method _getLocalizedTagName (line 16) | String _getLocalizedTagName(Tag tag)
    method _onTagTap (line 24) | void _onTagTap(BuildContext context, Tag tag)
    method build (line 37) | Widget build(BuildContext context)

FILE: lib/widgets/detail/work_info_header.dart
  class WorkInfoHeader (line 7) | class WorkInfoHeader extends StatelessWidget {
    method _onTagTap (line 15) | void _onTagTap(BuildContext context, String keyword)
    method build (line 27) | Widget build(BuildContext context)

FILE: lib/widgets/detail/work_stats_info.dart
  class WorkStatsInfo (line 4) | class WorkStatsInfo extends StatelessWidget {
    method _formatDuration (line 12) | String _formatDuration(int? seconds)
    method build (line 26) | Widget build(BuildContext context)

FILE: lib/widgets/drawer_menu.dart
  class DrawerMenu (line 12) | class DrawerMenu extends StatelessWidget {
    method _showLoginDialog (line 15) | void _showLoginDialog(BuildContext context)
    method build (line 23) | Widget build(BuildContext context)
    method _getThemeIcon (line 139) | IconData _getThemeIcon(ThemeMode mode)
    method _getThemeText (line 150) | String _getThemeText(ThemeMode mode)

FILE: lib/widgets/filter/filter_panel.dart
  class FilterPanel (line 3) | class FilterPanel extends StatelessWidget {
    method _getOrderFieldText (line 23) | String _getOrderFieldText(String field)
    method build (line 51) | Widget build(BuildContext context)
    method _buildOrderMenuItem (line 170) | PopupMenuItem<String> _buildOrderMenuItem(String text, String value)

FILE: lib/widgets/filter/filter_with_keyword.dart
  class FilterWithKeyword (line 3) | class FilterWithKeyword extends StatelessWidget {
    method build (line 22) | Widget build(BuildContext context)

FILE: lib/widgets/lyrics/components/lyric_line.dart
  class LyricLine (line 4) | class LyricLine extends StatelessWidget {
    method build (line 19) | Widget build(BuildContext context)

FILE: lib/widgets/lyrics/components/player_lyric_view.dart
  class PlayerLyricView (line 11) | class PlayerLyricView extends StatefulWidget {
    method createState (line 22) | State<PlayerLyricView> createState()
  class _PlayerLyricViewState (line 25) | class _PlayerLyricViewState extends State<PlayerLyricView> {
    method initState (line 44) | void initState()
    method dispose (line 49) | void dispose()
    method _scrollToCurrentLyric (line 56) | void _scrollToCurrentLyric(SubtitleWithState current)
    method build (line 85) | Widget build(BuildContext context)

FILE: lib/widgets/mini_player/mini_player.dart
  class MiniPlayer (line 9) | class MiniPlayer extends StatelessWidget {
    method build (line 15) | Widget build(BuildContext context)

FILE: lib/widgets/mini_player/mini_player_controls.dart
  class MiniPlayerControls (line 5) | class MiniPlayerControls extends StatelessWidget {
    method build (line 9) | Widget build(BuildContext context)

FILE: lib/widgets/mini_player/mini_player_cover.dart
  class MiniPlayerCover (line 5) | class MiniPlayerCover extends StatelessWidget {
    method build (line 16) | Widget build(BuildContext context)
    method _buildEmptyPlaceholder (line 34) | Widget _buildEmptyPlaceholder()
    method _buildPlaceholder (line 46) | Widget _buildPlaceholder(BuildContext context)
    method _buildErrorWidget (line 61) | Widget _buildErrorWidget()

FILE: lib/widgets/mini_player/mini_player_progress.dart
  class MiniPlayerProgress (line 5) | class MiniPlayerProgress extends StatelessWidget {
    method build (line 9) | Widget build(BuildContext context)

FILE: lib/widgets/pagination_controls.dart
  class PaginationControls (line 3) | class PaginationControls extends StatelessWidget {
    method build (line 18) | Widget build(BuildContext context)

FILE: lib/widgets/player/player_controls.dart
  class PlayerControls (line 5) | class PlayerControls extends StatelessWidget {
    method build (line 9) | Widget build(BuildContext context)

FILE: lib/widgets/player/player_cover.dart
  class PlayerCover (line 5) | class PlayerCover extends StatelessWidget {
    method build (line 16) | Widget build(BuildContext context)

FILE: lib/widgets/player/player_progress.dart
  class PlayerProgress (line 5) | class PlayerProgress extends StatelessWidget {
    method _formatDuration (line 8) | String _formatDuration(Duration? duration)
    method twoDigits (line 10) | String twoDigits(int n)
    method _ensureValueInRange (line 16) | double _ensureValueInRange(double value, double min, double max)
    method build (line 23) | Widget build(BuildContext context)

FILE: lib/widgets/player/player_seek_controls.dart
  class PlayerSeekControls (line 5) | class PlayerSeekControls extends StatelessWidget {
    method build (line 9) | Widget build(BuildContext context)

FILE: lib/widgets/player/player_work_info.dart
  class PlayerWorkInfo (line 5) | class PlayerWorkInfo extends StatelessWidget {
    method build (line 14) | Widget build(BuildContext context)

FILE: lib/widgets/work_card/components/work_cover_image.dart
  class WorkCoverImage (line 5) | class WorkCoverImage extends StatelessWidget {
    method build (line 20) | Widget build(BuildContext context)

FILE: lib/widgets/work_card/components/work_footer.dart
  class WorkFooter (line 4) | class WorkFooter extends StatelessWidget {
    method build (line 13) | Widget build(BuildContext context)

FILE: lib/widgets/work_card/components/work_info_section.dart
  class WorkInfoSection (line 7) | class WorkInfoSection extends StatelessWidget {
    method _formatDuration (line 15) | String _formatDuration(int? seconds)
    method build (line 29) | Widget build(BuildContext context)

FILE: lib/widgets/work_card/components/work_tags_panel.dart
  class WorkTagsPanel (line 5) | class WorkTagsPanel extends StatelessWidget {
    method _getLocalizedTagName (line 13) | String _getLocalizedTagName(Tag tag)
    method build (line 22) | Widget build(BuildContext context)

FILE: lib/widgets/work_card/components/work_title.dart
  class WorkTitle (line 4) | class WorkTitle extends StatelessWidget {
    method build (line 13) | Widget build(BuildContext context)

FILE: lib/widgets/work_card/work_card.dart
  class WorkCard (line 6) | class WorkCard extends StatelessWidget {
    method build (line 17) | Widget build(BuildContext context)

FILE: lib/widgets/work_grid.dart
  class WorkGrid (line 6) | class WorkGrid extends StatelessWidget {
    method build (line 19) | Widget build(BuildContext context)

FILE: lib/widgets/work_grid/components/grid_content.dart
  class GridContent (line 9) | class GridContent extends StatelessWidget {
    method _scrollToTop (line 31) | void _scrollToTop()
    method build (line 42) | Widget build(BuildContext context)

FILE: lib/widgets/work_grid/components/grid_empty.dart
  class GridEmpty (line 3) | class GridEmpty extends StatelessWidget {
    method build (line 14) | Widget build(BuildContext context)

FILE: lib/widgets/work_grid/components/grid_error.dart
  class GridError (line 3) | class GridError extends StatelessWidget {
    method build (line 14) | Widget build(BuildContext context)

FILE: lib/widgets/work_grid/components/grid_loading.dart
  class GridLoading (line 4) | class GridLoading extends StatelessWidget {
    method build (line 8) | Widget build(BuildContext context)

FILE: lib/widgets/work_grid/enhanced_work_grid_view.dart
  class EnhancedWorkGridView (line 10) | class EnhancedWorkGridView extends StatelessWidget {
    method build (line 43) | Widget build(BuildContext context)

FILE: lib/widgets/work_grid/models/grid_config.dart
  class GridConfig (line 3) | class GridConfig {

FILE: lib/widgets/work_grid_view.dart
  class WorkGridView (line 7) | class WorkGridView extends StatelessWidget {
    method build (line 32) | Widget build(BuildContext context)

FILE: lib/widgets/work_row.dart
  class WorkRow (line 5) | class WorkRow extends StatelessWidget {
    method build (line 18) | Widget build(BuildContext context)

FILE: linux/flutter/generated_plugin_registrant.cc
  function fl_register_plugins (line 10) | void fl_register_plugins(FlPluginRegistry* registry) {

FILE: linux/main.cc
  function main (line 3) | int main(int argc, char** argv) {

FILE: linux/my_application.cc
  type _MyApplication (line 10) | struct _MyApplication {
  function my_application_activate (line 18) | static void my_application_activate(GApplication* application) {
  function gboolean (line 66) | static gboolean my_application_local_command_line(GApplication* applicat...
  function my_application_startup (line 85) | static void my_application_startup(GApplication* application) {
  function my_application_shutdown (line 94) | static void my_application_shutdown(GApplication* application) {
  function my_application_dispose (line 103) | static void my_application_dispose(GObject* object) {
  function my_application_class_init (line 109) | static void my_application_class_init(MyApplicationClass* klass) {
  function my_application_init (line 117) | static void my_application_init(MyApplication* self) {}
  function MyApplication (line 119) | MyApplication* my_application_new() {

FILE: test/widget_test.dart
  function main (line 13) | void main()

FILE: windows/flutter/generated_plugin_registrant.cc
  function RegisterPlugins (line 11) | void RegisterPlugins(flutter::PluginRegistry* registry) {

FILE: windows/runner/flutter_window.cpp
  function LRESULT (line 50) | LRESULT

FILE: windows/runner/flutter_window.h
  function class (line 12) | class FlutterWindow : public Win32Window {

FILE: windows/runner/main.cpp
  function wWinMain (line 8) | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,

FILE: windows/runner/utils.cpp
  function CreateAndAttachConsole (line 10) | void CreateAndAttachConsole() {
  function GetCommandLineArguments (line 24) | std::vector<std::string> GetCommandLineArguments() {
  function Utf8FromUtf16 (line 44) | std::string Utf8FromUtf16(const wchar_t* utf16_string) {

FILE: windows/runner/win32_window.cpp
  function Scale (line 36) | int Scale(int source, double scale_factor) {
  function EnableFullDpiSupportIfAvailable (line 42) | void EnableFullDpiSupportIfAvailable(HWND hwnd) {
  class WindowClassRegistrar (line 59) | class WindowClassRegistrar {
    method WindowClassRegistrar (line 64) | static WindowClassRegistrar* GetInstance() {
    method WindowClassRegistrar (line 80) | WindowClassRegistrar() = default;
  function wchar_t (line 89) | const wchar_t* WindowClassRegistrar::GetWindowClass() {
  function LRESULT (line 157) | LRESULT CALLBACK Win32Window::WndProc(HWND const window,
  function LRESULT (line 176) | LRESULT
  function Win32Window (line 236) | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
  function RECT (line 252) | RECT Win32Window::GetClientArea() {
  function HWND (line 258) | HWND Win32Window::GetHandle() {

FILE: windows/runner/win32_window.h
  type Size (line 21) | struct Size {
Condensed preview — 318 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (916K chars).
[
  {
    "path": ".github/scripts/process_commits.sh",
    "chars": 5373,
    "preview": "#!/bin/bash\n\n# 主标题的 emoji 映射\nprocess_commit() {\n  local title=\"$1\"\n  case \"$title\" in\n    feat:*|feature:*) echo \"✨ ${ti"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 5367,
    "preview": "name: Build and Release\n\npermissions:\n  contents: write\n\non:\n  push:\n    tags:\n      - 'v*'  # 当推送 v 开头的tag时触发,如 v1.0.0\n"
  },
  {
    "path": ".gitignore",
    "chars": 762,
    "preview": "# 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 re"
  },
  {
    "path": ".metadata",
    "chars": 1706,
    "preview": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrade"
  },
  {
    "path": "LICENSE",
    "chars": 1776,
    "preview": "# Creative Commons Attribution-NonCommercial-ShareAlike License (CC BY-NC-SA)\n\n## License Summary\n\nThis license lets oth"
  },
  {
    "path": "README.md",
    "chars": 982,
    "preview": "# Yuro\n\n[English](README_en.md)\n\n一个使用 Flutter 构建的 ASMR.ONE 客户端。\n\n## 项目概述\n\nYuro 旨在通过精美的动画和现代化的用户界面,提供流畅愉悦的 ASMR 聆听体验。\n\n##"
  },
  {
    "path": "README_en.md",
    "chars": 1477,
    "preview": "# ASMR One App\n\n[中文说明](README.md)\n\nA beautiful and modern ASMR player application built with Flutter.\n\n## Project Overvi"
  },
  {
    "path": "analysis_options.yaml",
    "chars": 1534,
    "preview": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n#"
  },
  {
    "path": "android/.gitignore",
    "chars": 247,
    "preview": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remembe"
  },
  {
    "path": "android/app/build.gradle",
    "chars": 1974,
    "preview": "plugins {\n    id \"com.android.application\"\n    id \"kotlin-android\"\n    // The Flutter Gradle Plugin must be applied afte"
  },
  {
    "path": "android/app/proguard-rules.pro",
    "chars": 1114,
    "preview": "## Flutter wrapper\n-keep class io.flutter.app.** { *; }\n-keep class io.flutter.plugin.**  { *; }\n-keep class io.flutter."
  },
  {
    "path": "android/app/src/debug/AndroidManifest.xml",
    "chars": 378,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for d"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "chars": 3304,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <uses-permission android:name=\"android.permiss"
  },
  {
    "path": "android/app/src/main/kotlin/com/example/asmrapp/MainActivity.kt",
    "chars": 180,
    "preview": "package com.example.asmrapp\n\nimport io.flutter.embedding.android.FlutterActivity\nimport com.ryanheise.audioservice.Audio"
  },
  {
    "path": "android/app/src/main/kotlin/one/asmr/yuro/MainActivity.kt",
    "chars": 636,
    "preview": "package one.asmr.yuro\n\nimport io.flutter.embedding.android.FlutterActivity\nimport com.ryanheise.audioservice.AudioServic"
  },
  {
    "path": "android/app/src/main/kotlin/one/asmr/yuro/lyric/LyricOverlayPlugin.kt",
    "chars": 2377,
    "preview": "package one.asmr.yuro.lyric\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content."
  },
  {
    "path": "android/app/src/main/kotlin/one/asmr/yuro/lyric/LyricOverlayService.kt",
    "chars": 4908,
    "preview": "package one.asmr.yuro.lyric\n\nimport android.app.Service\nimport android.content.Context\nimport android.content.Intent\nimp"
  },
  {
    "path": "android/app/src/main/res/drawable/launch_background.xml",
    "chars": 434,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmln"
  },
  {
    "path": "android/app/src/main/res/drawable-v21/launch_background.xml",
    "chars": 438,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmln"
  },
  {
    "path": "android/app/src/main/res/layout/lyric_overlay.xml",
    "chars": 487,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "chars": 996,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is sta"
  },
  {
    "path": "android/app/src/main/res/values-night/styles.xml",
    "chars": 995,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is sta"
  },
  {
    "path": "android/app/src/main/res/xml/network_security_config.xml",
    "chars": 227,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n    <domain-config cleartextTrafficPermitted=\"true\">\n  "
  },
  {
    "path": "android/app/src/profile/AndroidManifest.xml",
    "chars": 378,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for d"
  },
  {
    "path": "android/build.gradle",
    "chars": 322,
    "preview": "allprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.buildDir = \"../build\"\nsubp"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "chars": 200,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dist"
  },
  {
    "path": "android/gradle.properties",
    "chars": 135,
    "preview": "org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError\nandroid.useAndroidX=true\nandroid.enabl"
  },
  {
    "path": "android/settings.gradle",
    "chars": 727,
    "preview": "pluginManagement {\n    def flutterSdkPath = {\n        def properties = new Properties()\n        file(\"local.properties\")"
  },
  {
    "path": "devtools_options.yaml",
    "chars": 184,
    "preview": "description: This file stores settings for Dart & Flutter DevTools.\ndocumentation: https://docs.flutter.dev/tools/devtoo"
  },
  {
    "path": "docs/architecture.md",
    "chars": 1151,
    "preview": "# ASMR Music App 架构设计\n\n## 目录结构\n\n<pre>\nlib/\n├── main.dart              # 应用程序入口\n├── screens/              # 页面\n│   ├── ho"
  },
  {
    "path": "docs/audio_architecture.md",
    "chars": 3715,
    "preview": "# ASMR One App 音频播放架构设计\n\n## 1. 架构概述\n\n本文档描述了 ASMR One App 音频播放功能的架构设计。遵循 Clean Architecture 原则,采用事件驱动架构,将音频播放功能分为核心层、数据层和"
  },
  {
    "path": "docs/guidelines.md",
    "chars": 4001,
    "preview": "# ASMR Music App Development Guidelines\n# ASMR Music App 开发准则\n\n## Important Notice | 重要说明\n\nThese guidelines are living d"
  },
  {
    "path": "docs/guidelines_en.md",
    "chars": 3409,
    "preview": "# ASMR One App Development Guidelines\n\n[中文版本](guidelines_zh.md)\n\n## Important Notice\n\nThese guidelines are living docume"
  },
  {
    "path": "docs/guidelines_zh.md",
    "chars": 2127,
    "preview": "# ASMR One App 开发准则\n\n[English Version](guidelines_en.md)\n\n## 重要说明\n\n本准则是一个动态文档,会随项目发展而演进。实践中的任何变更都必须更新到本准则中,特别是架构部分必须与实际项"
  },
  {
    "path": "ios/.gitignore",
    "chars": 569,
    "preview": "**/dgph\n*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/De"
  },
  {
    "path": "ios/Flutter/AppFrameworkInfo.plist",
    "chars": 774,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/Flutter/Debug.xcconfig",
    "chars": 107,
    "preview": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Flutter/Release.xcconfig",
    "chars": 109,
    "preview": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Podfile",
    "chars": 1414,
    "preview": "# Uncomment this line to define a global platform for your project\n# platform :ios, '12.0'\n\n# CocoaPods analytics sends "
  },
  {
    "path": "ios/Runner/AppDelegate.swift",
    "chars": 391,
    "preview": "import Flutter\nimport UIKit\n\n@main\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ appl"
  },
  {
    "path": "ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 2519,
    "preview": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n   "
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "chars": 391,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage.png\",\n      \"scale\" : \"1x\"\n    },\n  "
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
    "chars": 336,
    "preview": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in"
  },
  {
    "path": "ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "chars": 2377,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
  },
  {
    "path": "ios/Runner/Base.lproj/Main.storyboard",
    "chars": 1605,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
  },
  {
    "path": "ios/Runner/Info.plist",
    "chars": 1641,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/Runner/Runner-Bridging-Header.h",
    "chars": 38,
    "preview": "#import \"GeneratedPluginRegistrant.h\"\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.pbxproj",
    "chars": 31328,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 135,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "chars": 226,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "chars": 3647,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "ios/Runner.xcworkspace/contents.xcworkspacedata",
    "chars": 224,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodepr"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "chars": 226,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/RunnerTests/RunnerTests.swift",
    "chars": 285,
    "preview": "import Flutter\nimport UIKit\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n  func testExample() {\n    // If you add cod"
  },
  {
    "path": "lib/common/constants/strings.dart",
    "chars": 722,
    "preview": "class Strings {\n  // App\n  static const String appName = 'asmr.one';\n\n  // Common\n  static const String loading = '加载中.."
  },
  {
    "path": "lib/core/audio/README.md",
    "chars": 673,
    "preview": "# 音频核心功能\n\n## 当前架构\n\n### 1. 事件驱动系统\n- 基于 RxDart 的事件中心\n- 统一的事件定义和处理\n- 支持事件过滤和转换\n\n### 2. 核心服务 (AudioPlayerService)\n- 实现 IAudi"
  },
  {
    "path": "lib/core/audio/audio_player_handler.dart",
    "chars": 2038,
    "preview": "import 'package:asmrapp/core/audio/events/playback_event_hub.dart';\nimport 'package:audio_service/audio_service.dart';\ni"
  },
  {
    "path": "lib/core/audio/audio_player_service.dart",
    "chars": 4729,
    "preview": "import 'dart:async';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:just_audio/just_audio.dart';\nimport 'pa"
  },
  {
    "path": "lib/core/audio/audio_service.dart",
    "chars": 249,
    "preview": "import 'package:just_audio/just_audio.dart';\n\nabstract class AudioService {\n  Future<void> play(String url);\n  Future<vo"
  },
  {
    "path": "lib/core/audio/cache/audio_cache_manager.dart",
    "chars": 4166,
    "preview": "import 'dart:io';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:crypto/crypto.dart';\nimport 'dart:c"
  },
  {
    "path": "lib/core/audio/controllers/playback_controller.dart",
    "chars": 4335,
    "preview": "import 'package:asmrapp/utils/logger.dart';\nimport 'package:just_audio/just_audio.dart';\nimport '../models/playback_cont"
  },
  {
    "path": "lib/core/audio/events/playback_event.dart",
    "chars": 1591,
    "preview": "import 'package:just_audio/just_audio.dart';\nimport '../models/audio_track_info.dart';\nimport '../models/playback_contex"
  },
  {
    "path": "lib/core/audio/events/playback_event_hub.dart",
    "chars": 1235,
    "preview": "import 'package:rxdart/rxdart.dart';\nimport './playback_event.dart';\n\nclass PlaybackEventHub {\n  // 统一的事件流,处理所有类型的事件\n  f"
  },
  {
    "path": "lib/core/audio/i_audio_player_service.dart",
    "chars": 566,
    "preview": "import './models/audio_track_info.dart';\nimport './models/playback_context.dart';\n\nabstract class IAudioPlayerService {\n"
  },
  {
    "path": "lib/core/audio/models/audio_track_info.dart",
    "chars": 290,
    "preview": "class AudioTrackInfo {\n  final String title;\n  final String artist;\n  final String coverUrl;\n  final String url;\n  final"
  },
  {
    "path": "lib/core/audio/models/file_path.dart",
    "chars": 4694,
    "preview": "import 'package:asmrapp/data/models/files/files.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'pa"
  },
  {
    "path": "lib/core/audio/models/play_mode.dart",
    "chars": 84,
    "preview": "enum PlayMode {\n  single,     // 单曲循环\n  loop,       // 列表循环\n  sequence,   // 顺序播放\n} "
  },
  {
    "path": "lib/core/audio/models/playback_context.dart",
    "chars": 4809,
    "preview": "import 'package:asmrapp/core/audio/utils/audio_error_handler.dart';\nimport 'package:asmrapp/data/models/works/work.dart'"
  },
  {
    "path": "lib/core/audio/models/subtitle.dart",
    "chars": 4459,
    "preview": "import 'dart:math' as math;\n\nenum SubtitleState {\n  current,  // 当前播放的字幕\n  waiting,  // 即将播放的字幕\n  passed    // 已经播放过的字幕\n"
  },
  {
    "path": "lib/core/audio/notification/audio_notification_service.dart",
    "chars": 1804,
    "preview": "import 'package:asmrapp/core/audio/events/playback_event_hub.dart';\nimport 'package:audio_service/audio_service.dart';\ni"
  },
  {
    "path": "lib/core/audio/state/playback_state_manager.dart",
    "chars": 4357,
    "preview": "import 'dart:async';\nimport 'package:just_audio/just_audio.dart';\nimport '../models/audio_track_info.dart';\nimport '../m"
  },
  {
    "path": "lib/core/audio/storage/i_playback_state_repository.dart",
    "chars": 197,
    "preview": "import 'package:asmrapp/data/models/playback/playback_state.dart';\n\nabstract class IPlaybackStateRepository {\n  Future<v"
  },
  {
    "path": "lib/core/audio/storage/playback_state_repository.dart",
    "chars": 1192,
    "preview": "import 'dart:convert';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:asmrapp/utils/logger"
  },
  {
    "path": "lib/core/audio/utils/audio_error_handler.dart",
    "chars": 1401,
    "preview": "import 'package:asmrapp/utils/logger.dart';\n\nenum AudioErrorType {\n  playback,    // 播放错误\n  playlist,    // 播放列表错误\n  sta"
  },
  {
    "path": "lib/core/audio/utils/playlist_builder.dart",
    "chars": 1096,
    "preview": "import 'package:just_audio/just_audio.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmr"
  },
  {
    "path": "lib/core/audio/utils/track_info_creator.dart",
    "chars": 750,
    "preview": "import 'package:asmrapp/core/audio/models/audio_track_info.dart';\nimport 'package:asmrapp/data/models/files/child.dart';"
  },
  {
    "path": "lib/core/cache/recommendation_cache_manager.dart",
    "chars": 1948,
    "preview": "import 'dart:collection';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/utils/logger."
  },
  {
    "path": "lib/core/di/service_locator.dart",
    "chars": 3361,
    "preview": "import 'dart:io';\nimport 'package:asmrapp/core/platform/dummy_lyric_overlay_controller.dart';\nimport 'package:get_it/get"
  },
  {
    "path": "lib/core/platform/dummy_lyric_overlay_controller.dart",
    "chars": 723,
    "preview": "import 'package:asmrapp/utils/logger.dart';\nimport 'i_lyric_overlay_controller.dart';\n\nclass DummyLyricOverlayController"
  },
  {
    "path": "lib/core/platform/i_lyric_overlay_controller.dart",
    "chars": 412,
    "preview": "abstract class ILyricOverlayController {\n  /// 初始化悬浮窗\n  Future<void> initialize();\n  \n  /// 显示悬浮窗\n  Future<void> show();"
  },
  {
    "path": "lib/core/platform/lyric_overlay_controller.dart",
    "chars": 1711,
    "preview": "import 'package:flutter/services.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:permission_handler/p"
  },
  {
    "path": "lib/core/platform/lyric_overlay_manager.dart",
    "chars": 2972,
    "preview": "import 'dart:async';\n\nimport 'package:asmrapp/core/platform/i_lyric_overlay_controller.dart';\nimport 'package:asmrapp/co"
  },
  {
    "path": "lib/core/platform/wakelock_controller.dart",
    "chars": 1330,
    "preview": "import 'package:flutter/material.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nimport 'package:wa"
  },
  {
    "path": "lib/core/subtitle/cache/subtitle_cache_manager.dart",
    "chars": 1566,
    "preview": "import 'dart:convert';\nimport 'dart:typed_data';\n\nimport 'package:flutter_cache_manager/flutter_cache_manager.dart';\nimp"
  },
  {
    "path": "lib/core/subtitle/i_subtitle_service.dart",
    "chars": 621,
    "preview": "import 'package:asmrapp/core/audio/models/subtitle.dart';\n\nabstract class ISubtitleService {\n  // 字幕加载\n  Future<void> lo"
  },
  {
    "path": "lib/core/subtitle/managers/subtitle_state_manager.dart",
    "chars": 2139,
    "preview": "import 'dart:async';\nimport 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'package:asmrapp/utils/logger.dart"
  },
  {
    "path": "lib/core/subtitle/parsers/lrc_parser.dart",
    "chars": 2597,
    "preview": "import 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'package:asmrapp/core/subtitle/parsers/subtitle_parser."
  },
  {
    "path": "lib/core/subtitle/parsers/subtitle_parser.dart",
    "chars": 504,
    "preview": "import 'package:asmrapp/core/audio/models/subtitle.dart';\n\n/// 字幕解析器接口\nabstract class SubtitleParser {\n  /// 解析字幕内容\n  Su"
  },
  {
    "path": "lib/core/subtitle/parsers/subtitle_parser_factory.dart",
    "chars": 586,
    "preview": "import 'package:asmrapp/core/subtitle/parsers/subtitle_parser.dart';\nimport 'package:asmrapp/core/subtitle/parsers/vtt_p"
  },
  {
    "path": "lib/core/subtitle/parsers/vtt_parser.dart",
    "chars": 1894,
    "preview": "import 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'package:asmrapp/core/subtitle/parsers/subtitle_parser."
  },
  {
    "path": "lib/core/subtitle/subtitle_loader.dart",
    "chars": 2683,
    "preview": "import 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/data/models/files/files.dart';\nimport 'pa"
  },
  {
    "path": "lib/core/subtitle/subtitle_service.dart",
    "chars": 1682,
    "preview": "import 'dart:async';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/core/audio/models/subtitle.dart"
  },
  {
    "path": "lib/core/subtitle/utils/subtitle_matcher.dart",
    "chars": 1524,
    "preview": "import 'package:asmrapp/data/models/files/child.dart';\n\nclass SubtitleMatcher {\n  // 支持的字幕格式\n  static const supportedFor"
  },
  {
    "path": "lib/core/theme/app_colors.dart",
    "chars": 1148,
    "preview": "import 'package:flutter/material.dart';\n\n/// 应用颜色配置\nclass AppColors {\n  // 禁止实例化\n  const AppColors._();\n\n  // 亮色主题颜色\n  s"
  },
  {
    "path": "lib/core/theme/app_theme.dart",
    "chars": 1105,
    "preview": "import 'package:flutter/material.dart';\nimport 'app_colors.dart';\n\n/// 应用主题配置\nclass AppTheme {\n  // 禁止实例化\n  const AppThe"
  },
  {
    "path": "lib/core/theme/theme_controller.dart",
    "chars": 1107,
    "preview": "import 'package:flutter/material.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nclass ThemeControl"
  },
  {
    "path": "lib/data/models/audio/README.md",
    "chars": 182,
    "preview": "# 音频数据模型\n\n此目录包含所有音频相关的数据模型定义。\n\n## 文件结构\n\n- `audio_track.dart` - 音频轨道模型\n- `playlist.dart` - 播放列表模型\n- `audio_metadata.dart`"
  },
  {
    "path": "lib/data/models/auth/auth_resp/auth_resp.dart",
    "chars": 349,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'user.dart';\n\npart 'auth_resp.freezed.dart';\npart '"
  },
  {
    "path": "lib/data/models/auth/auth_resp/auth_resp.freezed.dart",
    "chars": 6105,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/auth/auth_resp/auth_resp.g.dart",
    "chars": 668,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'auth_resp.dart';\n\n// ***********************************************"
  },
  {
    "path": "lib/data/models/auth/auth_resp/user.dart",
    "chars": 358,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'user.freezed.dart';\npart 'user.g.dart';\n\n@freezed\ncl"
  },
  {
    "path": "lib/data/models/auth/auth_resp/user.freezed.dart",
    "chars": 7763,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/auth/auth_resp/user.g.dart",
    "chars": 810,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'user.dart';\n\n// ****************************************************"
  },
  {
    "path": "lib/data/models/files/child.dart",
    "chars": 476,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'work.dart';\n\npart 'child.freezed.dart';\npart 'chil"
  },
  {
    "path": "lib/data/models/files/child.freezed.dart",
    "chars": 11511,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/files/child.g.dart",
    "chars": 1334,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'child.dart';\n\n// ***************************************************"
  },
  {
    "path": "lib/data/models/files/files.dart",
    "chars": 347,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'child.dart';\n\npart 'files.freezed.dart';\npart 'fil"
  },
  {
    "path": "lib/data/models/files/files.freezed.dart",
    "chars": 6578,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/files/files.g.dart",
    "chars": 744,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'files.dart';\n\n// ***************************************************"
  },
  {
    "path": "lib/data/models/files/work.dart",
    "chars": 370,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'work.freezed.dart';\npart 'work.g.dart';\n\n@freezed\ncl"
  },
  {
    "path": "lib/data/models/files/work.freezed.dart",
    "chars": 6694,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/files/work.g.dart",
    "chars": 667,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'work.dart';\n\n// ****************************************************"
  },
  {
    "path": "lib/data/models/mark_lists/mark_lists.dart",
    "chars": 411,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'pagination.dart';\nimport 'playlist.dart';\n\npart 'm"
  },
  {
    "path": "lib/data/models/mark_lists/mark_lists.freezed.dart",
    "chars": 6967,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/mark_lists/mark_lists.g.dart",
    "chars": 824,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'mark_lists.dart';\n\n// **********************************************"
  },
  {
    "path": "lib/data/models/mark_lists/pagination.dart",
    "chars": 362,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'pagination.freezed.dart';\npart 'pagination.g.dart';\n"
  },
  {
    "path": "lib/data/models/mark_lists/pagination.freezed.dart",
    "chars": 6595,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/mark_lists/pagination.g.dart",
    "chars": 727,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'pagination.dart';\n\n// **********************************************"
  },
  {
    "path": "lib/data/models/mark_lists/playlist.dart",
    "chars": 733,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'playlist.freezed.dart';\npart 'playlist.g.dart';\n\n@fr"
  },
  {
    "path": "lib/data/models/mark_lists/playlist.freezed.dart",
    "chars": 14693,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/mark_lists/playlist.g.dart",
    "chars": 1488,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playlist.dart';\n\n// ************************************************"
  },
  {
    "path": "lib/data/models/mark_status.dart",
    "chars": 171,
    "preview": "enum MarkStatus {\n  wantToListen('想听'),\n  listening('在听'),\n  listened('听过'),\n  relistening('重听'),\n  onHold('搁置');\n\n  fin"
  },
  {
    "path": "lib/data/models/my_lists/README.md",
    "chars": 49,
    "preview": "虽然已有相似结构,但为了方便管理,还是单独创建一个文件夹,专门用来处理“播放清单”这个页面的东西。"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/my_playlists.dart",
    "chars": 427,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'pagination.dart';\nimport 'playlist.dart';\n\npart 'm"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/my_playlists.freezed.dart",
    "chars": 7094,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/my_playlists.g.dart",
    "chars": 836,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'my_playlists.dart';\n\n// ********************************************"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/pagination.dart",
    "chars": 362,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'pagination.freezed.dart';\npart 'pagination.g.dart';\n"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/pagination.freezed.dart",
    "chars": 6595,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/pagination.g.dart",
    "chars": 727,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'pagination.dart';\n\n// **********************************************"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/playlist.dart",
    "chars": 733,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'playlist.freezed.dart';\npart 'playlist.g.dart';\n\n@fr"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/playlist.freezed.dart",
    "chars": 14693,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/playlist.g.dart",
    "chars": 1488,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playlist.dart';\n\n// ************************************************"
  },
  {
    "path": "lib/data/models/playback/playback_state.dart",
    "chars": 827,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimpor"
  },
  {
    "path": "lib/data/models/playback/playback_state.freezed.dart",
    "chars": 12126,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/playback/playback_state.g.dart",
    "chars": 1494,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playback_state.dart';\n\n// ******************************************"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/pagination.dart",
    "chars": 362,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'pagination.freezed.dart';\npart 'pagination.g.dart';\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/pagination.freezed.dart",
    "chars": 6595,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/pagination.g.dart",
    "chars": 727,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'pagination.dart';\n\n// **********************************************"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlist.dart",
    "chars": 667,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'playlist.freezed.dart';\npart 'playlist.g.dart';\n\n@fr"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlist.freezed.dart",
    "chars": 13409,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlist.g.dart",
    "chars": 1371,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playlist.dart';\n\n// ************************************************"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlists_with_exist_statu.dart",
    "chars": 527,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'pagination.dart';\nimport 'playlist.dart';\n\npart 'p"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlists_with_exist_statu.freezed.dart",
    "chars": 7846,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlists_with_exist_statu.g.dart",
    "chars": 928,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playlists_with_exist_statu.dart';\n\n// ******************************"
  },
  {
    "path": "lib/data/models/works/circle.dart",
    "chars": 404,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'circle.freezed.dart';\npart 'circle.g.dart';\n\n@freeze"
  },
  {
    "path": "lib/data/models/works/circle.freezed.dart",
    "chars": 7436,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/circle.g.dart",
    "chars": 745,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'circle.dart';\n\n// **************************************************"
  },
  {
    "path": "lib/data/models/works/en_us.dart",
    "chars": 301,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'en_us.freezed.dart';\npart 'en_us.g.dart';\n\n@freezed\n"
  },
  {
    "path": "lib/data/models/works/en_us.freezed.dart",
    "chars": 5867,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/en_us.g.dart",
    "chars": 577,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'en_us.dart';\n\n// ***************************************************"
  },
  {
    "path": "lib/data/models/works/i18n.dart",
    "chars": 437,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'en_us.dart';\nimport 'ja_jp.dart';\nimport 'zh_cn.da"
  },
  {
    "path": "lib/data/models/works/i18n.freezed.dart",
    "chars": 7860,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/i18n.g.dart",
    "chars": 866,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'i18n.dart';\n\n// ****************************************************"
  },
  {
    "path": "lib/data/models/works/ja_jp.dart",
    "chars": 273,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'ja_jp.freezed.dart';\npart 'ja_jp.g.dart';\n\n@freezed\n"
  },
  {
    "path": "lib/data/models/works/ja_jp.freezed.dart",
    "chars": 4807,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/ja_jp.g.dart",
    "chars": 492,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'ja_jp.dart';\n\n// ***************************************************"
  },
  {
    "path": "lib/data/models/works/language_edition.dart",
    "chars": 566,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'language_edition.freezed.dart';\npart 'language_editi"
  },
  {
    "path": "lib/data/models/works/language_edition.freezed.dart",
    "chars": 9862,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/language_edition.g.dart",
    "chars": 1024,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'language_edition.dart';\n\n// ****************************************"
  },
  {
    "path": "lib/data/models/works/other_language_editions_in_db.dart",
    "chars": 641,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'other_language_editions_in_db.freezed.dart';\npart 'o"
  },
  {
    "path": "lib/data/models/works/other_language_editions_in_db.freezed.dart",
    "chars": 10260,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/other_language_editions_in_db.g.dart",
    "chars": 1045,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'other_language_editions_in_db.dart';\n\n// ***************************"
  },
  {
    "path": "lib/data/models/works/pagination.dart",
    "chars": 369,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'pagination.freezed.dart';\npart 'pagination.g.dart';\n"
  },
  {
    "path": "lib/data/models/works/pagination.freezed.dart",
    "chars": 6785,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/pagination.g.dart",
    "chars": 755,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'pagination.dart';\n\n// **********************************************"
  },
  {
    "path": "lib/data/models/works/tag.dart",
    "chars": 313,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'i18n.dart';\n\npart 'tag.freezed.dart';\npart 'tag.g."
  },
  {
    "path": "lib/data/models/works/tag.freezed.dart",
    "chars": 6279,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/tag.g.dart",
    "chars": 691,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'tag.dart';\n\n// *****************************************************"
  },
  {
    "path": "lib/data/models/works/translation_bonus_lang.dart",
    "chars": 710,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'translation_bonus_lang.freezed.dart';\npart 'translat"
  },
  {
    "path": "lib/data/models/works/translation_bonus_lang.freezed.dart",
    "chars": 11814,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/translation_bonus_lang.g.dart",
    "chars": 1263,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'translation_bonus_lang.dart';\n\n// **********************************"
  },
  {
    "path": "lib/data/models/works/translation_info.dart",
    "chars": 1815,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\nimport 'translation_bonus_lang.dart';\n\npart 'translation_in"
  },
  {
    "path": "lib/data/models/works/translation_info.freezed.dart",
    "chars": 19916,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/translation_info.g.dart",
    "chars": 1888,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'translation_info.dart';\n\n// ****************************************"
  },
  {
    "path": "lib/data/models/works/work.dart",
    "chars": 1768,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'circle.dart';\nimport 'language_edition.dart';\nimpo"
  },
  {
    "path": "lib/data/models/works/work.freezed.dart",
    "chars": 36460,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/work.g.dart",
    "chars": 3930,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'work.dart';\n\n// ****************************************************"
  },
  {
    "path": "lib/data/models/works/works.dart",
    "chars": 359,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'pagination.dart';\nimport 'work.dart';\n\npart 'works"
  },
  {
    "path": "lib/data/models/works/works.freezed.dart",
    "chars": 6536,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/works.g.dart",
    "chars": 775,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'works.dart';\n\n// ***************************************************"
  },
  {
    "path": "lib/data/models/works/zh_cn.dart",
    "chars": 301,
    "preview": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'zh_cn.freezed.dart';\npart 'zh_cn.g.dart';\n\n@freezed\n"
  },
  {
    "path": "lib/data/models/works/zh_cn.freezed.dart",
    "chars": 5867,
    "preview": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unus"
  },
  {
    "path": "lib/data/models/works/zh_cn.g.dart",
    "chars": 577,
    "preview": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'zh_cn.dart';\n\n// ***************************************************"
  },
  {
    "path": "lib/data/repositories/audio/README.md",
    "chars": 203,
    "preview": "# 音频数据仓库\n\n此目录包含音频数据访问的仓库实现。\n\n## 文件结构\n\n- `audio_repository.dart` - 音频数据仓库实现\n- `audio_repository_impl.dart` - 音频数据仓库具体实现\n-"
  },
  {
    "path": "lib/data/repositories/auth_repository.dart",
    "chars": 1203,
    "preview": "import 'dart:convert';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:asmrapp/data/models/"
  },
  {
    "path": "lib/data/services/api_service.dart",
    "chars": 14048,
    "preview": "import 'package:asmrapp/core/cache/recommendation_cache_manager.dart';\nimport 'package:asmrapp/data/models/mark_status.d"
  },
  {
    "path": "lib/data/services/auth_service.dart",
    "chars": 1206,
    "preview": "import 'package:asmrapp/data/models/auth/auth_resp/auth_resp.dart';\nimport 'package:dio/dio.dart';\nimport '../../utils/l"
  },
  {
    "path": "lib/data/services/interceptors/auth_interceptor.dart",
    "chars": 751,
    "preview": "import 'package:dio/dio.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/data/repositories/auth_repos"
  },
  {
    "path": "lib/main.dart",
    "chars": 1617,
    "preview": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/common/constants/strings.dart';\nimport 'package:asmrapp/"
  },
  {
    "path": "lib/presentation/layouts/work_layout_config.dart",
    "chars": 1857,
    "preview": "import 'package:flutter/material.dart';\n\n/// 设备类型\nenum DeviceType {\n  mobile,\n  tablet,\n  desktop;\n\n  /// 根据屏幕宽度获取设备类型\n "
  },
  {
    "path": "lib/presentation/layouts/work_layout_strategy.dart",
    "chars": 1228,
    "preview": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/pr"
  },
  {
    "path": "lib/presentation/models/filter_state.dart",
    "chars": 854,
    "preview": "class FilterState {\n  final String orderField;\n  final bool isDescending;\n\n  const FilterState({\n    this.orderField = '"
  },
  {
    "path": "lib/presentation/viewmodels/auth_viewmodel.dart",
    "chars": 2626,
    "preview": "import 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/auth/auth_resp/auth_resp.dart';\nimport 'pa"
  },
  {
    "path": "lib/presentation/viewmodels/base/paginated_works_viewmodel.dart",
    "chars": 2139,
    "preview": "import 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/"
  },
  {
    "path": "lib/presentation/viewmodels/detail_viewmodel.dart",
    "chars": 8317,
    "preview": "import 'package:asmrapp/data/models/playlists_with_exist_statu/pagination.dart';\nimport 'package:asmrapp/data/models/pla"
  },
  {
    "path": "lib/presentation/viewmodels/favorites_viewmodel.dart",
    "chars": 1637,
    "preview": "import 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/"
  },
  {
    "path": "lib/presentation/viewmodels/home_viewmodel.dart",
    "chars": 3392,
    "preview": "import 'dart:convert';\nimport 'package:asmrapp/presentation/viewmodels/base/paginated_works_viewmodel.dart';\nimport 'pac"
  },
  {
    "path": "lib/presentation/viewmodels/player_viewmodel.dart",
    "chars": 6011,
    "preview": "import 'package:asmrapp/core/audio/events/playback_event.dart';\nimport 'package:asmrapp/core/audio/models/audio_track_in"
  }
]

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

About this extraction

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

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

Copied to clipboard!