import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/contracts/library.dart'; import '../../providers/di_providers.dart'; import 'android/android_media_info_section.dart'; import 'widgets/additional_parts_section.dart'; import 'widgets/emby_cast_section.dart'; import 'widgets/episode_list_section.dart'; import 'widgets/similar_section.dart'; import 'widgets/special_features_section.dart'; import 'emby_detail_helpers.dart'; import 'model/detail_section_model.dart'; import 'model/episode_auto_select_args.dart'; import 'widgets/source_selection_section.dart'; List buildEmbyDetailSections({ required WidgetRef ref, required EmbyRawItem baseItem, required EmbyRawItem displayItem, required String? seriesId, required String similarItemId, required TmdbMediaRef? tmdbMediaRef, required String? detailServerId, required String? initialSeasonId, required EmbyRawItem? tmdbSeriesItem, required List sortedSources, required int selectedSourceIdx, required CrossServerSourceCard? selectedCrossServerCard, required String? currentServerName, required ValueChanged onEpisodeTap, required ValueChanged onSelectSourceIndex, required ValueChanged onSelectCrossServerSource, EmbyRawMediaSource? activeSource, List studioNames = const [], List directors = const [], Widget? sectionLeading, }) { return [ if (seriesId != null) DetailSection( id: 'emby_episodes', child: _buildEpisodesSection( ref: ref, baseItem: baseItem, displayItem: displayItem, seriesId: seriesId, tmdbMediaRef: tmdbMediaRef, detailServerId: detailServerId, initialSeasonId: initialSeasonId, tmdbSeriesItem: tmdbSeriesItem, onEpisodeTap: onEpisodeTap, leading: sectionLeading, ), ), if (displayItem.Type == 'Movie' || displayItem.Type == 'Episode') DetailSection( id: 'emby_source', child: Padding( padding: const EdgeInsets.only(top: 24), child: _buildSourceSection( baseItem: baseItem, displayItem: displayItem, tmdbSeriesItem: tmdbSeriesItem, sortedSources: sortedSources, selectedSourceIdx: selectedSourceIdx, selectedCrossServerCard: selectedCrossServerCard, currentServerName: currentServerName, onSelectSourceIndex: onSelectSourceIndex, onSelectCrossServerSource: onSelectCrossServerSource, leading: sectionLeading, ), ), ), DetailSection( id: 'emby_cast', child: EmbyCastSection( item: baseItem, embedded: true, leading: sectionLeading, ), ), DetailSection( id: 'emby_media_detail', child: AndroidMediaInfoSection( source: activeSource, studioNames: studioNames, directors: directors, leading: sectionLeading, ), ), if (baseItem.Type != 'Series') DetailSection( id: 'emby_additional_parts', child: AdditionalPartsSection( itemId: displayItem.Id, embedded: true, leading: sectionLeading, ), ), if (baseItem.Type != 'Series') DetailSection( id: 'emby_special_features', child: SpecialFeaturesSection( itemId: displayItem.Id, embedded: true, leading: sectionLeading, ), ), DetailSection( id: 'emby_similar', child: SimilarSection( itemId: similarItemId, embedded: true, leading: sectionLeading, ), ), ]; } Widget _buildEpisodesSection({ required WidgetRef ref, required EmbyRawItem baseItem, required EmbyRawItem displayItem, required String seriesId, required TmdbMediaRef? tmdbMediaRef, required String? detailServerId, required String? initialSeasonId, required EmbyRawItem? tmdbSeriesItem, required ValueChanged onEpisodeTap, Widget? leading, }) { final showItem = baseItem.Type == 'Series' ? baseItem : tmdbSeriesItem; final resumeAsync = baseItem.Type == 'Series' && detailServerId != null ? ref.watch( seriesResumeTargetProvider(( serverId: detailServerId, seriesId: seriesId, )), ) : null; final autoSelect = EpisodeAutoSelectArgs.fromResume(resumeAsync); return EpisodeListSection( seriesId: seriesId, tmdbSeriesId: tmdbMediaRef != null && tmdbMediaRef.mediaType == 'tv' ? tmdbMediaRef.id : null, currentEpisodeId: displayItem.Type == 'Episode' ? displayItem.Id : null, currentEpisodeSeasonId: displayItem.Type == 'Episode' ? displayItem.SeasonId : null, currentEpisodeSeasonNumber: displayItem.Type == 'Episode' ? (displayItem.extra['ParentIndexNumber'] as num?)?.toInt() : null, currentEpisodeIndexNumber: displayItem.Type == 'Episode' ? displayItem.IndexNumber : null, initialSeasonId: initialSeasonId, autoSelectFirstEpisode: autoSelect.autoSelectFirstEpisode, autoSelectEpisodeId: autoSelect.autoSelectEpisodeId, autoSelectSeasonId: autoSelect.autoSelectSeasonId, onEpisodeTap: onEpisodeTap, embedded: true, showProviderIds: showItem != null ? lowercaseProviderIds(providerIdsOfItem(showItem)) : null, showTitle: showItem?.Name, showYear: showItem?.ProductionYear, leading: leading, ); } Widget _buildSourceSection({ required EmbyRawItem baseItem, required EmbyRawItem displayItem, required EmbyRawItem? tmdbSeriesItem, required List sortedSources, required int selectedSourceIdx, required CrossServerSourceCard? selectedCrossServerCard, required String? currentServerName, required ValueChanged onSelectSourceIndex, required ValueChanged onSelectCrossServerSource, Widget? leading, }) { final seriesItemForReq = baseItem.Type == 'Series' ? baseItem : tmdbSeriesItem; final req = buildCrossServerReqForDetail(displayItem, seriesItemForReq); return SourceSelectionSection( req: req, localSources: sortedSources, selectedSourceIdx: selectedSourceIdx, selectedCrossServerCard: selectedCrossServerCard, currentServerName: currentServerName, localMediaInfoItem: displayItem, showLoadingWhenEmpty: true, loadingText: '正在查找可用版本…', onLocalSourceTap: (src, index) => onSelectSourceIndex(index), onCrossServerSourceTap: onSelectCrossServerSource, leading: leading, ); }