205 lines
6.6 KiB
Dart
205 lines
6.6 KiB
Dart
|
|
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<DetailSection> 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<EmbyRawMediaSource> sortedSources,
|
||
|
|
required int selectedSourceIdx,
|
||
|
|
required CrossServerSourceCard? selectedCrossServerCard,
|
||
|
|
required String? currentServerName,
|
||
|
|
required ValueChanged<EmbyRawItem> onEpisodeTap,
|
||
|
|
required ValueChanged<int> onSelectSourceIndex,
|
||
|
|
required ValueChanged<CrossServerSourceCard> onSelectCrossServerSource,
|
||
|
|
EmbyRawMediaSource? activeSource,
|
||
|
|
List<String> studioNames = const [],
|
||
|
|
List<String> directors = const [],
|
||
|
|
|
||
|
|
|
||
|
|
Widget? sectionLeading,
|
||
|
|
}) {
|
||
|
|
return <DetailSection>[
|
||
|
|
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<EmbyRawItem> 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<EmbyRawMediaSource> sortedSources,
|
||
|
|
required int selectedSourceIdx,
|
||
|
|
required CrossServerSourceCard? selectedCrossServerCard,
|
||
|
|
required String? currentServerName,
|
||
|
|
required ValueChanged<int> onSelectSourceIndex,
|
||
|
|
required ValueChanged<CrossServerSourceCard> 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,
|
||
|
|
);
|
||
|
|
}
|