From 65ba1a428793b38b9bee2dc1c137a98b896f79a3 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Wed, 5 Aug 2026 11:25:46 -0400 Subject: [PATCH] refactor: move editors out of core and reorganize --- .../components/organisms/side_action_bar.dart | 24 +- lib/src/core/di/service_locator.dart | 8 +- .../video/pro_video_processing_service.dart | 174 +++++++++++++++ .../video/video_processing_service.dart | 26 +++ .../utils/story_image_cropper.dart | 97 -------- .../pro_video_editor_repository.dart | 92 -------- .../pro_video_editor_repository_impl.dart | 174 --------------- .../services/audio_waveform_extractor.dart | 126 ----------- .../crop_rotate_editor_bottom_action_bar.dart | 96 -------- .../ui/widgets/player/preview_video.dart | 191 ---------------- .../text/text_editor_font_scale_sheet.dart | 166 -------------- lib/src/core/routing/app_router.dart | 6 - lib/src/core/routing/pages.dart | 1 - .../action_buttons/side_action_bar.dart | 10 +- .../feed/ui/widgets/feed/feeds_bar.dart | 2 +- .../ui/widgets/post/post_media_viewer.dart | 2 +- .../ui/pages/post_image_editor_page.dart | 40 ++++ .../ui/widgets/blur/blur_editor_bar.dart | 4 +- .../crop_rotate/crop_rotate_editor_bar.dart | 4 +- .../ui/widgets/editor_bottom_action_bar.dart} | 7 +- .../ui/widgets/filter/filter_editor_bar.dart | 4 +- .../ui/widgets/paint/paint_editor_bar.dart | 4 +- .../canvas/ui/widgets/sticker_picker.dart} | 0 .../ui/widgets/text/text_editor_bar.dart | 4 +- .../text/text_editor_color_picker.dart | 0 .../ui/widgets/tune/tune_editor_bar.dart | 4 +- .../models/media_editor_profile.dart | 1 + .../story/logic}/story_mention_layer.dart | 0 .../models/story_image_editor_result.dart | 0 .../ui/config}/story_editor_profile.dart | 6 +- .../config}/story_image_editor_configs.dart | 14 +- .../ui/mixins}/story_mention_editing.dart | 4 +- .../ui/pages}/story_image_editor_page.dart | 211 +----------------- .../story/ui}/story_sticker_editor.dart | 0 .../widgets/story_editor_bottom_section.dart | 0 .../ui/widgets/story_editor_toolbar.dart | 0 .../ui/widgets/story_editor_top_section.dart | 2 +- .../widgets/story_mention_picker_sheet.dart | 0 .../video}/models/video_editor_result.dart | 0 .../video}/services/audio_helper_service.dart | 2 +- .../services/audio_source_resolver.dart | 2 +- .../video}/services/video_export_planner.dart | 9 +- .../services/video_timing_export_service.dart | 0 .../config}/video_editor_configs_builder.dart | 41 ++-- .../video_editor_export_controller.dart | 18 +- .../video_editor_media_session.dart | 6 +- .../video_editor_preview_asset_loader.dart | 14 +- .../video/ui/pages/video_editor_page.dart} | 106 +++++---- .../audio/audio_range_selection_overlay.dart | 4 +- .../ui/widgets/clip/clip_editor_bar.dart | 4 +- .../ui/widgets/clip/clips_editor_bar.dart | 4 +- .../common/video_initializing_widget.dart | 0 .../widgets/common/video_progress_alert.dart | 7 +- .../layout/video_editor_bottom_section.dart | 8 +- .../widgets/layout/video_editor_header.dart | 0 .../layout/video_editor_regular_chrome.dart | 8 +- .../layout/video_editor_reveal_layout.dart | 2 +- .../widgets/layout/video_editor_toolbar.dart | 6 +- .../ui/widgets/layout/video_toolbar.dart | 0 .../widgets/player/video_player_widget.dart | 0 .../timeline/audio_timeline_track.dart | 8 +- .../timeline/layer_reorder_controller.dart | 0 .../widgets/timeline/layer_timing_track.dart | 6 +- .../primary_timeline_trim_overlay.dart | 4 +- .../widgets/timeline/scrollable_timeline.dart | 14 +- .../story_video_timeline_controls.dart | 4 +- .../widgets/timeline/timed_track_range.dart | 4 +- .../timeline/timed_track_range_parts.dart | 2 +- .../widgets/timeline/timeline_selection.dart | 0 .../timeline/timeline_selection_handle.dart | 0 .../timeline/timeline_subtrack_content.dart | 0 .../timeline/timeline_visual_tracks.dart | 2 +- .../ui/widgets/timeline/video_timeline.dart | 6 +- .../timeline/video_timeline_state.dart | 0 .../posting/ui/pages/image_review_page.dart | 9 +- .../posting/ui/pages/recording_page.dart | 56 +++-- .../widgets/image_sound_selection_sheet.dart | 6 +- .../posting/utils/captured_photo_flow.dart | 2 +- .../posting/utils}/create_media_actions.dart | 32 +-- .../profile/ui/pages/profile_page.dart | 2 +- .../audio_audition_controller.dart | 4 +- .../controllers/audio_audition_playback.dart | 0 .../sound}/models/audio_audition_timing.dart | 0 .../sound}/models/sound_audio_track.dart | 0 .../sound_picker_search_provider.dart | 2 +- .../providers/sound_picker_search_state.dart | 0 .../audio_selection_bottom_sheet.dart | 6 +- .../ui/widgets}/audio_track_list_section.dart | 8 +- .../sound/ui/widgets}/audio_waveform.dart | 0 .../sound/ui/widgets}/sound_artwork.dart | 0 .../sound/ui/widgets/sound_header_card.dart | 4 +- .../widgets}/sound_picker_sheet_scaffold.dart | 0 .../pro_video_processing_service_test.dart} | 7 +- .../story}/story_mention_layer_test.dart | 2 +- .../story}/story_sticker_editor_test.dart | 2 +- .../ui/widgets/story_editor_chrome_test.dart | 4 +- .../services/audio_helper_service_test.dart | 6 +- .../services/video_export_planner_test.dart | 24 +- .../video_timing_export_service_test.dart | 2 +- .../video_editor_media_session_test.dart | 6 +- .../audio_range_selection_overlay_test.dart | 4 +- .../video_editor_bottom_section_test.dart | 8 +- .../video_editor_reveal_layout_test.dart | 6 +- .../layout/video_editor_toolbar_test.dart | 6 +- .../scrollable_timeline_rendering_test.dart | 4 +- .../scrollable_timeline_scrolling_test.dart | 4 +- .../scrollable_timeline_selection_test.dart | 6 +- .../timeline/scrollable_timeline_test.dart | 4 +- .../scrollable_timeline_test_harness.dart | 8 +- .../timeline/video_timeline_state_test.dart | 2 +- .../widgets/timeline/video_timeline_test.dart | 8 +- .../utils/captured_photo_flow_test.dart | 2 +- .../audio_audition_controller_test.dart | 4 +- .../sound/models/sound_audio_track_test.dart} | 2 +- .../sound_picker_search_provider_test.dart | 4 +- .../audio_selection_bottom_sheet_test.dart | 12 +- .../sound_picker_sheet_scaffold_test.dart | 2 +- 117 files changed, 611 insertions(+), 1454 deletions(-) create mode 100644 lib/src/core/media_processing/video/pro_video_processing_service.dart create mode 100644 lib/src/core/media_processing/video/video_processing_service.dart delete mode 100644 lib/src/core/pro_image_editor/utils/story_image_cropper.dart delete mode 100644 lib/src/core/pro_video_editor/pro_video_editor_repository.dart delete mode 100644 lib/src/core/pro_video_editor/pro_video_editor_repository_impl.dart delete mode 100644 lib/src/core/pro_video_editor/services/audio_waveform_extractor.dart delete mode 100644 lib/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bottom_action_bar.dart delete mode 100644 lib/src/core/pro_video_editor/ui/widgets/player/preview_video.dart delete mode 100644 lib/src/core/pro_video_editor/ui/widgets/text/text_editor_font_scale_sheet.dart create mode 100644 lib/src/features/media_editor/canvas/ui/pages/post_image_editor_page.dart rename lib/src/{core/pro_video_editor => features/media_editor/canvas}/ui/widgets/blur/blur_editor_bar.dart (91%) rename lib/src/{core/pro_video_editor => features/media_editor/canvas}/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart (97%) rename lib/src/{core/pro_video_editor/ui/widgets/common/text_editor_bottom_action_bar.dart => features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart} (92%) rename lib/src/{core/pro_video_editor => features/media_editor/canvas}/ui/widgets/filter/filter_editor_bar.dart (96%) rename lib/src/{core/pro_video_editor => features/media_editor/canvas}/ui/widgets/paint/paint_editor_bar.dart (97%) rename lib/src/{core/pro_video_editor/ui/widgets/common/build_stickers.dart => features/media_editor/canvas/ui/widgets/sticker_picker.dart} (100%) rename lib/src/{core/pro_video_editor => features/media_editor/canvas}/ui/widgets/text/text_editor_bar.dart (97%) rename lib/src/{core/pro_video_editor => features/media_editor/canvas}/ui/widgets/text/text_editor_color_picker.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/canvas}/ui/widgets/tune/tune_editor_bar.dart (97%) create mode 100644 lib/src/features/media_editor/models/media_editor_profile.dart rename lib/src/{core/pro_image_editor => features/media_editor/story/logic}/story_mention_layer.dart (100%) rename lib/src/{core/pro_image_editor => features/media_editor/story}/models/story_image_editor_result.dart (100%) rename lib/src/{core/pro_image_editor => features/media_editor/story/ui/config}/story_editor_profile.dart (89%) rename lib/src/{core/pro_image_editor => features/media_editor/story/ui/config}/story_image_editor_configs.dart (92%) rename lib/src/{core/pro_image_editor => features/media_editor/story/ui/mixins}/story_mention_editing.dart (87%) rename lib/src/{core/pro_image_editor/ui => features/media_editor/story/ui/pages}/story_image_editor_page.dart (55%) rename lib/src/{core/pro_image_editor => features/media_editor/story/ui}/story_sticker_editor.dart (100%) rename lib/src/{core/pro_image_editor => features/media_editor/story}/ui/widgets/story_editor_bottom_section.dart (100%) rename lib/src/{core/pro_image_editor => features/media_editor/story}/ui/widgets/story_editor_toolbar.dart (100%) rename lib/src/{core/pro_image_editor => features/media_editor/story}/ui/widgets/story_editor_top_section.dart (95%) rename lib/src/{core/pro_image_editor => features/media_editor/story}/ui/widgets/story_mention_picker_sheet.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/models/video_editor_result.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/services/audio_helper_service.dart (99%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/services/audio_source_resolver.dart (97%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/services/video_export_planner.dart (96%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/services/video_timing_export_service.dart (100%) rename lib/src/{core/pro_video_editor/ui/widgets/common => features/media_editor/video/ui/config}/video_editor_configs_builder.dart (88%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/controllers/video_editor_export_controller.dart (81%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/controllers/video_editor_media_session.dart (97%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/controllers/video_editor_preview_asset_loader.dart (72%) rename lib/src/{core/pro_video_editor/ui/video_editor_grounded_page.dart => features/media_editor/video/ui/pages/video_editor_page.dart} (89%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/audio/audio_range_selection_overlay.dart (98%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/clip/clip_editor_bar.dart (88%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/clip/clips_editor_bar.dart (88%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/common/video_initializing_widget.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/common/video_progress_alert.dart (93%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/layout/video_editor_bottom_section.dart (94%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/layout/video_editor_header.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/layout/video_editor_regular_chrome.dart (91%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/layout/video_editor_reveal_layout.dart (99%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/layout/video_editor_toolbar.dart (96%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/layout/video_toolbar.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/player/video_player_widget.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/audio_timeline_track.dart (91%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/layer_reorder_controller.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/layer_timing_track.dart (91%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/primary_timeline_trim_overlay.dart (96%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/scrollable_timeline.dart (96%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/story_video_timeline_controls.dart (92%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/timed_track_range.dart (98%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/timed_track_range_parts.dart (97%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/timeline_selection.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/timeline_selection_handle.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/timeline_subtrack_content.dart (100%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/timeline_visual_tracks.dart (98%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/video_timeline.dart (96%) rename lib/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/video_timeline_state.dart (100%) rename lib/src/{core/media => features/posting/utils}/create_media_actions.dart (82%) rename lib/src/{core/pro_video_editor/ui => features/sound}/controllers/audio_audition_controller.dart (98%) rename lib/src/{core/pro_video_editor/ui => features/sound}/controllers/audio_audition_playback.dart (100%) rename lib/src/{core/pro_video_editor => features/sound}/models/audio_audition_timing.dart (100%) rename lib/src/{core/pro_video_editor => features/sound}/models/sound_audio_track.dart (100%) rename lib/src/{core/pro_video_editor => features/sound}/providers/sound_picker_search_provider.dart (98%) rename lib/src/{core/pro_video_editor => features/sound}/providers/sound_picker_search_state.dart (100%) rename lib/src/{core/pro_video_editor/ui/widgets/audio => features/sound/ui/widgets}/audio_selection_bottom_sheet.dart (93%) rename lib/src/{core/pro_video_editor/ui/widgets/audio => features/sound/ui/widgets}/audio_track_list_section.dart (96%) rename lib/src/{core/pro_video_editor/ui/widgets/audio => features/sound/ui/widgets}/audio_waveform.dart (100%) rename lib/src/{core/pro_video_editor/ui/widgets/audio => features/sound/ui/widgets}/sound_artwork.dart (100%) rename lib/src/{core/pro_video_editor/ui/widgets/audio => features/sound/ui/widgets}/sound_picker_sheet_scaffold.dart (100%) rename test/src/core/{pro_video_editor/services/audio_waveform_extractor_test.dart => media_processing/video/pro_video_processing_service_test.dart} (86%) rename test/src/{core/pro_image_editor => features/media_editor/story}/story_mention_layer_test.dart (98%) rename test/src/{core/pro_image_editor => features/media_editor/story}/story_sticker_editor_test.dart (96%) rename test/src/{core/pro_image_editor => features/media_editor/story}/ui/widgets/story_editor_chrome_test.dart (96%) rename test/src/{core/pro_video_editor => features/media_editor/video}/services/audio_helper_service_test.dart (98%) rename test/src/{core/pro_video_editor => features/media_editor/video}/services/video_export_planner_test.dart (91%) rename test/src/{core/pro_video_editor => features/media_editor/video}/services/video_timing_export_service_test.dart (99%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/controllers/video_editor_media_session_test.dart (97%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/audio/audio_range_selection_overlay_test.dart (98%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/layout/video_editor_bottom_section_test.dart (93%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/layout/video_editor_reveal_layout_test.dart (98%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/layout/video_editor_toolbar_test.dart (94%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/scrollable_timeline_rendering_test.dart (96%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/scrollable_timeline_scrolling_test.dart (96%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/scrollable_timeline_selection_test.dart (92%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/scrollable_timeline_test.dart (99%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/scrollable_timeline_test_harness.dart (90%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/video_timeline_state_test.dart (96%) rename test/src/{core/pro_video_editor => features/media_editor/video}/ui/widgets/timeline/video_timeline_test.dart (74%) rename test/src/{core/pro_video_editor/ui => features/sound}/controllers/audio_audition_controller_test.dart (98%) rename test/src/{core/network/atproto/data/models/sound_models_test.dart => features/sound/models/sound_audio_track_test.dart} (99%) rename test/src/{core/pro_video_editor => features/sound}/providers/sound_picker_search_provider_test.dart (97%) rename test/src/{core/pro_video_editor/ui/widgets/audio => features/sound/ui/widgets}/audio_selection_bottom_sheet_test.dart (94%) rename test/src/{core/pro_video_editor/ui/widgets/audio => features/sound/ui/widgets}/sound_picker_sheet_scaffold_test.dart (92%) diff --git a/lib/src/core/design_system/components/organisms/side_action_bar.dart b/lib/src/core/design_system/components/organisms/side_action_bar.dart index 57ba7bd..18cfca6 100644 --- a/lib/src/core/design_system/components/organisms/side_action_bar.dart +++ b/lib/src/core/design_system/components/organisms/side_action_bar.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:spark/src/core/design_system/components/atoms/icons.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/sound_artwork.dart'; class SparkSideActionBar extends StatefulWidget { const SparkSideActionBar({ @@ -20,7 +19,7 @@ class SparkSideActionBar extends StatefulWidget { this.shareCount, this.isLiked = false, this.isReposted = false, - this.soundCover, + this.soundArtwork, }); final VoidCallback? onLike; @@ -39,7 +38,7 @@ class SparkSideActionBar extends StatefulWidget { final bool isLiked; final bool isReposted; - final String? soundCover; + final Widget? soundArtwork; @override State createState() => _SparkSideActionBarState(); @@ -100,10 +99,10 @@ class _SparkSideActionBarState extends State { ]); } - if (widget.onSoundTap != null) { + if (widget.onSoundTap != null && widget.soundArtwork != null) { children.addAll([ const SizedBox(height: 13), - _SoundItem(cover: widget.soundCover, onTap: widget.onSoundTap), + _SoundItem(onTap: widget.onSoundTap, child: widget.soundArtwork!), ]); } @@ -222,26 +221,17 @@ class _ActionItemState extends State<_ActionItem> } class _SoundItem extends StatelessWidget { - const _SoundItem({this.cover, this.onTap}); + const _SoundItem({required this.child, this.onTap}); - final String? cover; + final Widget child; final VoidCallback? onTap; @override Widget build(BuildContext context) { - const albumSize = 35.0; - return GestureDetector( behavior: HitTestBehavior.opaque, onTap: onTap, - child: ClipOval( - child: SoundArtwork( - imageUrl: cover, - size: albumSize, - borderRadius: albumSize / 2, - backgroundColor: Colors.grey.shade800, - ), - ), + child: ClipOval(child: child), ); } } diff --git a/lib/src/core/di/service_locator.dart b/lib/src/core/di/service_locator.dart index b5a4d22..9e933e0 100644 --- a/lib/src/core/di/service_locator.dart +++ b/lib/src/core/di/service_locator.dart @@ -2,6 +2,8 @@ import 'package:get_it/get_it.dart'; import 'package:spark/src/core/auth/data/repositories/auth_repository_impl.dart'; import 'package:spark/src/core/auth/data/repositories/onboarding_repository.dart'; import 'package:spark/src/core/auth/data/repositories/onboarding_repository_impl.dart'; +import 'package:spark/src/core/media_processing/video/pro_video_processing_service.dart'; +import 'package:spark/src/core/media_processing/video/video_processing_service.dart'; import 'package:spark/src/core/network/atproto/atproto.dart'; import 'package:spark/src/core/network/atproto/data/repositories/actor_repository_impl.dart'; import 'package:spark/src/core/network/atproto/data/repositories/graph_repository_impl.dart'; @@ -17,8 +19,6 @@ import 'package:spark/src/core/network/messages/data/repository/messages_reposit import 'package:spark/src/core/network/messages/data/repository/messages_repository_xrpc.dart'; import 'package:spark/src/core/network/xrpc/service_auth_helper.dart'; import 'package:spark/src/core/notifications/push_notification_service.dart'; -import 'package:spark/src/core/pro_video_editor/pro_video_editor_repository.dart'; -import 'package:spark/src/core/pro_video_editor/pro_video_editor_repository_impl.dart'; import 'package:spark/src/core/storage/cache/download_manager_interface.dart'; import 'package:spark/src/core/storage/storage.dart'; import 'package:spark/src/core/ui/theme/data/repositories/theme_repository.dart'; @@ -96,8 +96,8 @@ Future initServiceLocator({ authRepository: sl(), ), ) - ..registerSingleton( - const ProVideoEditorRepositoryImpl(), + ..registerSingleton( + const ProVideoProcessingService(), ) ..registerSingleton( NotificationRepositoryImpl(sl()), diff --git a/lib/src/core/media_processing/video/pro_video_processing_service.dart b/lib/src/core/media_processing/video/pro_video_processing_service.dart new file mode 100644 index 0000000..f244208 --- /dev/null +++ b/lib/src/core/media_processing/video/pro_video_processing_service.dart @@ -0,0 +1,174 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:image_picker/image_picker.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:pro_image_editor/pro_image_editor.dart'; +import 'package:pro_video_editor/pro_video_editor.dart'; +import 'package:spark/src/core/media_processing/video/video_processing_service.dart'; + +class ProVideoProcessingService implements VideoProcessingService { + const ProVideoProcessingService(); + + @override + Future getMetadata(EditorVideo video) { + return ProVideoEditor.instance.getMetadata(video); + } + + @override + Future> getThumbnails(ThumbnailConfigs configs) { + return ProVideoEditor.instance.getThumbnails(configs); + } + + @override + Future renderToFile(String outputPath, VideoRenderData data) { + return ProVideoEditor.instance.renderVideoToFile(outputPath, data); + } + + @override + Stream progressFor(String taskId) { + return ProVideoEditor.instance.progressStreamById(taskId); + } + + @override + Future> extractVideoWaveform(EditorVideo video) async { + final path = await _videoPath(video); + if (path == null) return const []; + return extractWaveformFromPath(path); + } + + @override + Future> extractAudioWaveform( + EditorAudio audio, { + String preferredExtension = 'mp3', + }) async { + final path = await _audioPath( + audio, + preferredExtension: preferredExtension, + ); + if (path == null) return const []; + return extractWaveformFromPath(path); + } + + @override + Future> extractWaveformFromPath(String path) async { + try { + final waveform = await ProVideoEditor.instance.getWaveform( + WaveformConfigs( + video: EditorVideo.file(path), + resolution: WaveformResolution.medium, + ), + ); + final left = waveform.leftChannel; + final right = waveform.rightChannel; + final samples = right == null + ? left.toList(growable: false) + : List.generate( + left.length > right.length ? left.length : right.length, + (index) { + final leftSample = index < left.length ? left[index] : 0.0; + final rightSample = index < right.length ? right[index] : 0.0; + return leftSample.abs() > rightSample.abs() + ? leftSample + : rightSample; + }, + growable: false, + ); + return _normalizeWaveform(samples); + } catch (_) { + return const []; + } + } + + @override + Future stitchSegments(List segments) async { + if (segments.isEmpty) { + throw ArgumentError.value( + segments, + 'segments', + 'At least one segment is required', + ); + } + + final directory = await getTemporaryDirectory(); + final timestamp = DateTime.now().millisecondsSinceEpoch; + final outputPath = '${directory.path}/spark_recording_$timestamp.mp4'; + + if (segments.length == 1) { + final copiedFile = await File(segments.first.path).copy(outputPath); + return XFile( + copiedFile.path, + mimeType: 'video/mp4', + name: copiedFile.uri.pathSegments.last, + ); + } + + await renderToFile( + outputPath, + VideoRenderData( + videoSegments: segments + .map( + (segment) => VideoSegment(video: EditorVideo.file(segment.path)), + ) + .toList(), + ), + ); + return XFile( + outputPath, + mimeType: 'video/mp4', + name: outputPath.split('/').last, + ); + } + + List _normalizeWaveform(List samples) { + if (samples.isEmpty) return const []; + final maximum = samples + .map((sample) => sample.abs()) + .reduce((left, right) => left > right ? left : right); + if (maximum == 0) return List.filled(samples.length, 0.5); + return samples + .map((sample) => (sample.abs() / maximum).clamp(0.0, 1.0)) + .toList(growable: false); + } + + Future _videoPath(EditorVideo video) async { + if (video.file != null) return video.file!.path; + if (video.networkUrl != null) { + return _downloadToTemp(video.networkUrl!, 'temp_video.mp4'); + } + if (video.byteArray != null) { + return _writeBytesToTemp(video.byteArray!, 'temp_video.mp4'); + } + return null; + } + + Future _audioPath( + EditorAudio audio, { + required String preferredExtension, + }) async { + if (audio.hasFile) return audio.file!.path; + final filename = 'temp_audio.$preferredExtension'; + if (audio.hasNetworkUrl) { + return _downloadToTemp(audio.networkUrl!, filename); + } + if (audio.hasBytes) { + return _writeBytesToTemp(audio.bytes!, filename); + } + return null; + } + + Future _downloadToTemp(String url, String filename) async { + final file = await fetchVideoToFile( + url, + '${(await getTemporaryDirectory()).path}/$filename', + ); + return file.path; + } + + Future _writeBytesToTemp(Uint8List bytes, String filename) async { + final directory = await getTemporaryDirectory(); + final file = File('${directory.path}/$filename'); + await file.writeAsBytes(bytes); + return file.path; + } +} diff --git a/lib/src/core/media_processing/video/video_processing_service.dart b/lib/src/core/media_processing/video/video_processing_service.dart new file mode 100644 index 0000000..860e4eb --- /dev/null +++ b/lib/src/core/media_processing/video/video_processing_service.dart @@ -0,0 +1,26 @@ +import 'dart:typed_data'; + +import 'package:image_picker/image_picker.dart'; +import 'package:pro_image_editor/pro_image_editor.dart'; +import 'package:pro_video_editor/pro_video_editor.dart'; + +abstract interface class VideoProcessingService { + Future getMetadata(EditorVideo video); + + Future> getThumbnails(ThumbnailConfigs configs); + + Future renderToFile(String outputPath, VideoRenderData data); + + Stream progressFor(String taskId); + + Future> extractVideoWaveform(EditorVideo video); + + Future> extractAudioWaveform( + EditorAudio audio, { + String preferredExtension = 'mp3', + }); + + Future> extractWaveformFromPath(String path); + + Future stitchSegments(List segments); +} diff --git a/lib/src/core/pro_image_editor/utils/story_image_cropper.dart b/lib/src/core/pro_image_editor/utils/story_image_cropper.dart deleted file mode 100644 index 604fb8c..0000000 --- a/lib/src/core/pro_image_editor/utils/story_image_cropper.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'dart:io'; -import 'dart:ui' as ui; - -import 'package:flutter/material.dart'; -import 'package:path_provider/path_provider.dart'; - -/// Utility to crop images to story aspect ratio (9:16). -class StoryImageCropper { - StoryImageCropper._(); - - /// Target story aspect ratio (9:16 = 0.5625). - static const double targetAspectRatio = 9 / 16; - - /// Crops the given image file to 9:16 aspect ratio (center crop). - /// - /// Returns the path to the cropped image file. - static Future cropToStoryAspectRatio(File imageFile) async { - // Decode the image - final bytes = await imageFile.readAsBytes(); - final codec = await ui.instantiateImageCodec(bytes); - final frame = await codec.getNextFrame(); - final image = frame.image; - - final originalWidth = image.width.toDouble(); - final originalHeight = image.height.toDouble(); - final originalAspectRatio = originalWidth / originalHeight; - - // Calculate crop area (center crop to 9:16) - double cropWidth; - double cropHeight; - double cropX; - double cropY; - - if (originalAspectRatio > targetAspectRatio) { - // Image is wider than 9:16 - crop horizontally - cropHeight = originalHeight; - cropWidth = originalHeight * targetAspectRatio; - cropX = (originalWidth - cropWidth) / 2; - cropY = 0; - } else { - // Image is taller than 9:16 - crop vertically - cropWidth = originalWidth; - cropHeight = originalWidth / targetAspectRatio; - cropX = 0; - cropY = (originalHeight - cropHeight) / 2; - } - - // Create a picture recorder to draw the cropped image - final recorder = ui.PictureRecorder(); - final canvas = Canvas(recorder); - - // Draw only the cropped portion at native pixel size. - final srcRect = Rect.fromLTWH(cropX, cropY, cropWidth, cropHeight); - final dstRect = Rect.fromLTWH(0, 0, cropWidth, cropHeight); - - canvas.drawImageRect(image, srcRect, dstRect, Paint()); - - // Convert to image - final picture = recorder.endRecording(); - final croppedImage = await picture.toImage( - cropWidth.round(), - cropHeight.round(), - ); - - // Encode to PNG - final byteData = await croppedImage.toByteData( - format: ui.ImageByteFormat.png, - ); - final pngBytes = byteData!.buffer.asUint8List(); - - // Save to temp file - final tempDir = await getTemporaryDirectory(); - final timestamp = DateTime.now().millisecondsSinceEpoch; - final croppedFile = File('${tempDir.path}/story_cropped_$timestamp.png'); - await croppedFile.writeAsBytes(pngBytes); - - // Clean up - image.dispose(); - croppedImage.dispose(); - - return croppedFile; - } - - /// Checks if an image needs to be cropped to 9:16 aspect ratio. - static Future needsCropping(File imageFile) async { - final bytes = await imageFile.readAsBytes(); - final codec = await ui.instantiateImageCodec(bytes); - final frame = await codec.getNextFrame(); - final image = frame.image; - - final aspectRatio = image.width / image.height; - image.dispose(); - - // Allow small tolerance for floating point comparison - return (aspectRatio - targetAspectRatio).abs() > 0.01; - } -} diff --git a/lib/src/core/pro_video_editor/pro_video_editor_repository.dart b/lib/src/core/pro_video_editor/pro_video_editor_repository.dart deleted file mode 100644 index 15d5d45..0000000 --- a/lib/src/core/pro_video_editor/pro_video_editor_repository.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'dart:typed_data'; - -import 'package:flutter/widgets.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:pro_video_editor/pro_video_editor.dart'; -import 'package:spark/src/core/pro_image_editor/models/story_image_editor_result.dart'; -import 'package:spark/src/core/pro_video_editor/models/video_editor_result.dart'; - -/// Abstraction over the pro_video_editor plugin. -/// -/// Focuses on video processing tasks provided by pro_video_editor. UI concerns -/// (Grounded Design widgets) are exposed separately via helper builders. -abstract class ProVideoEditorRepository { - /// Retrieve metadata for a given video source. - Future getMetadata(EditorVideo video); - - /// Generate thumbnails at specific timestamps. - Future> getThumbnails(ThumbnailConfigs configs); - - /// Generate key frames (typically faster on Android) for the given video. - Future> getKeyFrames(KeyFramesConfigs configs); - - /// Render a video and return the bytes. Use for small/short videos. - Future renderVideo(VideoRenderData model); - - /// Render a video directly to a file path to avoid RAM pressure. - Future renderVideoToFile(String outputPath, VideoRenderData model); - - /// Stream progress updates for a given render task id. - /// - /// The caller is responsible to pass the same [VideoRenderData.id] or - /// [ThumbnailConfigs.id]/[KeyFramesConfigs.id]. - Stream progressStream(); - - /// Concatenates recorded video [segments] into a single file for editing. - /// - /// When only one segment is provided, returns a copied file so the caller can - /// clean up the original temporary capture safely. - Future stitchVideoSegments(List segments); - - /// Opens the ProImageEditor UI to edit the given [source] image and returns - /// an edited image file when the editor is closed. - /// - /// Returns `null` if the user cancels without completing an edit. - Future openImageEditor(BuildContext context, XFile source); - - /// Opens the ProVideoEditor UI to edit the given [video] and returns - /// the edited video with optional audio metadata when the editor is closed. - /// - /// Returns `null` if the user cancels without completing an edit. - Future openVideoEditor( - BuildContext context, - EditorVideo video, { - AudioTrack? initialAudioTrack, - }); - - /// Opens the Story Image Editor with a fixed 9:16 aspect ratio canvas. - /// - /// The [source] image is displayed in the editor with story-appropriate - /// tools (text, paint, stickers, emoji, filter, blur - NO crop/rotate). - /// - /// Returns `null` if the user cancels without completing the edit. - Future openStoryImageEditor( - BuildContext context, - XFile source, - ); - - /// Opens a blank canvas Story Image Editor (1080x1920). - /// - /// Optionally adds [backgroundImage] as a movable layer on the canvas. - /// This gives more flexibility for positioning the image. - /// - /// Returns `null` if the user cancels without completing the edit. - Future openStoryBlankCanvasEditor( - BuildContext context, { - XFile? backgroundImage, - Color backgroundColor = const Color(0xFF000000), - }); - - /// Opens the Story Video Editor with story-appropriate tools. - /// - /// Uses the same limited toolset as the story image editor - /// (paint, text, filter, blur, emoji, stickers - NO crop/rotate/tune). - /// - /// Returns `null` if the user cancels without completing the edit. - Future openStoryVideoEditor( - BuildContext context, - EditorVideo video, { - AudioTrack? initialAudioTrack, - }); -} diff --git a/lib/src/core/pro_video_editor/pro_video_editor_repository_impl.dart b/lib/src/core/pro_video_editor/pro_video_editor_repository_impl.dart deleted file mode 100644 index b9446bf..0000000 --- a/lib/src/core/pro_video_editor/pro_video_editor_repository_impl.dart +++ /dev/null @@ -1,174 +0,0 @@ -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:pro_video_editor/pro_video_editor.dart'; -import 'package:spark/src/core/pro_image_editor/models/story_image_editor_result.dart'; -import 'package:spark/src/core/pro_image_editor/ui/story_image_editor_page.dart'; -import 'package:spark/src/core/pro_video_editor/models/video_editor_result.dart'; -import 'package:spark/src/core/pro_video_editor/pro_video_editor_repository.dart'; -import 'package:spark/src/core/pro_video_editor/ui/video_editor_grounded_page.dart'; - -/// Default implementation that directly delegates to ProVideoEditor.instance. -class ProVideoEditorRepositoryImpl implements ProVideoEditorRepository { - const ProVideoEditorRepositoryImpl(); - - @override - Future getMetadata(EditorVideo video) { - return ProVideoEditor.instance.getMetadata(video); - } - - @override - Future> getThumbnails(ThumbnailConfigs configs) { - return ProVideoEditor.instance.getThumbnails(configs); - } - - @override - Future> getKeyFrames(KeyFramesConfigs configs) { - return ProVideoEditor.instance.getKeyFrames(configs); - } - - @override - Future renderVideo(VideoRenderData model) { - return ProVideoEditor.instance.renderVideo(model); - } - - @override - Future renderVideoToFile(String outputPath, VideoRenderData model) { - return ProVideoEditor.instance.renderVideoToFile(outputPath, model); - } - - @override - Stream progressStream() => - ProVideoEditor.instance.progressStream; - - @override - Future stitchVideoSegments(List segments) async { - if (segments.isEmpty) { - throw ArgumentError.value( - segments, - 'segments', - 'At least one segment is required', - ); - } - - final dir = await getTemporaryDirectory(); - final timestamp = DateTime.now().millisecondsSinceEpoch; - final outputPath = '${dir.path}/spark_recording_$timestamp.mp4'; - - if (segments.length == 1) { - final copiedFile = await File(segments.first.path).copy(outputPath); - return XFile( - copiedFile.path, - mimeType: 'video/mp4', - name: copiedFile.uri.pathSegments.last, - ); - } - - await ProVideoEditor.instance.renderVideoToFile( - outputPath, - VideoRenderData( - videoSegments: segments - .map( - (segment) => VideoSegment(video: EditorVideo.file(segment.path)), - ) - .toList(), - ), - ); - - return XFile( - outputPath, - mimeType: 'video/mp4', - name: outputPath.split('/').last, - ); - } - - @override - Future openImageEditor(BuildContext context, XFile source) async { - return Navigator.of(context).push( - MaterialPageRoute( - builder: (ctx) { - return ProImageEditor.file( - File(source.path), - callbacks: ProImageEditorCallbacks( - onImageEditingComplete: (bytes) async { - final dir = await getTemporaryDirectory(); - final timestamp = DateTime.now().millisecondsSinceEpoch; - final filename = 'spark_edited_$timestamp.jpg'; - final file = File('${dir.path}/$filename'); - await file.writeAsBytes(bytes, flush: true); - if (ctx.mounted) { - Navigator.of(ctx).pop( - XFile(file.path, mimeType: 'image/jpeg', name: filename), - ); - } - }, - ), - ); - }, - ), - ); - } - - @override - Future openVideoEditor( - BuildContext context, - EditorVideo video, { - AudioTrack? initialAudioTrack, - }) async { - return Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => VideoEditorGroundedPage( - video: video, - initialAudioTrack: initialAudioTrack, - ), - ), - ); - } - - @override - Future openStoryImageEditor( - BuildContext context, - XFile source, - ) { - return StoryBlankCanvasEditorPage.open( - context, - backgroundImage: File(source.path), - ); - } - - @override - Future openStoryBlankCanvasEditor( - BuildContext context, { - XFile? backgroundImage, - Color backgroundColor = const Color(0xFF000000), - }) { - return StoryBlankCanvasEditorPage.open( - context, - backgroundImage: backgroundImage != null - ? File(backgroundImage.path) - : null, - backgroundColor: backgroundColor, - ); - } - - @override - Future openStoryVideoEditor( - BuildContext context, - EditorVideo video, { - AudioTrack? initialAudioTrack, - }) async { - return Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => VideoEditorGroundedPage( - video: video, - storyMode: true, - initialAudioTrack: initialAudioTrack, - ), - ), - ); - } -} diff --git a/lib/src/core/pro_video_editor/services/audio_waveform_extractor.dart b/lib/src/core/pro_video_editor/services/audio_waveform_extractor.dart deleted file mode 100644 index 23c7966..0000000 --- a/lib/src/core/pro_video_editor/services/audio_waveform_extractor.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:pro_video_editor/pro_video_editor.dart'; - -/// Extracts waveform data from video or audio files. -class AudioWaveformExtractor { - AudioWaveformExtractor._(); - - static final instance = AudioWaveformExtractor._(); - - /// Extracts waveform samples from a video file. - /// - /// Returns normalized waveform data as a list of doubles (0.0 to 1.0). - Future> extractFromVideo(EditorVideo video) async { - final path = await _getVideoPath(video); - if (path == null) return []; - return extractFromPath(path); - } - - /// Extracts waveform samples from an audio source. - Future> extractFromAudio( - EditorAudio audio, { - String preferredExtension = 'mp3', - }) async { - final path = await _getAudioPath( - audio, - preferredExtension: preferredExtension, - ); - if (path == null) return []; - return extractFromPath(path); - } - - /// Extracts waveform samples from a file path. - Future> extractFromPath(String path) async { - try { - final waveform = await ProVideoEditor.instance.getWaveform( - WaveformConfigs( - video: EditorVideo.file(path), - resolution: WaveformResolution.medium, - ), - ); - final leftChannel = waveform.leftChannel; - final rightChannel = waveform.rightChannel; - final samples = rightChannel == null - ? leftChannel.toList(growable: false) - : List.generate( - leftChannel.length > rightChannel.length - ? leftChannel.length - : rightChannel.length, - (index) { - final left = index < leftChannel.length - ? leftChannel[index] - : 0.0; - final right = index < rightChannel.length - ? rightChannel[index] - : 0.0; - return left.abs() > right.abs() ? left : right; - }, - growable: false, - ); - return _normalizeWaveform(samples); - } catch (_) { - return []; - } - } - - /// Normalizes waveform data to values between 0.0 and 1.0. - List _normalizeWaveform(List data) { - if (data.isEmpty) return []; - - final maxValue = data.map((e) => e.abs()).reduce((a, b) => a > b ? a : b); - if (maxValue == 0) return List.filled(data.length, 0.5); - - return data.map((e) => (e.abs() / maxValue).clamp(0.0, 1.0)).toList(); - } - - Future _getVideoPath(EditorVideo video) async { - if (video.file != null) return video.file!.path; - - if (video.networkUrl != null) { - return _downloadToTemp(video.networkUrl!, 'temp_video.mp4'); - } - - if (video.byteArray != null) { - return _writeBytesToTemp(video.byteArray!, 'temp_video.mp4'); - } - - return null; - } - - Future _getAudioPath( - EditorAudio audio, { - required String preferredExtension, - }) async { - if (audio.hasFile) return audio.file!.path; - - final filename = 'temp_audio.$preferredExtension'; - if (audio.hasNetworkUrl) { - return _downloadToTemp(audio.networkUrl!, filename); - } - - if (audio.hasBytes) { - return _writeBytesToTemp(audio.bytes!, filename); - } - - return null; - } - - Future _downloadToTemp(String url, String filename) async { - final file = await fetchVideoToFile( - url, - '${(await getTemporaryDirectory()).path}/$filename', - ); - return file.path; - } - - Future _writeBytesToTemp(Uint8List bytes, String filename) async { - final tempDir = await getTemporaryDirectory(); - final file = File('${tempDir.path}/$filename'); - await file.writeAsBytes(bytes); - return file.path; - } -} diff --git a/lib/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bottom_action_bar.dart b/lib/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bottom_action_bar.dart deleted file mode 100644 index d480014..0000000 --- a/lib/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bottom_action_bar.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/design_system/tokens/colors.dart'; - -/// A bottom action bar widget for the crop/rotate editor with done, close, undo, and redo buttons. -class CropRotateEditorBottomActionBar extends StatelessWidget { - /// Creates a [CropRotateEditorBottomActionBar]. - const CropRotateEditorBottomActionBar({ - required this.configs, - required this.done, - required this.close, - this.undo, - this.redo, - this.enableUndo = false, - this.enableRedo = false, - super.key, - }); - - /// Configuration settings for the image editor. - final ProImageEditorConfigs configs; - - /// Function to handle completion of editing. - final VoidCallback done; - - /// Function to handle closing the editor. - final VoidCallback close; - - /// Function to handle undo action. - final VoidCallback? undo; - - /// Function to handle redo action. - final VoidCallback? redo; - - /// Boolean flag to enable or disable the undo action. - final bool enableUndo; - - /// Boolean flag to enable or disable the redo action. - final bool enableRedo; - - @override - Widget build(BuildContext context) { - final foreGroundColor = configs.mainEditor.style.appBarColor; - - return Container( - padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 12), - color: AppColors.grey800, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - IconButton( - tooltip: configs.i18n.cancel, - onPressed: close, - icon: Icon( - configs.mainEditor.icons.closeEditor, - color: foreGroundColor, - ), - ), - if (redo != null) - Row( - children: [ - IconButton( - tooltip: configs.i18n.undo, - onPressed: enableUndo ? undo : null, - icon: Icon( - configs.mainEditor.icons.undoAction, - color: enableUndo - ? foreGroundColor - : foreGroundColor.withValues(alpha: 80), - ), - ), - const SizedBox(width: 3), - IconButton( - tooltip: configs.i18n.redo, - onPressed: enableRedo ? redo : null, - icon: Icon( - configs.mainEditor.icons.redoAction, - color: enableRedo - ? foreGroundColor - : foreGroundColor.withValues(alpha: 80), - ), - ), - ], - ), - IconButton( - tooltip: configs.i18n.done, - onPressed: done, - icon: Icon( - configs.mainEditor.icons.doneIcon, - color: foreGroundColor, - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/core/pro_video_editor/ui/widgets/player/preview_video.dart b/lib/src/core/pro_video_editor/ui/widgets/player/preview_video.dart deleted file mode 100644 index 779f854..0000000 --- a/lib/src/core/pro_video_editor/ui/widgets/player/preview_video.dart +++ /dev/null @@ -1,191 +0,0 @@ -import 'dart:math'; -import 'dart:ui'; - -import 'package:flutter/material.dart'; -import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:pro_video_editor/pro_video_editor.dart'; -import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:video_player/video_player.dart'; - -/// A widget that previews a video from a file path. -/// -/// Displays the video and optionally shows generation meta. -class PreviewVideo extends StatefulWidget { - const PreviewVideo({ - required this.filePath, - required this.generationTime, - super.key, - }); - - final String filePath; - final Duration generationTime; - - @override - State createState() => _PreviewVideoState(); -} - -class _PreviewVideoState extends State { - final _valueStyle = const TextStyle(fontStyle: FontStyle.italic); - - late Future _videoMetadata; - late final int _generationTime = widget.generationTime.inMilliseconds; - - VideoPlayerController? _videoController; - - @override - void initState() { - super.initState(); - - _videoMetadata = ProVideoEditor.instance.getMetadata( - EditorVideo.file(widget.filePath), - ); - _initializePlayer(); - } - - @override - void dispose() { - _videoController?.dispose(); - super.dispose(); - } - - Future _initializePlayer() async { - final controller = VideoPlayerController.networkUrl( - Uri.file(widget.filePath), - ); - await controller.initialize(); - await controller.setLooping(false); - await controller.setVolume(1); - if (mounted) { - setState(() => _videoController = controller); - } else { - controller.dispose(); - } - } - - String _formatBytes(int bytes, [int decimals = 2]) { - if (bytes <= 0) return '0 B'; - const suffixes = ['B', 'KB', 'MB', 'GB', 'TB']; - final i = (log(bytes) / log(1024)).floor(); - final size = bytes / pow(1024, i); - return '${size.toStringAsFixed(decimals)} ${suffixes[i]}'; - } - - @override - Widget build(BuildContext context) { - final l10n = AppLocalizations.of(context); - return Scaffold( - appBar: AppBar(title: Text(l10n.pageTitleResult)), - body: LayoutBuilder( - builder: (context, constraints) { - return Stack( - children: [ - Positioned.fill(child: _buildVideoPlayer(constraints)), - _buildGenerationInfos(l10n), - ], - ); - }, - ), - ); - } - - Widget _buildVideoPlayer(BoxConstraints constraints) { - return FutureBuilder( - future: _videoMetadata, - builder: (context, snapshot) { - if (_videoController == null || - !_videoController!.value.isInitialized) { - return const Center(child: CircularProgressIndicator()); - } - - final aspectRatio = snapshot.data?.resolution.aspectRatio ?? 1; - - final maxWidth = constraints.maxWidth; - final maxHeight = constraints.maxHeight; - - var width = maxWidth; - var height = width / aspectRatio; - - if (height > maxHeight) { - height = maxHeight; - width = height * aspectRatio; - } - - return Center( - child: SizedBox( - width: width, - height: height, - child: Hero( - tag: const ProImageEditorConfigs().heroTag, - child: VideoPlayer(_videoController!), - ), - ), - ); - }, - ); - } - - Widget _buildGenerationInfos(AppLocalizations l10n) { - const tableSpace = TableRow(children: [SizedBox(height: 3), SizedBox()]); - return Positioned( - top: 10, - left: 10, - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6), - child: Container( - decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.5), - borderRadius: BorderRadius.circular(7), - ), - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), - child: FutureBuilder( - future: _videoMetadata, - builder: (context, snapshot) { - final md = snapshot.data; - return Table( - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - columnWidths: const {0: IntrinsicColumnWidth()}, - children: [ - TableRow( - children: [ - Text(l10n.labelGenerationTime), - Text('${_generationTime}ms', style: _valueStyle), - ], - ), - tableSpace, - if (md != null) ...[ - TableRow( - children: [ - Text(l10n.labelDuration), - Text(md.duration.toString(), style: _valueStyle), - ], - ), - tableSpace, - TableRow( - children: [ - Text(l10n.labelSize), - Text(_formatBytes(md.fileSize), style: _valueStyle), - ], - ), - tableSpace, - TableRow( - children: [ - Text(l10n.labelResolution), - Text( - '${md.resolution.width.toInt()}x' - '${md.resolution.height.toInt()}', - style: _valueStyle, - ), - ], - ), - ], - ], - ); - }, - ), - ), - ), - ), - ); - } -} diff --git a/lib/src/core/pro_video_editor/ui/widgets/text/text_editor_font_scale_sheet.dart b/lib/src/core/pro_video_editor/ui/widgets/text/text_editor_font_scale_sheet.dart deleted file mode 100644 index d9e187f..0000000 --- a/lib/src/core/pro_video_editor/ui/widgets/text/text_editor_font_scale_sheet.dart +++ /dev/null @@ -1,166 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/design_system/tokens/colors.dart'; - -/// A modal bottom sheet for adjusting font scale in the text editor. -class TextEditorFontScaleSheet extends StatefulWidget { - /// Creates a [TextEditorFontScaleSheet]. - const TextEditorFontScaleSheet({ - required this.editor, - required this.configs, - required this.rebuildController, - super.key, - }); - - /// The text editor state. - final TextEditorState editor; - - /// Configuration settings for the editor. - final ProImageEditorConfigs configs; - - /// Stream controller for triggering UI updates. - final StreamController rebuildController; - - @override - State createState() => - _TextEditorFontScaleSheetState(); -} - -class _TextEditorFontScaleSheetState extends State { - late double _value; - late final double _presetValue; - late final TextEditorConfigs textEditorConfigs; - late final I18n i18n; - - @override - void initState() { - super.initState(); - _value = widget.editor.fontScale; - _presetValue = widget.editor.fontScale; - textEditorConfigs = widget.configs.textEditor; - i18n = widget.configs.i18n; - } - - void updateValue(double value) { - widget.editor.fontScale = value; - _value = value; - setState(() {}); - } - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Material( - color: Colors.transparent, - child: SingleChildScrollView( - physics: const ClampingScrollPhysics(), - child: Padding( - padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [_buildHeader(), _buildBody()], - ), - ), - ), - ), - ); - } - - Widget _buildHeader() { - final factorText = ' ${_value.toStringAsFixed(1)}x'; - - return Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 0, 4), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${i18n.textEditor.fontScale}$factorText', - style: - textEditorConfigs.style.fontSizeBottomSheetTitle ?? - const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - IconButton( - onPressed: () => Navigator.pop(context), - icon: const Icon(Icons.close, color: Colors.white), - ), - ], - ), - ); - } - - Widget _buildBody() { - return Row( - children: [ - Expanded( - child: Slider( - max: textEditorConfigs.maxFontScale, - min: textEditorConfigs.minFontScale, - divisions: - ((textEditorConfigs.maxFontScale - - textEditorConfigs.minFontScale) / - 0.1) - .round(), - value: _value, - onChanged: updateValue, - activeColor: AppColors.primary400, - ), - ), - const SizedBox(width: 8), - _buildResetButton(), - const SizedBox(width: 2), - ], - ); - } - - Widget _buildResetButton() { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 150), - child: _value != _presetValue - ? IconButton( - key: const ValueKey('reset-enabled'), - onPressed: () { - updateValue(_presetValue); - }, - icon: Icon( - textEditorConfigs.icons.resetFontScale, - color: Colors.white, - ), - ) - : IconButton( - key: const ValueKey('reset-disabled'), - color: Colors.transparent, - onPressed: null, - icon: Icon( - textEditorConfigs.icons.resetFontScale, - color: Colors.transparent, - ), - ), - ); - } -} - -/// Helper function to show the font scale bottom sheet. -void showTextEditorFontScaleSheet({ - required BuildContext context, - required TextEditorState editor, - required ProImageEditorConfigs configs, - required StreamController rebuildController, -}) { - showModalBottomSheet( - context: context, - backgroundColor: configs.textEditor.style.fontScaleBottomSheetBackground, - builder: (BuildContext context) => TextEditorFontScaleSheet( - editor: editor, - configs: configs, - rebuildController: rebuildController, - ), - ); -} diff --git a/lib/src/core/routing/app_router.dart b/lib/src/core/routing/app_router.dart index a525358..b31c34f 100644 --- a/lib/src/core/routing/app_router.dart +++ b/lib/src/core/routing/app_router.dart @@ -4,8 +4,6 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:pro_video_editor/pro_video_editor.dart'; import 'package:spark/src/core/auth/data/repositories/auth_repository.dart'; import 'package:spark/src/core/auth/data/repositories/onboarding_repository.dart'; import 'package:spark/src/core/network/atproto/atproto.dart'; @@ -157,10 +155,6 @@ class AppRouter extends RootStackRouter { AutoRoute(page: VideoReviewRoute.page, path: '/video-review'), AutoRoute(page: ImageReviewRoute.page, path: '/image-review'), AutoRoute(page: StoryPostRoute.page, path: '/story-post'), - AutoRoute( - page: VideoEditorGroundedRoute.page, - path: '/video-editor-grounded', - ), AutoRoute(page: RecordingRoute.page, path: '/recording'), // Stories pages diff --git a/lib/src/core/routing/pages.dart b/lib/src/core/routing/pages.dart index fe481c2..19d9243 100644 --- a/lib/src/core/routing/pages.dart +++ b/lib/src/core/routing/pages.dart @@ -1,4 +1,3 @@ -export 'package:spark/src/core/pro_video_editor/ui/video_editor_grounded_page.dart'; export 'package:spark/src/features/auth/ui/pages/auth_prompt_page.dart'; export 'package:spark/src/features/auth/ui/pages/auth_recovery_page.dart'; export 'package:spark/src/features/auth/ui/pages/login_page.dart'; diff --git a/lib/src/features/feed/ui/widgets/action_buttons/side_action_bar.dart b/lib/src/features/feed/ui/widgets/action_buttons/side_action_bar.dart index 9c9fbc0..1161e9f 100644 --- a/lib/src/features/feed/ui/widgets/action_buttons/side_action_bar.dart +++ b/lib/src/features/feed/ui/widgets/action_buttons/side_action_bar.dart @@ -22,6 +22,7 @@ import 'package:spark/src/features/feed/providers/repost_post.dart'; import 'package:spark/src/features/feed/ui/widgets/action_buttons/share_panel.dart'; import 'package:spark/src/features/feed/ui/widgets/post_likes_sheet.dart'; import 'package:spark/src/features/profile/providers/profile_feed_provider.dart'; +import 'package:spark/src/features/sound/ui/widgets/sound_artwork.dart'; class SideActionBar extends ConsumerStatefulWidget { const SideActionBar({ @@ -441,7 +442,14 @@ class SideActionBarState extends ConsumerState { shareCount: widget.shareCount, isLiked: _isLiked, isReposted: _isReposted, - soundCover: currentPost.sound?.coverArt.toString(), + soundArtwork: currentPost.sound == null + ? null + : SoundArtwork( + imageUrl: currentPost.sound?.coverArt.toString(), + size: 35, + borderRadius: 17.5, + backgroundColor: Colors.grey.shade800, + ), ); } } diff --git a/lib/src/features/feed/ui/widgets/feed/feeds_bar.dart b/lib/src/features/feed/ui/widgets/feed/feeds_bar.dart index 3318fa6..03a8263 100644 --- a/lib/src/features/feed/ui/widgets/feed/feeds_bar.dart +++ b/lib/src/features/feed/ui/widgets/feed/feeds_bar.dart @@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; import 'package:spark/src/core/design_system/components/molecules/feed_tag_list.dart'; import 'package:spark/src/core/design_system/templates/feeds_bar_template.dart'; -import 'package:spark/src/core/media/create_media_actions.dart'; +import 'package:spark/src/features/posting/utils/create_media_actions.dart'; import 'package:spark/src/core/network/atproto/data/models/feed_models.dart'; import 'package:spark/src/features/feed/providers/feed_refresh_trigger_provider.dart'; import 'package:spark/src/features/settings/providers/settings_provider.dart'; diff --git a/lib/src/features/feed/ui/widgets/post/post_media_viewer.dart b/lib/src/features/feed/ui/widgets/post/post_media_viewer.dart index cbf641d..30a98f2 100644 --- a/lib/src/features/feed/ui/widgets/post/post_media_viewer.dart +++ b/lib/src/features/feed/ui/widgets/post/post_media_viewer.dart @@ -5,7 +5,7 @@ import 'package:spark/src/core/design_system/tokens/colors.dart'; import 'package:spark/src/core/media/media_playback_gate.dart'; import 'package:spark/src/core/network/atproto/data/models/feed_models.dart'; import 'package:spark/src/core/network/atproto/data/models/feed_video_aspect_ratio.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; import 'package:spark/src/features/feed/ui/widgets/images/image_carousel.dart'; import 'package:spark/src/features/feed/ui/widgets/post/static_media_sound_player.dart'; import 'package:spark/src/features/feed/ui/widgets/videos/video_player.dart'; diff --git a/lib/src/features/media_editor/canvas/ui/pages/post_image_editor_page.dart b/lib/src/features/media_editor/canvas/ui/pages/post_image_editor_page.dart new file mode 100644 index 0000000..820c666 --- /dev/null +++ b/lib/src/features/media_editor/canvas/ui/pages/post_image_editor_page.dart @@ -0,0 +1,40 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:pro_image_editor/pro_image_editor.dart'; + +class PostImageEditorPage extends StatelessWidget { + const PostImageEditorPage({required this.source, super.key}); + + final XFile source; + + static Future open(BuildContext context, XFile source) { + return Navigator.of(context).push( + MaterialPageRoute(builder: (_) => PostImageEditorPage(source: source)), + ); + } + + Future _complete(BuildContext context, List bytes) async { + final directory = await getTemporaryDirectory(); + final timestamp = DateTime.now().millisecondsSinceEpoch; + final filename = 'spark_edited_$timestamp.jpg'; + final file = File('${directory.path}/$filename'); + await file.writeAsBytes(bytes, flush: true); + if (!context.mounted) return; + Navigator.of( + context, + ).pop(XFile(file.path, mimeType: 'image/jpeg', name: filename)); + } + + @override + Widget build(BuildContext context) { + return ProImageEditor.file( + File(source.path), + callbacks: ProImageEditorCallbacks( + onImageEditingComplete: (bytes) => _complete(context, bytes), + ), + ); + } +} diff --git a/lib/src/core/pro_video_editor/ui/widgets/blur/blur_editor_bar.dart b/lib/src/features/media_editor/canvas/ui/widgets/blur/blur_editor_bar.dart similarity index 91% rename from lib/src/core/pro_video_editor/ui/widgets/blur/blur_editor_bar.dart rename to lib/src/features/media_editor/canvas/ui/widgets/blur/blur_editor_bar.dart index c423d39..2746b49 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/blur/blur_editor_bar.dart +++ b/lib/src/features/media_editor/canvas/ui/widgets/blur/blur_editor_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/text_editor_bottom_action_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart'; /// A custom blur editor bottom bar widget for video editor. /// @@ -42,7 +42,7 @@ class BlurEditorBar extends StatelessWidget { activeColor: AppColors.primary400, ), ), - TextEditorBottomActionBar( + EditorBottomActionBar( configs: configs, done: editor.done, close: editor.close, diff --git a/lib/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart b/lib/src/features/media_editor/canvas/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart similarity index 97% rename from lib/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart rename to lib/src/features/media_editor/canvas/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart index f2f79e7..83a18c6 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart +++ b/lib/src/features/media_editor/canvas/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/features/crop_rotate_editor/widgets/crop_aspect_ratio_button.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bottom_action_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart'; /// A custom crop/rotate editor bottom bar widget for video editor. /// @@ -63,7 +63,7 @@ class _CropRotateEditorBarState extends State { mainAxisSize: MainAxisSize.min, children: [ _buildFunctions(), - CropRotateEditorBottomActionBar( + EditorBottomActionBar( configs: widget.configs, done: widget.editor.done, close: widget.editor.close, diff --git a/lib/src/core/pro_video_editor/ui/widgets/common/text_editor_bottom_action_bar.dart b/lib/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart similarity index 92% rename from lib/src/core/pro_video_editor/ui/widgets/common/text_editor_bottom_action_bar.dart rename to lib/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart index 0813ab2..dd3ad80 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/common/text_editor_bottom_action_bar.dart +++ b/lib/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart @@ -2,10 +2,9 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -/// A bottom action bar widget for the text editor with done and close buttons. -class TextEditorBottomActionBar extends StatelessWidget { - /// Creates a [TextEditorBottomActionBar]. - const TextEditorBottomActionBar({ +/// Shared completion controls for canvas sub-editors. +class EditorBottomActionBar extends StatelessWidget { + const EditorBottomActionBar({ required this.configs, required this.done, required this.close, diff --git a/lib/src/core/pro_video_editor/ui/widgets/filter/filter_editor_bar.dart b/lib/src/features/media_editor/canvas/ui/widgets/filter/filter_editor_bar.dart similarity index 96% rename from lib/src/core/pro_video_editor/ui/widgets/filter/filter_editor_bar.dart rename to lib/src/features/media_editor/canvas/ui/widgets/filter/filter_editor_bar.dart index 0539765..ba7a89a 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/filter/filter_editor_bar.dart +++ b/lib/src/features/media_editor/canvas/ui/widgets/filter/filter_editor_bar.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/core/utils/size_utils.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/text_editor_bottom_action_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart'; /// A custom filter editor bottom bar widget for video editor. /// @@ -58,7 +58,7 @@ class _FilterEditorBarState extends State { mainAxisSize: MainAxisSize.min, children: [ _buildFunctions(), - TextEditorBottomActionBar( + EditorBottomActionBar( configs: widget.configs, done: widget.editor.done, close: widget.editor.close, diff --git a/lib/src/core/pro_video_editor/ui/widgets/paint/paint_editor_bar.dart b/lib/src/features/media_editor/canvas/ui/widgets/paint/paint_editor_bar.dart similarity index 97% rename from lib/src/core/pro_video_editor/ui/widgets/paint/paint_editor_bar.dart rename to lib/src/features/media_editor/canvas/ui/widgets/paint/paint_editor_bar.dart index 9a70f4e..94eb7a9 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/paint/paint_editor_bar.dart +++ b/lib/src/features/media_editor/canvas/ui/widgets/paint/paint_editor_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bottom_action_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart'; /// A custom paint editor bottom bar widget for video editor. /// @@ -67,7 +67,7 @@ class _PaintEditorBarState extends State { mainAxisSize: MainAxisSize.min, children: [ _buildFunctions(), - CropRotateEditorBottomActionBar( + EditorBottomActionBar( configs: widget.configs, done: widget.editor.done, close: widget.editor.close, diff --git a/lib/src/core/pro_video_editor/ui/widgets/common/build_stickers.dart b/lib/src/features/media_editor/canvas/ui/widgets/sticker_picker.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/common/build_stickers.dart rename to lib/src/features/media_editor/canvas/ui/widgets/sticker_picker.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/text/text_editor_bar.dart b/lib/src/features/media_editor/canvas/ui/widgets/text/text_editor_bar.dart similarity index 97% rename from lib/src/core/pro_video_editor/ui/widgets/text/text_editor_bar.dart rename to lib/src/features/media_editor/canvas/ui/widgets/text/text_editor_bar.dart index cf8822a..f40ab6c 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/text/text_editor_bar.dart +++ b/lib/src/features/media_editor/canvas/ui/widgets/text/text_editor_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/text_editor_bottom_action_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart'; /// A custom text editor bottom bar widget for video editor. /// @@ -67,7 +67,7 @@ class _TextEditorBarState extends State { mainAxisSize: MainAxisSize.min, children: [ _buildFunctions(), - TextEditorBottomActionBar( + EditorBottomActionBar( configs: widget.configs, done: widget.editor.done, close: widget.editor.close, diff --git a/lib/src/core/pro_video_editor/ui/widgets/text/text_editor_color_picker.dart b/lib/src/features/media_editor/canvas/ui/widgets/text/text_editor_color_picker.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/text/text_editor_color_picker.dart rename to lib/src/features/media_editor/canvas/ui/widgets/text/text_editor_color_picker.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/tune/tune_editor_bar.dart b/lib/src/features/media_editor/canvas/ui/widgets/tune/tune_editor_bar.dart similarity index 97% rename from lib/src/core/pro_video_editor/ui/widgets/tune/tune_editor_bar.dart rename to lib/src/features/media_editor/canvas/ui/widgets/tune/tune_editor_bar.dart index beffa29..289b7ec 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/tune/tune_editor_bar.dart +++ b/lib/src/features/media_editor/canvas/ui/widgets/tune/tune_editor_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bottom_action_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart'; /// A custom tune editor bottom bar widget for video editor. /// @@ -46,7 +46,7 @@ class _TuneEditorBarState extends State { mainAxisSize: MainAxisSize.min, children: [ _buildFunctions(), - CropRotateEditorBottomActionBar( + EditorBottomActionBar( configs: widget.configs, done: widget.editor.done, close: widget.editor.close, diff --git a/lib/src/features/media_editor/models/media_editor_profile.dart b/lib/src/features/media_editor/models/media_editor_profile.dart new file mode 100644 index 0000000..fb33721 --- /dev/null +++ b/lib/src/features/media_editor/models/media_editor_profile.dart @@ -0,0 +1 @@ +enum MediaEditorProfile { post, story } diff --git a/lib/src/core/pro_image_editor/story_mention_layer.dart b/lib/src/features/media_editor/story/logic/story_mention_layer.dart similarity index 100% rename from lib/src/core/pro_image_editor/story_mention_layer.dart rename to lib/src/features/media_editor/story/logic/story_mention_layer.dart diff --git a/lib/src/core/pro_image_editor/models/story_image_editor_result.dart b/lib/src/features/media_editor/story/models/story_image_editor_result.dart similarity index 100% rename from lib/src/core/pro_image_editor/models/story_image_editor_result.dart rename to lib/src/features/media_editor/story/models/story_image_editor_result.dart diff --git a/lib/src/core/pro_image_editor/story_editor_profile.dart b/lib/src/features/media_editor/story/ui/config/story_editor_profile.dart similarity index 89% rename from lib/src/core/pro_image_editor/story_editor_profile.dart rename to lib/src/features/media_editor/story/ui/config/story_editor_profile.dart index 9050a53..5e96f9d 100644 --- a/lib/src/core/pro_image_editor/story_editor_profile.dart +++ b/lib/src/features/media_editor/story/ui/config/story_editor_profile.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_image_editor/story_sticker_editor.dart'; -import 'package:spark/src/core/pro_image_editor/ui/widgets/story_editor_bottom_section.dart'; -import 'package:spark/src/core/pro_image_editor/ui/widgets/story_editor_top_section.dart'; +import 'package:spark/src/features/media_editor/story/ui/story_sticker_editor.dart'; +import 'package:spark/src/features/media_editor/story/ui/widgets/story_editor_bottom_section.dart'; +import 'package:spark/src/features/media_editor/story/ui/widgets/story_editor_top_section.dart'; /// Canonical product-profile configuration shared by Story image and video /// editors. diff --git a/lib/src/core/pro_image_editor/story_image_editor_configs.dart b/lib/src/features/media_editor/story/ui/config/story_image_editor_configs.dart similarity index 92% rename from lib/src/core/pro_image_editor/story_image_editor_configs.dart rename to lib/src/features/media_editor/story/ui/config/story_image_editor_configs.dart index 28d60ab..3a0efb7 100644 --- a/lib/src/core/pro_image_editor/story_image_editor_configs.dart +++ b/lib/src/features/media_editor/story/ui/config/story_image_editor_configs.dart @@ -6,13 +6,13 @@ import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/theme/color_scheme.dart'; import 'package:spark/src/core/design_system/theme/text_theme.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_image_editor/story_editor_profile.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/blur/blur_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/build_stickers.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/filter/filter_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/paint/paint_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/text/text_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/text/text_editor_color_picker.dart'; +import 'package:spark/src/features/media_editor/story/ui/config/story_editor_profile.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/blur/blur_editor_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/sticker_picker.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/filter/filter_editor_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/paint/paint_editor_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/text/text_editor_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/text/text_editor_color_picker.dart'; /// Configuration builder for the Story Image Editor. /// diff --git a/lib/src/core/pro_image_editor/story_mention_editing.dart b/lib/src/features/media_editor/story/ui/mixins/story_mention_editing.dart similarity index 87% rename from lib/src/core/pro_image_editor/story_mention_editing.dart rename to lib/src/features/media_editor/story/ui/mixins/story_mention_editing.dart index e4757ad..33f8016 100644 --- a/lib/src/core/pro_image_editor/story_mention_editing.dart +++ b/lib/src/features/media_editor/story/ui/mixins/story_mention_editing.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/network/atproto/data/models/story_embed_models.dart'; -import 'package:spark/src/core/pro_image_editor/story_mention_layer.dart'; -import 'package:spark/src/core/pro_image_editor/ui/widgets/story_mention_picker_sheet.dart'; +import 'package:spark/src/features/media_editor/story/logic/story_mention_layer.dart'; +import 'package:spark/src/features/media_editor/story/ui/widgets/story_mention_picker_sheet.dart'; mixin StoryMentionEditing on State { GlobalKey get storyEditorKey; diff --git a/lib/src/core/pro_image_editor/ui/story_image_editor_page.dart b/lib/src/features/media_editor/story/ui/pages/story_image_editor_page.dart similarity index 55% rename from lib/src/core/pro_image_editor/ui/story_image_editor_page.dart rename to lib/src/features/media_editor/story/ui/pages/story_image_editor_page.dart index 6eea09b..b4146c5 100644 --- a/lib/src/core/pro_image_editor/ui/story_image_editor_page.dart +++ b/lib/src/features/media_editor/story/ui/pages/story_image_editor_page.dart @@ -7,206 +7,16 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_image_editor/models/story_image_editor_result.dart'; -import 'package:spark/src/core/pro_image_editor/story_mention_editing.dart'; -import 'package:spark/src/core/pro_image_editor/story_image_editor_configs.dart'; -import 'package:spark/src/core/pro_image_editor/utils/story_image_cropper.dart'; +import 'package:spark/src/features/media_editor/story/models/story_image_editor_result.dart'; +import 'package:spark/src/features/media_editor/story/ui/mixins/story_mention_editing.dart'; +import 'package:spark/src/features/media_editor/story/ui/config/story_image_editor_configs.dart'; -/// A story-specific image editor page. -/// -/// This editor uses a fixed 9:16 aspect ratio canvas optimized -/// for Instagram Stories-style content. -/// -/// Features: -/// - Auto-crops input images to 9:16 aspect ratio -/// - Rounded preview area matching the camera view style -/// - Text, paint, stickers, emoji, filter, and blur tools -/// - NO crop/rotate tools (to maintain aspect ratio) -/// - Custom UI matching the app's design language -class StoryImageEditorPage extends StatefulWidget { - const StoryImageEditorPage({required this.imageFile, super.key}); - - /// The source image file to edit. - final File imageFile; - - /// Opens the story image editor and returns the edited image. - /// - /// Returns `null` if the user cancels without completing the edit. - static Future open( - BuildContext context, - File imageFile, - ) async { - return Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => StoryImageEditorPage(imageFile: imageFile), - ), - ); - } - - @override - State createState() => _StoryImageEditorPageState(); -} - -class _StoryImageEditorPageState extends State - with StoryMentionEditing { - final _editorKey = GlobalKey(); - final bool _useMaterialDesign = - platformDesignMode == ImageEditorDesignMode.material; - - late ProImageEditorConfigs _configs; - File? _croppedImageFile; - bool _isLoading = true; - String? _error; - - @override - GlobalKey get storyEditorKey => _editorKey; - - @override - Size get storyCanvasFallbackSize => StoryImageEditorConfigs.storySize; - - @override - void initState() { - super.initState(); - _prepareImage(); - } - - @override - void dispose() { - // Clean up cropped temp file if it exists and is different from original - if (_croppedImageFile != null && - _croppedImageFile!.path != widget.imageFile.path) { - _croppedImageFile!.delete().catchError((_) => _croppedImageFile!); - } - super.dispose(); - } - - Future _prepareImage() async { - try { - // Check if image needs cropping to 9:16 - final needsCrop = await StoryImageCropper.needsCropping(widget.imageFile); - - if (needsCrop) { - // Crop to 9:16 aspect ratio - _croppedImageFile = await StoryImageCropper.cropToStoryAspectRatio( - widget.imageFile, - ); - } else { - _croppedImageFile = widget.imageFile; - } - - // Initialize editor config - _configs = StoryImageEditorConfigs.build( - useMaterialDesign: _useMaterialDesign, - imagePreviewBuilder: () => - Image.file(_croppedImageFile!, fit: BoxFit.cover), - onMention: addStoryMention, - onDone: finishStoryEditing, - ); - - if (mounted) { - setState(() { - _isLoading = false; - }); - } - } catch (e) { - if (mounted) { - setState(() { - _isLoading = false; - _error = e.toString(); - }); - } - } - } - - Future _onImageEditingComplete(Uint8List bytes) async { - final directory = await getTemporaryDirectory(); - final timestamp = DateTime.now().millisecondsSinceEpoch; - final filename = 'spark_story_$timestamp.png'; - final file = File('${directory.path}/$filename'); - await file.writeAsBytes(bytes, flush: true); - - if (mounted) { - Navigator.of(context).pop( - StoryImageEditorResult( - image: XFile(file.path, mimeType: 'image/png', name: filename), - embeds: pendingStoryEmbeds, - ), - ); - } - } - - void _onCloseEditor(EditorMode _) => Navigator.of(context).pop(); - - @override - Widget build(BuildContext context) { - if (_isLoading) { - return const Scaffold( - backgroundColor: Colors.black, - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - CircularProgressIndicator(color: Colors.white), - SizedBox(height: 16), - Text( - 'Preparing image...', - style: TextStyle(color: Colors.white70), - ), - ], - ), - ), - ); - } - - if (_error != null) { - return Scaffold( - backgroundColor: Colors.black, - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.error_outline, color: Colors.red, size: 48), - const SizedBox(height: 16), - Text( - AppLocalizations.of(context).errorFailedToLoadImage, - style: const TextStyle(color: Colors.white, fontSize: 18), - ), - const SizedBox(height: 8), - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: Text(AppLocalizations.of(context).buttonGoBack), - ), - ], - ), - ), - ); - } - - return Scaffold( - backgroundColor: Colors.black, - body: ProImageEditor.file( - _croppedImageFile, - key: _editorKey, - callbacks: ProImageEditorCallbacks( - onImageEditingComplete: _onImageEditingComplete, - onCloseEditor: _onCloseEditor, - stickerEditorCallbacks: StickerEditorCallbacks( - onSearchChanged: (_) {}, - ), - ), - configs: _configs, - ), - ); - } -} - -/// A blank canvas story editor that allows adding images as movable layers. +/// A story editor that composes images as movable layers on a 9:16 canvas. /// /// This approach gives more flexibility - the user's image becomes a movable /// layer on a fixed 9:16 canvas, allowing them to position it anywhere. -class StoryBlankCanvasEditorPage extends StatefulWidget { - const StoryBlankCanvasEditorPage({ +class StoryImageEditorPage extends StatefulWidget { + const StoryImageEditorPage({ this.backgroundImage, this.backgroundColor = Colors.black, super.key, @@ -226,7 +36,7 @@ class StoryBlankCanvasEditorPage extends StatefulWidget { }) async { return Navigator.of(context).push( MaterialPageRoute( - builder: (_) => StoryBlankCanvasEditorPage( + builder: (_) => StoryImageEditorPage( backgroundImage: backgroundImage, backgroundColor: backgroundColor, ), @@ -235,12 +45,11 @@ class StoryBlankCanvasEditorPage extends StatefulWidget { } @override - State createState() => - _StoryBlankCanvasEditorPageState(); + State createState() => _StoryImageEditorPageState(); } -class _StoryBlankCanvasEditorPageState extends State - with StoryMentionEditing { +class _StoryImageEditorPageState extends State + with StoryMentionEditing { final _editorKey = GlobalKey(); final bool _useMaterialDesign = platformDesignMode == ImageEditorDesignMode.material; diff --git a/lib/src/core/pro_image_editor/story_sticker_editor.dart b/lib/src/features/media_editor/story/ui/story_sticker_editor.dart similarity index 100% rename from lib/src/core/pro_image_editor/story_sticker_editor.dart rename to lib/src/features/media_editor/story/ui/story_sticker_editor.dart diff --git a/lib/src/core/pro_image_editor/ui/widgets/story_editor_bottom_section.dart b/lib/src/features/media_editor/story/ui/widgets/story_editor_bottom_section.dart similarity index 100% rename from lib/src/core/pro_image_editor/ui/widgets/story_editor_bottom_section.dart rename to lib/src/features/media_editor/story/ui/widgets/story_editor_bottom_section.dart diff --git a/lib/src/core/pro_image_editor/ui/widgets/story_editor_toolbar.dart b/lib/src/features/media_editor/story/ui/widgets/story_editor_toolbar.dart similarity index 100% rename from lib/src/core/pro_image_editor/ui/widgets/story_editor_toolbar.dart rename to lib/src/features/media_editor/story/ui/widgets/story_editor_toolbar.dart diff --git a/lib/src/core/pro_image_editor/ui/widgets/story_editor_top_section.dart b/lib/src/features/media_editor/story/ui/widgets/story_editor_top_section.dart similarity index 95% rename from lib/src/core/pro_image_editor/ui/widgets/story_editor_top_section.dart rename to lib/src/features/media_editor/story/ui/widgets/story_editor_top_section.dart index 9d79b6c..21ec714 100644 --- a/lib/src/core/pro_image_editor/ui/widgets/story_editor_top_section.dart +++ b/lib/src/features/media_editor/story/ui/widgets/story_editor_top_section.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:spark/src/core/design_system/components/atoms/buttons/circle_icon_button.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_image_editor/ui/widgets/story_editor_toolbar.dart'; +import 'package:spark/src/features/media_editor/story/ui/widgets/story_editor_toolbar.dart'; /// Shared Story-profile chrome for image and video editors. class StoryEditorTopSection extends StatelessWidget { diff --git a/lib/src/core/pro_image_editor/ui/widgets/story_mention_picker_sheet.dart b/lib/src/features/media_editor/story/ui/widgets/story_mention_picker_sheet.dart similarity index 100% rename from lib/src/core/pro_image_editor/ui/widgets/story_mention_picker_sheet.dart rename to lib/src/features/media_editor/story/ui/widgets/story_mention_picker_sheet.dart diff --git a/lib/src/core/pro_video_editor/models/video_editor_result.dart b/lib/src/features/media_editor/video/models/video_editor_result.dart similarity index 100% rename from lib/src/core/pro_video_editor/models/video_editor_result.dart rename to lib/src/features/media_editor/video/models/video_editor_result.dart diff --git a/lib/src/core/pro_video_editor/services/audio_helper_service.dart b/lib/src/features/media_editor/video/services/audio_helper_service.dart similarity index 99% rename from lib/src/core/pro_video_editor/services/audio_helper_service.dart rename to lib/src/features/media_editor/video/services/audio_helper_service.dart index 80252ee..e522be8 100644 --- a/lib/src/core/pro_video_editor/services/audio_helper_service.dart +++ b/lib/src/features/media_editor/video/services/audio_helper_service.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:audioplayers/audioplayers.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; import 'package:video_player/video_player.dart'; const _resumeSeekTolerance = Duration(milliseconds: 250); diff --git a/lib/src/core/pro_video_editor/services/audio_source_resolver.dart b/lib/src/features/media_editor/video/services/audio_source_resolver.dart similarity index 97% rename from lib/src/core/pro_video_editor/services/audio_source_resolver.dart rename to lib/src/features/media_editor/video/services/audio_source_resolver.dart index 0e29347..0997f65 100644 --- a/lib/src/core/pro_video_editor/services/audio_source_resolver.dart +++ b/lib/src/features/media_editor/video/services/audio_source_resolver.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:pro_video_editor/pro_video_editor.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; typedef TemporaryDirectoryProvider = Future Function(); typedef AssetAudioPathWriter = diff --git a/lib/src/core/pro_video_editor/services/video_export_planner.dart b/lib/src/features/media_editor/video/services/video_export_planner.dart similarity index 96% rename from lib/src/core/pro_video_editor/services/video_export_planner.dart rename to lib/src/features/media_editor/video/services/video_export_planner.dart index ecb6e21..e7f7bb2 100644 --- a/lib/src/core/pro_video_editor/services/video_export_planner.dart +++ b/lib/src/features/media_editor/video/services/video_export_planner.dart @@ -4,8 +4,9 @@ import 'dart:ui' show Size; import 'package:flutter/painting.dart' show BoxFit; import 'package:pro_image_editor/pro_image_editor.dart' as image_editor; import 'package:pro_video_editor/pro_video_editor.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_source_resolver.dart'; -import 'package:spark/src/core/pro_video_editor/services/video_timing_export_service.dart'; +import 'package:spark/src/features/media_editor/models/media_editor_profile.dart'; +import 'package:spark/src/features/media_editor/video/services/audio_source_resolver.dart'; +import 'package:spark/src/features/media_editor/video/services/video_timing_export_service.dart'; class VideoExportPlan { VideoExportPlan._( @@ -215,11 +216,11 @@ class _PreparedAudioTrack { ExportTransform? buildVideoExportTransform({ required image_editor.CompleteParameters parameters, - required bool storyMode, + required MediaEditorProfile profile, required Size sourceSize, required Size storyCanvasSize, }) { - if (!storyMode) { + if (profile == MediaEditorProfile.post) { if (!parameters.isTransformed) return null; return ExportTransform( width: parameters.cropWidth, diff --git a/lib/src/core/pro_video_editor/services/video_timing_export_service.dart b/lib/src/features/media_editor/video/services/video_timing_export_service.dart similarity index 100% rename from lib/src/core/pro_video_editor/services/video_timing_export_service.dart rename to lib/src/features/media_editor/video/services/video_timing_export_service.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/common/video_editor_configs_builder.dart b/lib/src/features/media_editor/video/ui/config/video_editor_configs_builder.dart similarity index 88% rename from lib/src/core/pro_video_editor/ui/widgets/common/video_editor_configs_builder.dart rename to lib/src/features/media_editor/video/ui/config/video_editor_configs_builder.dart index 0123dca..5d9ccd4 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/common/video_editor_configs_builder.dart +++ b/lib/src/features/media_editor/video/ui/config/video_editor_configs_builder.dart @@ -4,24 +4,25 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:pro_image_editor/designs/grounded/grounded_design.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:pro_video_editor/pro_video_editor.dart'; +import 'package:spark/src/core/media_processing/video/video_processing_service.dart'; import 'package:spark/src/core/design_system/theme/color_scheme.dart'; import 'package:spark/src/core/design_system/theme/text_theme.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_image_editor/story_editor_profile.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/blur/blur_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/clip/clip_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/clip/clips_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/build_stickers.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/video_progress_alert.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/filter/filter_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_regular_chrome.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/paint/paint_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/text/text_editor_bar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/text/text_editor_color_picker.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/story_video_timeline_controls.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/tune/tune_editor_bar.dart'; +import 'package:spark/src/features/media_editor/story/ui/config/story_editor_profile.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/blur/blur_editor_bar.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/clip/clip_editor_bar.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/clip/clips_editor_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/sticker_picker.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/common/video_progress_alert.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/crop_rotate/crop_rotate_editor_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/filter/filter_editor_bar.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_regular_chrome.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/paint/paint_editor_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/text/text_editor_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/text/text_editor_color_picker.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/story_video_timeline_controls.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/tune/tune_editor_bar.dart'; class VideoEditorConfigsBuilder { const VideoEditorConfigsBuilder._(); @@ -41,6 +42,7 @@ class VideoEditorConfigsBuilder { static ProImageEditorConfigs buildStory({ required EditorVideo video, required String taskId, + required VideoProcessingService processingService, required bool useMaterialDesign, required Widget Function() videoPlayerBuilder, required VideoTimelineState timelineState, @@ -60,6 +62,7 @@ class VideoEditorConfigsBuilder { return _build( video: video, taskId: taskId, + processingService: processingService, useMaterialDesign: useMaterialDesign, videoPlayerBuilder: videoPlayerBuilder, tools: StoryEditorProfile.tools, @@ -86,6 +89,7 @@ class VideoEditorConfigsBuilder { static ProImageEditorConfigs buildRegular({ required EditorVideo video, required String taskId, + required VideoProcessingService processingService, required bool useMaterialDesign, required Widget Function() videoPlayerBuilder, required VideoEditorRegularChrome chrome, @@ -94,6 +98,7 @@ class VideoEditorConfigsBuilder { return _build( video: video, taskId: taskId, + processingService: processingService, useMaterialDesign: useMaterialDesign, videoPlayerBuilder: videoPlayerBuilder, tools: _fullTools, @@ -128,6 +133,7 @@ class VideoEditorConfigsBuilder { static ProImageEditorConfigs _build({ required EditorVideo video, required String taskId, + required VideoProcessingService processingService, required bool useMaterialDesign, required Widget Function() videoPlayerBuilder, required List tools, @@ -141,8 +147,9 @@ class VideoEditorConfigsBuilder { designMode: platformDesignMode, dialogConfigs: DialogConfigs( widgets: DialogWidgets( - loadingDialog: (message, configs) => - VideoProgressAlert(taskId: taskId), + loadingDialog: (message, configs) => VideoProgressAlert( + progress: processingService.progressFor(taskId), + ), ), ), theme: ThemeData( diff --git a/lib/src/core/pro_video_editor/ui/controllers/video_editor_export_controller.dart b/lib/src/features/media_editor/video/ui/controllers/video_editor_export_controller.dart similarity index 81% rename from lib/src/core/pro_video_editor/ui/controllers/video_editor_export_controller.dart rename to lib/src/features/media_editor/video/ui/controllers/video_editor_export_controller.dart index de3ffcd..dcfc2ad 100644 --- a/lib/src/core/pro_video_editor/ui/controllers/video_editor_export_controller.dart +++ b/lib/src/features/media_editor/video/ui/controllers/video_editor_export_controller.dart @@ -3,23 +3,29 @@ import 'package:image_picker/image_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:pro_video_editor/pro_video_editor.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_source_resolver.dart'; -import 'package:spark/src/core/pro_video_editor/services/video_export_planner.dart'; +import 'package:spark/src/core/media_processing/video/video_processing_service.dart'; +import 'package:spark/src/features/media_editor/models/media_editor_profile.dart'; +import 'package:spark/src/features/media_editor/video/services/audio_source_resolver.dart'; +import 'package:spark/src/features/media_editor/video/services/video_export_planner.dart'; class VideoEditorExportController { - const VideoEditorExportController({required this.taskId}); + const VideoEditorExportController({ + required this.taskId, + required this.processingService, + }); static const _compressionMinFileSizeBytes = 25 * 1024 * 1024; static const _compressionBitrate = 3000000; static const _compressionMaxLongEdge = 1920.0; final String taskId; + final VideoProcessingService processingService; Future export({ required CompleteParameters parameters, required EditorVideo video, required VideoMetadata metadata, - required bool storyMode, + required MediaEditorProfile profile, required Size storyCanvasSize, required TrimDurationSpan? trimSpan, required bool originalAudioMuted, @@ -31,7 +37,7 @@ class VideoEditorExportController { final compressForUpload = await _shouldCompress(sourceVideoPath); final transform = buildVideoExportTransform( parameters: parameters, - storyMode: storyMode, + profile: profile, sourceSize: metadata.resolution, storyCanvasSize: storyCanvasSize, ); @@ -66,7 +72,7 @@ class VideoEditorExportController { try { final directory = await getTemporaryDirectory(); final now = DateTime.now().millisecondsSinceEpoch; - final outputPath = await ProVideoEditor.instance.renderVideoToFile( + final outputPath = await processingService.renderToFile( '${directory.path}/spark_edited_$now.mp4', plan.renderData, ); diff --git a/lib/src/core/pro_video_editor/ui/controllers/video_editor_media_session.dart b/lib/src/features/media_editor/video/ui/controllers/video_editor_media_session.dart similarity index 97% rename from lib/src/core/pro_video_editor/ui/controllers/video_editor_media_session.dart rename to lib/src/features/media_editor/video/ui/controllers/video_editor_media_session.dart index bfe2329..f71709b 100644 --- a/lib/src/core/pro_video_editor/ui/controllers/video_editor_media_session.dart +++ b/lib/src/features/media_editor/video/ui/controllers/video_editor_media_session.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_helper_service.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_playback.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/services/audio_helper_service.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_playback.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; import 'package:video_player/video_player.dart'; typedef VideoEditorSeekErrorHandler = diff --git a/lib/src/core/pro_video_editor/ui/controllers/video_editor_preview_asset_loader.dart b/lib/src/features/media_editor/video/ui/controllers/video_editor_preview_asset_loader.dart similarity index 72% rename from lib/src/core/pro_video_editor/ui/controllers/video_editor_preview_asset_loader.dart rename to lib/src/features/media_editor/video/ui/controllers/video_editor_preview_asset_loader.dart index f75f591..f9d23b2 100644 --- a/lib/src/core/pro_video_editor/ui/controllers/video_editor_preview_asset_loader.dart +++ b/lib/src/features/media_editor/video/ui/controllers/video_editor_preview_asset_loader.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:pro_video_editor/pro_video_editor.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_waveform_extractor.dart'; +import 'package:spark/src/core/media_processing/video/video_processing_service.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; class VideoEditorPreviewAssetLoader { - const VideoEditorPreviewAssetLoader(); + const VideoEditorPreviewAssetLoader(this._processingService); + + final VideoProcessingService _processingService; static const _thumbnailCount = 7; @@ -16,7 +18,7 @@ class VideoEditorPreviewAssetLoader { required double devicePixelRatio, }) async { final segmentDuration = duration.inMilliseconds / _thumbnailCount; - final bytes = await ProVideoEditor.instance.getThumbnails( + final bytes = await _processingService.getThumbnails( ThumbnailConfigs( video: video, outputSize: Size.square( @@ -32,11 +34,11 @@ class VideoEditorPreviewAssetLoader { } Future> loadVideoWaveform(EditorVideo video) { - return AudioWaveformExtractor.instance.extractFromVideo(video); + return _processingService.extractVideoWaveform(video); } Future> loadCustomWaveform(AudioTrack track) { - return AudioWaveformExtractor.instance.extractFromAudio( + return _processingService.extractAudioWaveform( track.audio, preferredExtension: decodeSoundTrackAudioFileExtension(track.id), ); diff --git a/lib/src/core/pro_video_editor/ui/video_editor_grounded_page.dart b/lib/src/features/media_editor/video/ui/pages/video_editor_page.dart similarity index 89% rename from lib/src/core/pro_video_editor/ui/video_editor_grounded_page.dart rename to lib/src/features/media_editor/video/ui/pages/video_editor_page.dart index e6268ce..6336b45 100644 --- a/lib/src/core/pro_video_editor/ui/video_editor_grounded_page.dart +++ b/lib/src/features/media_editor/video/ui/pages/video_editor_page.dart @@ -1,61 +1,78 @@ import 'dart:async'; import 'dart:math' as math; -import 'package:poptart_lex/com/atproto/repo/strong_ref.dart'; -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart' hide ColorFilter; import 'package:get_it/get_it.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:poptart_lex/com/atproto/repo/strong_ref.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:pro_video_editor/pro_video_editor.dart'; import 'package:spark/src/core/design_system/tokens/recording_layout.dart'; -import 'package:spark/src/core/pro_image_editor/story_mention_editing.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; -import 'package:spark/src/core/pro_video_editor/models/video_editor_result.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_helper_service.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_controller.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_playback.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/video_editor_export_controller.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/video_editor_media_session.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/video_editor_preview_asset_loader.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_range_selection_overlay.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_selection_bottom_sheet.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/video_editor_configs_builder.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/video_initializing_widget.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_regular_chrome.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/player/video_player_widget.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/core/media_processing/video/video_processing_service.dart'; import 'package:spark/src/core/utils/logging/log_service.dart'; import 'package:spark/src/core/utils/logging/logger.dart'; +import 'package:spark/src/features/media_editor/models/media_editor_profile.dart'; +import 'package:spark/src/features/media_editor/story/ui/mixins/story_mention_editing.dart'; +import 'package:spark/src/features/media_editor/video/models/video_editor_result.dart'; +import 'package:spark/src/features/media_editor/video/services/audio_helper_service.dart'; +import 'package:spark/src/features/media_editor/video/ui/controllers/video_editor_export_controller.dart'; +import 'package:spark/src/features/media_editor/video/ui/controllers/video_editor_media_session.dart'; +import 'package:spark/src/features/media_editor/video/ui/controllers/video_editor_preview_asset_loader.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/audio/audio_range_selection_overlay.dart'; +import 'package:spark/src/features/media_editor/video/ui/config/video_editor_configs_builder.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/common/video_initializing_widget.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_regular_chrome.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/player/video_player_widget.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_controller.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_playback.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; +import 'package:spark/src/features/sound/ui/widgets/audio_selection_bottom_sheet.dart'; import 'package:video_player/video_player.dart' hide VideoAudioTrack; -@RoutePage() -class VideoEditorGroundedPage extends StatefulWidget { - const VideoEditorGroundedPage({ +class VideoEditorPage extends StatefulWidget { + const VideoEditorPage({ required this.video, - this.storyMode = false, + required this.profile, + required this.processingService, this.initialAudioTrack, super.key, }); + static Future open( + BuildContext context, { + required EditorVideo video, + required MediaEditorProfile profile, + required VideoProcessingService processingService, + AudioTrack? initialAudioTrack, + }) { + return Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => VideoEditorPage( + video: video, + profile: profile, + processingService: processingService, + initialAudioTrack: initialAudioTrack, + ), + ), + ); + } + /// Input video to be edited. final EditorVideo video; - /// When true, uses story-specific tools (no crop/rotate/tune). - final bool storyMode; + final MediaEditorProfile profile; + final VideoProcessingService processingService; /// Optional custom sound selected before the editor opens. final AudioTrack? initialAudioTrack; @override - State createState() => - _VideoEditorGroundedPageState(); + State createState() => _VideoEditorPageState(); } -class _VideoEditorGroundedPageState extends State - with - StoryMentionEditing, - SingleTickerProviderStateMixin { +class _VideoEditorPageState extends State + with StoryMentionEditing, SingleTickerProviderStateMixin { static const _storyCanvasSize = Size(1440, 2560); final _editorKey = GlobalKey(); final bool _useMaterialDesign = @@ -112,10 +129,10 @@ class _VideoEditorGroundedPageState extends State final _updateClipsNotifier = ValueNotifier(false); final _selectedLayerIdNotifier = ValueNotifier(null); - final _previewAssetLoader = const VideoEditorPreviewAssetLoader(); - late final _exportController = VideoEditorExportController(taskId: _taskId); + late final VideoEditorPreviewAssetLoader _previewAssetLoader; + late final VideoEditorExportController _exportController; late final SparkLogger _logger = GetIt.I().getLogger( - 'VideoEditorGroundedPage', + 'VideoEditorPage', ); late ProImageEditorConfigs _configs; @@ -132,6 +149,13 @@ class _VideoEditorGroundedPageState extends State @override void initState() { super.initState(); + _previewAssetLoader = VideoEditorPreviewAssetLoader( + widget.processingService, + ); + _exportController = VideoEditorExportController( + taskId: _taskId, + processingService: widget.processingService, + ); _video = widget.video; _initializePlayer(); } @@ -173,7 +197,7 @@ class _VideoEditorGroundedPageState extends State } Future _initializePlayer() async { - final metadataFuture = ProVideoEditor.instance.getMetadata(_video); + final metadataFuture = widget.processingService.getMetadata(_video); VideoEditorMediaSession? media; VideoEditorRegularChrome? regularChrome; AudioAuditionController? audioAudition; @@ -209,10 +233,11 @@ class _VideoEditorGroundedPageState extends State ); final ProImageEditorConfigs configs; - if (widget.storyMode) { + if (widget.profile == MediaEditorProfile.story) { configs = VideoEditorConfigsBuilder.buildStory( video: widget.video, taskId: _taskId, + processingService: widget.processingService, useMaterialDesign: _useMaterialDesign, videoPlayerBuilder: buildVideoPlayer, videoEditorConfigs: _videoConfigs, @@ -247,6 +272,7 @@ class _VideoEditorGroundedPageState extends State configs = VideoEditorConfigsBuilder.buildRegular( video: widget.video, taskId: _taskId, + processingService: widget.processingService, useMaterialDesign: _useMaterialDesign, videoPlayerBuilder: buildVideoPlayer, videoEditorConfigs: _videoConfigs.copyWith(enablePlayButton: true), @@ -283,7 +309,7 @@ class _VideoEditorGroundedPageState extends State useCoverFit: _videoFit == BoxFit.cover, borderRadius: recordingPagePreviewBorderRadius, ), - initialResolution: widget.storyMode + initialResolution: widget.profile == MediaEditorProfile.story ? _storyCanvasSize : _regularEditorPreviewSize, videoDuration: metadata.duration, @@ -333,7 +359,7 @@ class _VideoEditorGroundedPageState extends State regularChrome = null; audioAudition = null; - if (!widget.storyMode) { + if (widget.profile == MediaEditorProfile.post) { WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; _runBackgroundTask( @@ -389,7 +415,9 @@ class _VideoEditorGroundedPageState extends State Size get _regularEditorPreviewSize => _videoMetadata.resolution; - BoxFit get _videoFit => widget.storyMode ? BoxFit.cover : BoxFit.contain; + BoxFit get _videoFit => widget.profile == MediaEditorProfile.story + ? BoxFit.cover + : BoxFit.contain; Future _extractCustomAudioWaveform(AudioTrack track) async { final request = ++_waveformRequest; @@ -771,7 +799,7 @@ class _VideoEditorGroundedPageState extends State parameters: parameters, video: _video, metadata: _videoMetadata, - storyMode: widget.storyMode, + profile: widget.profile, storyCanvasSize: _storyCanvasSize, trimSpan: _durationSpan, originalAudioMuted: _videoTimelineState.isOriginalAudioMuted, diff --git a/lib/src/core/pro_video_editor/ui/widgets/audio/audio_range_selection_overlay.dart b/lib/src/features/media_editor/video/ui/widgets/audio/audio_range_selection_overlay.dart similarity index 98% rename from lib/src/core/pro_video_editor/ui/widgets/audio/audio_range_selection_overlay.dart rename to lib/src/features/media_editor/video/ui/widgets/audio/audio_range_selection_overlay.dart index 21579de..a6e75f1 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/audio/audio_range_selection_overlay.dart +++ b/lib/src/features/media_editor/video/ui/widgets/audio/audio_range_selection_overlay.dart @@ -7,8 +7,8 @@ import 'package:spark/src/core/design_system/components/atoms/buttons/app_button import 'package:spark/src/core/design_system/tokens/colors.dart'; import 'package:spark/src/core/design_system/tokens/typography.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/models/audio_audition_timing.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_waveform.dart'; +import 'package:spark/src/features/sound/models/audio_audition_timing.dart'; +import 'package:spark/src/features/sound/ui/widgets/audio_waveform.dart'; const _waveformHeight = 72.0; const _selectionHorizontalInset = 28.0; diff --git a/lib/src/core/pro_video_editor/ui/widgets/clip/clip_editor_bar.dart b/lib/src/features/media_editor/video/ui/widgets/clip/clip_editor_bar.dart similarity index 88% rename from lib/src/core/pro_video_editor/ui/widgets/clip/clip_editor_bar.dart rename to lib/src/features/media_editor/video/ui/widgets/clip/clip_editor_bar.dart index 1c364a9..b2bc771 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/clip/clip_editor_bar.dart +++ b/lib/src/features/media_editor/video/ui/widgets/clip/clip_editor_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/features/clips_editor/pages/clips_editor_edit_page.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/text_editor_bottom_action_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart'; /// A custom clip editor bottom bar widget for video editor. /// @@ -29,7 +29,7 @@ class ClipEditorBar extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: EdgeInsets.only(bottom: MediaQuery.viewInsetsOf(context).bottom), - child: TextEditorBottomActionBar( + child: EditorBottomActionBar( configs: configs, done: editor.done, close: editor.close, diff --git a/lib/src/core/pro_video_editor/ui/widgets/clip/clips_editor_bar.dart b/lib/src/features/media_editor/video/ui/widgets/clip/clips_editor_bar.dart similarity index 88% rename from lib/src/core/pro_video_editor/ui/widgets/clip/clips_editor_bar.dart rename to lib/src/features/media_editor/video/ui/widgets/clip/clips_editor_bar.dart index 7a900e2..8872413 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/clip/clips_editor_bar.dart +++ b/lib/src/features/media_editor/video/ui/widgets/clip/clips_editor_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/features/clips_editor/pages/clips_editor_page.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/common/text_editor_bottom_action_bar.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/widgets/editor_bottom_action_bar.dart'; /// A custom clips editor bottom bar widget for video editor. /// @@ -28,7 +28,7 @@ class ClipsEditorBar extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: EdgeInsets.only(bottom: MediaQuery.viewInsetsOf(context).bottom), - child: TextEditorBottomActionBar( + child: EditorBottomActionBar( configs: configs, done: editor.done, close: editor.close, diff --git a/lib/src/core/pro_video_editor/ui/widgets/common/video_initializing_widget.dart b/lib/src/features/media_editor/video/ui/widgets/common/video_initializing_widget.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/common/video_initializing_widget.dart rename to lib/src/features/media_editor/video/ui/widgets/common/video_initializing_widget.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/common/video_progress_alert.dart b/lib/src/features/media_editor/video/ui/widgets/common/video_progress_alert.dart similarity index 93% rename from lib/src/core/pro_video_editor/ui/widgets/common/video_progress_alert.dart rename to lib/src/features/media_editor/video/ui/widgets/common/video_progress_alert.dart index 5a46924..dcd90f4 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/common/video_progress_alert.dart +++ b/lib/src/features/media_editor/video/ui/widgets/common/video_progress_alert.dart @@ -7,10 +7,9 @@ import 'package:spark/src/core/l10n/app_localizations.dart'; /// Listens to the ProVideoEditor progress stream and shows a circular /// progress indicator with percentage text. class VideoProgressAlert extends StatelessWidget { - const VideoProgressAlert({super.key, this.taskId = ''}); + const VideoProgressAlert({required this.progress, super.key}); - /// Optional taskId to scope the progress stream. - final String taskId; + final Stream progress; @override Widget build(BuildContext context) { @@ -38,7 +37,7 @@ class VideoProgressAlert extends StatelessWidget { Widget _buildProgressBody() { return StreamBuilder( - stream: ProVideoEditor.instance.progressStreamById(taskId), + stream: progress, builder: (context, snapshot) { final progress = snapshot.data?.progress ?? 0; return TweenAnimationBuilder( diff --git a/lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_bottom_section.dart b/lib/src/features/media_editor/video/ui/widgets/layout/video_editor_bottom_section.dart similarity index 94% rename from lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_bottom_section.dart rename to lib/src/features/media_editor/video/ui/widgets/layout/video_editor_bottom_section.dart index ffc0dd6..aa9a76b 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_bottom_section.dart +++ b/lib/src/features/media_editor/video/ui/widgets/layout/video_editor_bottom_section.dart @@ -2,10 +2,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_toolbar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_toolbar.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; class VideoEditorBottomSection extends StatefulWidget { const VideoEditorBottomSection({ diff --git a/lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_header.dart b/lib/src/features/media_editor/video/ui/widgets/layout/video_editor_header.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_header.dart rename to lib/src/features/media_editor/video/ui/widgets/layout/video_editor_header.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_regular_chrome.dart b/lib/src/features/media_editor/video/ui/widgets/layout/video_editor_regular_chrome.dart similarity index 91% rename from lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_regular_chrome.dart rename to lib/src/features/media_editor/video/ui/widgets/layout/video_editor_regular_chrome.dart index 38b9e1e..a6ae8f3 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_regular_chrome.dart +++ b/lib/src/features/media_editor/video/ui/widgets/layout/video_editor_regular_chrome.dart @@ -1,10 +1,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_bottom_section.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_header.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_reveal_layout.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_bottom_section.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_header.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_reveal_layout.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; class VideoEditorRegularChrome { VideoEditorRegularChrome({ diff --git a/lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_reveal_layout.dart b/lib/src/features/media_editor/video/ui/widgets/layout/video_editor_reveal_layout.dart similarity index 99% rename from lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_reveal_layout.dart rename to lib/src/features/media_editor/video/ui/widgets/layout/video_editor_reveal_layout.dart index 356fc0a..a9ea386 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_reveal_layout.dart +++ b/lib/src/features/media_editor/video/ui/widgets/layout/video_editor_reveal_layout.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; import 'package:spark/src/core/design_system/tokens/recording_layout.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; const _kRevealDuration = Duration(milliseconds: 240); const _kRevealCurve = Cubic(0.32, 0.72, 0, 1); diff --git a/lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_toolbar.dart b/lib/src/features/media_editor/video/ui/widgets/layout/video_editor_toolbar.dart similarity index 96% rename from lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_toolbar.dart rename to lib/src/features/media_editor/video/ui/widgets/layout/video_editor_toolbar.dart index 512f84b..92405e7 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/layout/video_editor_toolbar.dart +++ b/lib/src/features/media_editor/video/ui/widgets/layout/video_editor_toolbar.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_toolbar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_toolbar.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; class VideoEditorToolbar extends StatelessWidget { const VideoEditorToolbar({ diff --git a/lib/src/core/pro_video_editor/ui/widgets/layout/video_toolbar.dart b/lib/src/features/media_editor/video/ui/widgets/layout/video_toolbar.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/layout/video_toolbar.dart rename to lib/src/features/media_editor/video/ui/widgets/layout/video_toolbar.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/player/video_player_widget.dart b/lib/src/features/media_editor/video/ui/widgets/player/video_player_widget.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/player/video_player_widget.dart rename to lib/src/features/media_editor/video/ui/widgets/player/video_player_widget.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/audio_timeline_track.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/audio_timeline_track.dart similarity index 91% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/audio_timeline_track.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/audio_timeline_track.dart index 0cd63b1..0b97722 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/timeline/audio_timeline_track.dart +++ b/lib/src/features/media_editor/video/ui/widgets/timeline/audio_timeline_track.dart @@ -2,10 +2,10 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_waveform.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_subtrack_content.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/sound/ui/widgets/audio_waveform.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timed_track_range.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_subtrack_content.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; class AudioTimelineTrack extends StatelessWidget { const AudioTimelineTrack({ diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/layer_reorder_controller.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/layer_reorder_controller.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/layer_reorder_controller.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/layer_reorder_controller.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/layer_timing_track.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/layer_timing_track.dart similarity index 91% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/layer_timing_track.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/layer_timing_track.dart index 6019e72..796bd5e 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/timeline/layer_timing_track.dart +++ b/lib/src/features/media_editor/video/ui/widgets/timeline/layer_timing_track.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/layer_reorder_controller.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_subtrack_content.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/layer_reorder_controller.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timed_track_range.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_subtrack_content.dart'; class LayerTimingTrack extends StatelessWidget { const LayerTimingTrack({ diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/primary_timeline_trim_overlay.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/primary_timeline_trim_overlay.dart similarity index 96% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/primary_timeline_trim_overlay.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/primary_timeline_trim_overlay.dart index 4fe7e29..e41b8e6 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/timeline/primary_timeline_trim_overlay.dart +++ b/lib/src/features/media_editor/video/ui/widgets/timeline/primary_timeline_trim_overlay.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection_handle.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection_handle.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; const _handleWidth = 12.0; const _capWidth = 6.0; diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline.dart similarity index 96% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline.dart index 93ffa18..9255d05 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline.dart +++ b/lib/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline.dart @@ -4,13 +4,13 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/audio_timeline_track.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/layer_reorder_controller.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/layer_timing_track.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/primary_timeline_trim_overlay.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_visual_tracks.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/audio_timeline_track.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/layer_reorder_controller.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/layer_timing_track.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/primary_timeline_trim_overlay.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_visual_tracks.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; const _kSubtrackSpacing = 6.0; const _kSubtrackScrollIndicatorHeight = 20.0; diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/story_video_timeline_controls.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/story_video_timeline_controls.dart similarity index 92% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/story_video_timeline_controls.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/story_video_timeline_controls.dart index 910ced5..35c0b93 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/timeline/story_video_timeline_controls.dart +++ b/lib/src/features/media_editor/video/ui/widgets/timeline/story_video_timeline_controls.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/layer_timing_track.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/layer_timing_track.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; class StoryVideoTimelineControls extends StatelessWidget { const StoryVideoTimelineControls({ diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/timed_track_range.dart similarity index 98% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/timed_track_range.dart index f744604..b872036 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range.dart +++ b/lib/src/features/media_editor/video/ui/widgets/timeline/timed_track_range.dart @@ -3,8 +3,8 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/layer_reorder_controller.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range_parts.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/layer_reorder_controller.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timed_track_range_parts.dart'; const _kRepositionAxisDeadZone = 4.0; diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range_parts.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/timed_track_range_parts.dart similarity index 97% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range_parts.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/timed_track_range_parts.dart index 68dc495..e8a3a7e 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range_parts.dart +++ b/lib/src/features/media_editor/video/ui/widgets/timeline/timed_track_range_parts.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection_handle.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection_handle.dart'; const timedTrackRangeHandleHitWidth = 32.0; const _outsideRangeMarkerWidth = 8.0; diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection_handle.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/timeline_selection_handle.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection_handle.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/timeline_selection_handle.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/timeline_subtrack_content.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/timeline_subtrack_content.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/timeline_subtrack_content.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/timeline_subtrack_content.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/timeline_visual_tracks.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/timeline_visual_tracks.dart similarity index 98% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/timeline_visual_tracks.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/timeline_visual_tracks.dart index facece3..13dea27 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/timeline/timeline_visual_tracks.dart +++ b/lib/src/features/media_editor/video/ui/widgets/timeline/timeline_visual_tracks.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; class TimelineTimeRuler extends StatelessWidget { const TimelineTimeRuler({ diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/video_timeline.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/video_timeline.dart similarity index 96% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/video_timeline.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/video_timeline.dart index 2387b26..b64300c 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/timeline/video_timeline.dart +++ b/lib/src/features/media_editor/video/ui/widgets/timeline/video_timeline.dart @@ -3,9 +3,9 @@ import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/components/atoms/icons.dart'; import 'package:spark/src/core/design_system/theme/text_theme.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; class VideoTimeline extends StatelessWidget { const VideoTimeline({ diff --git a/lib/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart b/lib/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart rename to lib/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart diff --git a/lib/src/features/posting/ui/pages/image_review_page.dart b/lib/src/features/posting/ui/pages/image_review_page.dart index 6851997..c99d17b 100644 --- a/lib/src/features/posting/ui/pages/image_review_page.dart +++ b/lib/src/features/posting/ui/pages/image_review_page.dart @@ -10,15 +10,15 @@ import 'package:spark/src/core/l10n/app_localizations.dart'; import 'package:spark/src/core/design_system/templates/image_review_page_template.dart'; import 'package:spark/src/core/design_system/tokens/constants.dart'; import 'package:spark/src/core/network/atproto/atproto.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; -import 'package:spark/src/core/pro_video_editor/pro_video_editor_repository.dart'; import 'package:spark/src/core/routing/app_router.dart'; import 'package:spark/src/core/ui/widgets/alt_text_editor_dialog.dart'; import 'package:spark/src/features/auth/providers/auth_providers.dart'; +import 'package:spark/src/features/media_editor/canvas/ui/pages/post_image_editor_page.dart'; import 'package:spark/src/features/posting/models/mention_controller.dart'; import 'package:spark/src/features/posting/providers/post_story.dart'; import 'package:spark/src/features/posting/ui/widgets/image_sound_selection_sheet.dart'; import 'package:spark/src/features/profile/providers/profile_feed_provider.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; @RoutePage() class ImageReviewPage extends ConsumerStatefulWidget { @@ -47,10 +47,7 @@ class _ImageReviewPageState extends ConsumerState { late final FeedRepository _feedRepository; Future showImageEditor(BuildContext context, XFile imageFile) async { - final newImage = await GetIt.I().openImageEditor( - context, - imageFile, - ); + final newImage = await PostImageEditorPage.open(context, imageFile); // If the user edited the image, replace the original file in the list if (newImage != null) { if (!mounted) return; diff --git a/lib/src/features/posting/ui/pages/recording_page.dart b/lib/src/features/posting/ui/pages/recording_page.dart index caf0255..d1762df 100644 --- a/lib/src/features/posting/ui/pages/recording_page.dart +++ b/lib/src/features/posting/ui/pages/recording_page.dart @@ -11,22 +11,25 @@ import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:pro_video_editor/pro_video_editor.dart'; import 'package:spark/src/core/design_system/templates/recording_page_template.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; +import 'package:spark/src/core/media_processing/video/video_processing_service.dart'; import 'package:spark/src/core/network/atproto/data/models/models.dart'; -import 'package:spark/src/core/pro_image_editor/models/story_image_editor_result.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; -import 'package:spark/src/core/pro_video_editor/models/video_editor_result.dart'; -import 'package:spark/src/core/pro_video_editor/pro_video_editor_repository.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_track_list_section.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/sound_picker_sheet_scaffold.dart'; import 'package:spark/src/core/routing/app_router.dart'; import 'package:spark/src/core/utils/error_messages.dart'; import 'package:spark/src/core/utils/logging/logging.dart'; +import 'package:spark/src/features/media_editor/models/media_editor_profile.dart'; +import 'package:spark/src/features/media_editor/story/models/story_image_editor_result.dart'; +import 'package:spark/src/features/media_editor/story/ui/pages/story_image_editor_page.dart'; +import 'package:spark/src/features/media_editor/video/models/video_editor_result.dart'; +import 'package:spark/src/features/media_editor/video/ui/pages/video_editor_page.dart'; import 'package:spark/src/features/posting/providers/camera_provider.dart'; import 'package:spark/src/features/posting/providers/recording_provider.dart'; import 'package:spark/src/features/posting/ui/models/media_selection.dart'; import 'package:spark/src/features/posting/ui/pages/media_picker_page.dart'; import 'package:spark/src/features/posting/utils/captured_photo_flow.dart'; import 'package:spark/src/features/posting/utils/story_direct_post.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; +import 'package:spark/src/features/sound/ui/widgets/audio_track_list_section.dart'; +import 'package:spark/src/features/sound/ui/widgets/sound_picker_sheet_scaffold.dart'; export 'package:spark/src/core/design_system/templates/recording_page_template.dart' show CaptureMode; @@ -282,8 +285,10 @@ class _RecordingPageState extends ConsumerState { ImageReviewRoute(imageFiles: [photo], storyMode: false), ); }, - openStoryEditor: (photo) => GetIt.I() - .openStoryImageEditor(context, photo), + openStoryEditor: (photo) => StoryImageEditorPage.open( + context, + backgroundImage: File(photo.path), + ), publishStory: _publishEditedStoryPhoto, ).run( profile: widget.storyMode @@ -501,10 +506,10 @@ class _RecordingPageState extends ConsumerState { } final segments = recordingState.segmentPaths.map(XFile.new).toList(); - final repository = GetIt.I(); + final processingService = GetIt.I(); try { - final stitchedVideo = await repository.stitchVideoSegments(segments); + final stitchedVideo = await processingService.stitchSegments(segments); if (!mounted) return; final selectedSound = recordingState.selectedSound; @@ -556,25 +561,18 @@ class _RecordingPageState extends ConsumerState { if (!mounted || !context.mounted) return; final editorVideo = EditorVideo.file(File(videoFile.path)); - final repository = GetIt.I(); - VideoEditorResult? result; - if (widget.storyMode) { - if (!context.mounted) return; - result = await repository.openStoryVideoEditor( - context, - editorVideo, - initialAudioTrack: - initialAudioTrack ?? ref.read(recordingProvider).selectedSound, - ); - } else { - if (!context.mounted) return; - result = await repository.openVideoEditor( - context, - editorVideo, - initialAudioTrack: - initialAudioTrack ?? ref.read(recordingProvider).selectedSound, - ); - } + final processingService = GetIt.I(); + if (!context.mounted) return; + final VideoEditorResult? result = await VideoEditorPage.open( + context, + video: editorVideo, + profile: widget.storyMode + ? MediaEditorProfile.story + : MediaEditorProfile.post, + processingService: processingService, + initialAudioTrack: + initialAudioTrack ?? ref.read(recordingProvider).selectedSound, + ); if (!mounted) return; diff --git a/lib/src/features/posting/ui/widgets/image_sound_selection_sheet.dart b/lib/src/features/posting/ui/widgets/image_sound_selection_sheet.dart index 5ce6af8..8258cdb 100644 --- a/lib/src/features/posting/ui/widgets/image_sound_selection_sheet.dart +++ b/lib/src/features/posting/ui/widgets/image_sound_selection_sheet.dart @@ -5,9 +5,9 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/components/atoms/buttons/app_button.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_track_list_section.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/sound_picker_sheet_scaffold.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; +import 'package:spark/src/features/sound/ui/widgets/audio_track_list_section.dart'; +import 'package:spark/src/features/sound/ui/widgets/sound_picker_sheet_scaffold.dart'; class ImageSoundSelectionSheet extends StatefulWidget { const ImageSoundSelectionSheet({super.key, this.initialSelectedTrack}); diff --git a/lib/src/features/posting/utils/captured_photo_flow.dart b/lib/src/features/posting/utils/captured_photo_flow.dart index a3691c4..ea4b513 100644 --- a/lib/src/features/posting/utils/captured_photo_flow.dart +++ b/lib/src/features/posting/utils/captured_photo_flow.dart @@ -1,5 +1,5 @@ import 'package:image_picker/image_picker.dart'; -import 'package:spark/src/core/pro_image_editor/models/story_image_editor_result.dart'; +import 'package:spark/src/features/media_editor/story/models/story_image_editor_result.dart'; enum CapturedPhotoProfile { story, post } diff --git a/lib/src/core/media/create_media_actions.dart b/lib/src/features/posting/utils/create_media_actions.dart similarity index 82% rename from lib/src/core/media/create_media_actions.dart rename to lib/src/features/posting/utils/create_media_actions.dart index 9843efe..52e4b79 100644 --- a/lib/src/core/media/create_media_actions.dart +++ b/lib/src/features/posting/utils/create_media_actions.dart @@ -7,9 +7,12 @@ import 'package:get_it/get_it.dart'; import 'package:image_picker/image_picker.dart'; import 'package:pro_video_editor/pro_video_editor.dart'; import 'package:spark/src/core/media/media_playback_suspension_provider.dart'; -import 'package:spark/src/core/pro_video_editor/models/video_editor_result.dart'; -import 'package:spark/src/core/pro_video_editor/pro_video_editor_repository.dart'; +import 'package:spark/src/core/media_processing/video/video_processing_service.dart'; import 'package:spark/src/core/routing/app_router.dart'; +import 'package:spark/src/features/media_editor/models/media_editor_profile.dart'; +import 'package:spark/src/features/media_editor/story/ui/pages/story_image_editor_page.dart'; +import 'package:spark/src/features/media_editor/video/models/video_editor_result.dart'; +import 'package:spark/src/features/media_editor/video/ui/pages/video_editor_page.dart'; import 'package:spark/src/features/posting/ui/pages/recording_page.dart'; /// Centralized factories for the create media actions used across the app. @@ -74,15 +77,16 @@ class CreateMediaActions { ); try { final editorVideo = EditorVideo.file(File(pickedVideo.path)); - final repository = GetIt.I(); - VideoEditorResult? result; - if (storyMode) { - if (!context.mounted) return; - result = await repository.openStoryVideoEditor(context, editorVideo); - } else { - if (!context.mounted) return; - result = await repository.openVideoEditor(context, editorVideo); - } + final processingService = GetIt.I(); + if (!context.mounted) return; + final VideoEditorResult? result = await VideoEditorPage.open( + context, + video: editorVideo, + profile: storyMode + ? MediaEditorProfile.story + : MediaEditorProfile.post, + processingService: processingService, + ); if (result != null && context.mounted) { if (storyMode) { @@ -127,8 +131,10 @@ class CreateMediaActions { ); if (pickedImage != null && context.mounted) { // Open story editor - final editedImage = await GetIt.I() - .openStoryImageEditor(context, pickedImage); + final editedImage = await StoryImageEditorPage.open( + context, + backgroundImage: File(pickedImage.path), + ); if (editedImage != null && context.mounted) { // Post directly await context.router.push( diff --git a/lib/src/features/profile/ui/pages/profile_page.dart b/lib/src/features/profile/ui/pages/profile_page.dart index fe124ac..03bbd82 100644 --- a/lib/src/features/profile/ui/pages/profile_page.dart +++ b/lib/src/features/profile/ui/pages/profile_page.dart @@ -8,7 +8,7 @@ import 'package:spark/src/core/design_system/components/atoms/icons.dart'; import 'package:spark/src/core/design_system/components/atoms/profile_tab_item.dart'; import 'package:spark/src/core/design_system/components/molecules/profile_tab_bar.dart'; import 'package:spark/src/core/design_system/templates/profile_page_template.dart'; -import 'package:spark/src/core/media/create_media_actions.dart'; +import 'package:spark/src/features/posting/utils/create_media_actions.dart'; import 'package:spark/src/core/network/atproto/atproto.dart'; import 'package:spark/src/core/routing/app_router.dart'; import 'package:spark/src/core/ui/widgets/options_panel.dart'; diff --git a/lib/src/core/pro_video_editor/ui/controllers/audio_audition_controller.dart b/lib/src/features/sound/controllers/audio_audition_controller.dart similarity index 98% rename from lib/src/core/pro_video_editor/ui/controllers/audio_audition_controller.dart rename to lib/src/features/sound/controllers/audio_audition_controller.dart index 168afdb..f3fd7d2 100644 --- a/lib/src/core/pro_video_editor/ui/controllers/audio_audition_controller.dart +++ b/lib/src/features/sound/controllers/audio_audition_controller.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/models/audio_audition_timing.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_playback.dart'; +import 'package:spark/src/features/sound/models/audio_audition_timing.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_playback.dart'; typedef AudioAuditionErrorHandler = void Function(String message, Object error, StackTrace stackTrace); diff --git a/lib/src/core/pro_video_editor/ui/controllers/audio_audition_playback.dart b/lib/src/features/sound/controllers/audio_audition_playback.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/controllers/audio_audition_playback.dart rename to lib/src/features/sound/controllers/audio_audition_playback.dart diff --git a/lib/src/core/pro_video_editor/models/audio_audition_timing.dart b/lib/src/features/sound/models/audio_audition_timing.dart similarity index 100% rename from lib/src/core/pro_video_editor/models/audio_audition_timing.dart rename to lib/src/features/sound/models/audio_audition_timing.dart diff --git a/lib/src/core/pro_video_editor/models/sound_audio_track.dart b/lib/src/features/sound/models/sound_audio_track.dart similarity index 100% rename from lib/src/core/pro_video_editor/models/sound_audio_track.dart rename to lib/src/features/sound/models/sound_audio_track.dart diff --git a/lib/src/core/pro_video_editor/providers/sound_picker_search_provider.dart b/lib/src/features/sound/providers/sound_picker_search_provider.dart similarity index 98% rename from lib/src/core/pro_video_editor/providers/sound_picker_search_provider.dart rename to lib/src/features/sound/providers/sound_picker_search_provider.dart index 43ab02b..e880054 100644 --- a/lib/src/core/pro_video_editor/providers/sound_picker_search_provider.dart +++ b/lib/src/features/sound/providers/sound_picker_search_provider.dart @@ -4,9 +4,9 @@ import 'package:get_it/get_it.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:spark/src/core/network/atproto/data/repositories/sound_repository.dart'; import 'package:spark/src/core/providers/debounce_scheduler.dart'; -import 'package:spark/src/core/pro_video_editor/providers/sound_picker_search_state.dart'; import 'package:spark/src/core/utils/logging/log_service.dart'; import 'package:spark/src/core/utils/logging/logger.dart'; +import 'package:spark/src/features/sound/providers/sound_picker_search_state.dart'; part 'sound_picker_search_provider.g.dart'; diff --git a/lib/src/core/pro_video_editor/providers/sound_picker_search_state.dart b/lib/src/features/sound/providers/sound_picker_search_state.dart similarity index 100% rename from lib/src/core/pro_video_editor/providers/sound_picker_search_state.dart rename to lib/src/features/sound/providers/sound_picker_search_state.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/audio/audio_selection_bottom_sheet.dart b/lib/src/features/sound/ui/widgets/audio_selection_bottom_sheet.dart similarity index 93% rename from lib/src/core/pro_video_editor/ui/widgets/audio/audio_selection_bottom_sheet.dart rename to lib/src/features/sound/ui/widgets/audio_selection_bottom_sheet.dart index 279c2f7..ffc3e4f 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/audio/audio_selection_bottom_sheet.dart +++ b/lib/src/features/sound/ui/widgets/audio_selection_bottom_sheet.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/components/atoms/buttons/app_button.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_controller.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_track_list_section.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/sound_picker_sheet_scaffold.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_controller.dart'; +import 'package:spark/src/features/sound/ui/widgets/audio_track_list_section.dart'; +import 'package:spark/src/features/sound/ui/widgets/sound_picker_sheet_scaffold.dart'; Future showAudioSelectionFlow({ required BuildContext context, diff --git a/lib/src/core/pro_video_editor/ui/widgets/audio/audio_track_list_section.dart b/lib/src/features/sound/ui/widgets/audio_track_list_section.dart similarity index 96% rename from lib/src/core/pro_video_editor/ui/widgets/audio/audio_track_list_section.dart rename to lib/src/features/sound/ui/widgets/audio_track_list_section.dart index 8d00212..ce09238 100644 --- a/lib/src/core/pro_video_editor/ui/widgets/audio/audio_track_list_section.dart +++ b/lib/src/features/sound/ui/widgets/audio_track_list_section.dart @@ -5,10 +5,10 @@ import 'package:spark/src/core/design_system/components/atoms/buttons/interactiv import 'package:spark/src/core/design_system/tokens/colors.dart'; import 'package:spark/src/core/design_system/tokens/typography.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; -import 'package:spark/src/core/pro_video_editor/providers/sound_picker_search_provider.dart'; -import 'package:spark/src/core/pro_video_editor/providers/sound_picker_search_state.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/sound_artwork.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; +import 'package:spark/src/features/sound/providers/sound_picker_search_provider.dart'; +import 'package:spark/src/features/sound/providers/sound_picker_search_state.dart'; +import 'package:spark/src/features/sound/ui/widgets/sound_artwork.dart'; const defaultAudioTrackArtworkBackground = Color(0xFF9E9E9E); diff --git a/lib/src/core/pro_video_editor/ui/widgets/audio/audio_waveform.dart b/lib/src/features/sound/ui/widgets/audio_waveform.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/audio/audio_waveform.dart rename to lib/src/features/sound/ui/widgets/audio_waveform.dart diff --git a/lib/src/core/pro_video_editor/ui/widgets/audio/sound_artwork.dart b/lib/src/features/sound/ui/widgets/sound_artwork.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/audio/sound_artwork.dart rename to lib/src/features/sound/ui/widgets/sound_artwork.dart diff --git a/lib/src/features/sound/ui/widgets/sound_header_card.dart b/lib/src/features/sound/ui/widgets/sound_header_card.dart index edc5723..2db2a7f 100644 --- a/lib/src/features/sound/ui/widgets/sound_header_card.dart +++ b/lib/src/features/sound/ui/widgets/sound_header_card.dart @@ -6,8 +6,8 @@ import 'package:spark/src/core/design_system/tokens/colors.dart'; import 'package:spark/src/core/design_system/tokens/shapes.dart'; import 'package:spark/src/core/design_system/tokens/typography.dart'; import 'package:spark/src/core/network/atproto/data/models/models.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/sound_artwork.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; +import 'package:spark/src/features/sound/ui/widgets/sound_artwork.dart'; import 'package:spark/src/core/routing/app_router.dart'; class SoundHeaderCard extends StatefulWidget { diff --git a/lib/src/core/pro_video_editor/ui/widgets/audio/sound_picker_sheet_scaffold.dart b/lib/src/features/sound/ui/widgets/sound_picker_sheet_scaffold.dart similarity index 100% rename from lib/src/core/pro_video_editor/ui/widgets/audio/sound_picker_sheet_scaffold.dart rename to lib/src/features/sound/ui/widgets/sound_picker_sheet_scaffold.dart diff --git a/test/src/core/pro_video_editor/services/audio_waveform_extractor_test.dart b/test/src/core/media_processing/video/pro_video_processing_service_test.dart similarity index 86% rename from test/src/core/pro_video_editor/services/audio_waveform_extractor_test.dart rename to test/src/core/media_processing/video/pro_video_processing_service_test.dart index 0fdb38e..822fdbd 100644 --- a/test/src/core/pro_video_editor/services/audio_waveform_extractor_test.dart +++ b/test/src/core/media_processing/video/pro_video_processing_service_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_waveform_extractor.dart'; +import 'package:spark/src/core/media_processing/video/pro_video_processing_service.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -26,9 +26,8 @@ void main() { }; }); - final samples = await AudioWaveformExtractor.instance.extractFromPath( - '/tmp/test-audio.mp3', - ); + final samples = await const ProVideoProcessingService() + .extractWaveformFromPath('/tmp/test-audio.mp3'); expect(receivedCall?.method, 'getWaveform'); expect( diff --git a/test/src/core/pro_image_editor/story_mention_layer_test.dart b/test/src/features/media_editor/story/story_mention_layer_test.dart similarity index 98% rename from test/src/core/pro_image_editor/story_mention_layer_test.dart rename to test/src/features/media_editor/story/story_mention_layer_test.dart index 0fd758b..e313f18 100644 --- a/test/src/core/pro_image_editor/story_mention_layer_test.dart +++ b/test/src/features/media_editor/story/story_mention_layer_test.dart @@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:sprk_poptart/so/sprk/actor/defs.dart'; -import 'package:spark/src/core/pro_image_editor/story_mention_layer.dart'; +import 'package:spark/src/features/media_editor/story/logic/story_mention_layer.dart'; import 'package:spark/src/core/ui/widgets/story_mention_chip.dart'; void main() { diff --git a/test/src/core/pro_image_editor/story_sticker_editor_test.dart b/test/src/features/media_editor/story/story_sticker_editor_test.dart similarity index 96% rename from test/src/core/pro_image_editor/story_sticker_editor_test.dart rename to test/src/features/media_editor/story/story_sticker_editor_test.dart index 9ee134f..9492a1f 100644 --- a/test/src/core/pro_image_editor/story_sticker_editor_test.dart +++ b/test/src/features/media_editor/story/story_sticker_editor_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_image_editor/story_sticker_editor.dart'; +import 'package:spark/src/features/media_editor/story/ui/story_sticker_editor.dart'; void main() { testWidgets('Story sticker sheet preserves the configured editor lifecycle', ( diff --git a/test/src/core/pro_image_editor/ui/widgets/story_editor_chrome_test.dart b/test/src/features/media_editor/story/ui/widgets/story_editor_chrome_test.dart similarity index 96% rename from test/src/core/pro_image_editor/ui/widgets/story_editor_chrome_test.dart rename to test/src/features/media_editor/story/ui/widgets/story_editor_chrome_test.dart index 3446df1..5929918 100644 --- a/test/src/core/pro_image_editor/ui/widgets/story_editor_chrome_test.dart +++ b/test/src/features/media_editor/story/ui/widgets/story_editor_chrome_test.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_image_editor/ui/widgets/story_editor_bottom_section.dart'; -import 'package:spark/src/core/pro_image_editor/ui/widgets/story_editor_top_section.dart'; +import 'package:spark/src/features/media_editor/story/ui/widgets/story_editor_bottom_section.dart'; +import 'package:spark/src/features/media_editor/story/ui/widgets/story_editor_top_section.dart'; void main() { testWidgets( diff --git a/test/src/core/pro_video_editor/services/audio_helper_service_test.dart b/test/src/features/media_editor/video/services/audio_helper_service_test.dart similarity index 98% rename from test/src/core/pro_video_editor/services/audio_helper_service_test.dart rename to test/src/features/media_editor/video/services/audio_helper_service_test.dart index 1060af1..46ae8a9 100644 --- a/test/src/core/pro_video_editor/services/audio_helper_service_test.dart +++ b/test/src/features/media_editor/video/services/audio_helper_service_test.dart @@ -4,9 +4,9 @@ import 'dart:io'; import 'package:audioplayers/audioplayers.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_helper_service.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_source_resolver.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; +import 'package:spark/src/features/media_editor/video/services/audio_helper_service.dart'; +import 'package:spark/src/features/media_editor/video/services/audio_source_resolver.dart'; import 'package:video_player/video_player.dart'; void main() { diff --git a/test/src/core/pro_video_editor/services/video_export_planner_test.dart b/test/src/features/media_editor/video/services/video_export_planner_test.dart similarity index 91% rename from test/src/core/pro_video_editor/services/video_export_planner_test.dart rename to test/src/features/media_editor/video/services/video_export_planner_test.dart index 93a6cae..08f719c 100644 --- a/test/src/core/pro_video_editor/services/video_export_planner_test.dart +++ b/test/src/features/media_editor/video/services/video_export_planner_test.dart @@ -6,15 +6,16 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart' show SizedBox; import 'package:pro_image_editor/pro_image_editor.dart' as image_editor; import 'package:pro_video_editor/pro_video_editor.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_source_resolver.dart'; -import 'package:spark/src/core/pro_video_editor/services/video_export_planner.dart'; -import 'package:spark/src/core/pro_video_editor/services/video_timing_export_service.dart'; +import 'package:spark/src/features/media_editor/models/media_editor_profile.dart'; +import 'package:spark/src/features/media_editor/video/services/audio_source_resolver.dart'; +import 'package:spark/src/features/media_editor/video/services/video_export_planner.dart'; +import 'package:spark/src/features/media_editor/video/services/video_timing_export_service.dart'; void main() { test('keeps an unedited portrait video in source space', () { final transform = buildVideoExportTransform( parameters: _parameters(), - storyMode: false, + profile: MediaEditorProfile.post, sourceSize: const Size(1080, 1350), storyCanvasSize: const Size(1440, 2560), ); @@ -22,6 +23,21 @@ void main() { expect(transform, isNull); }); + test('crops a landscape story video to the story canvas', () { + final transform = buildVideoExportTransform( + parameters: _parameters(), + profile: MediaEditorProfile.story, + sourceSize: const Size(1920, 1080), + storyCanvasSize: const Size(1440, 2560), + ); + + expect(transform, isNotNull); + expect(transform!.width, 608); + expect(transform.height, 1080); + expect(transform.x, 656); + expect(transform.y, 0); + }); + test('does not attenuate original audio for an unrendered track', () async { final resolver = _FakeAudioSourceResolver(); final planner = VideoExportPlanner(resolver); diff --git a/test/src/core/pro_video_editor/services/video_timing_export_service_test.dart b/test/src/features/media_editor/video/services/video_timing_export_service_test.dart similarity index 99% rename from test/src/core/pro_video_editor/services/video_timing_export_service_test.dart rename to test/src/features/media_editor/video/services/video_timing_export_service_test.dart index ae89621..f9a08b5 100644 --- a/test/src/core/pro_video_editor/services/video_timing_export_service_test.dart +++ b/test/src/features/media_editor/video/services/video_timing_export_service_test.dart @@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart' as image_editor; import 'package:pro_video_editor/pro_video_editor.dart' as video_editor; -import 'package:spark/src/core/pro_video_editor/services/video_timing_export_service.dart'; +import 'package:spark/src/features/media_editor/video/services/video_timing_export_service.dart'; void main() { test('swaps the export canvas for quarter-turn rotations', () { diff --git a/test/src/core/pro_video_editor/ui/controllers/video_editor_media_session_test.dart b/test/src/features/media_editor/video/ui/controllers/video_editor_media_session_test.dart similarity index 97% rename from test/src/core/pro_video_editor/ui/controllers/video_editor_media_session_test.dart rename to test/src/features/media_editor/video/ui/controllers/video_editor_media_session_test.dart index 1f4f436..640c6ce 100644 --- a/test/src/core/pro_video_editor/ui/controllers/video_editor_media_session_test.dart +++ b/test/src/features/media_editor/video/ui/controllers/video_editor_media_session_test.dart @@ -4,9 +4,9 @@ import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/services/audio_helper_service.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_playback.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/video_editor_media_session.dart'; +import 'package:spark/src/features/media_editor/video/services/audio_helper_service.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_playback.dart'; +import 'package:spark/src/features/media_editor/video/ui/controllers/video_editor_media_session.dart'; import 'package:video_player/video_player.dart'; void main() { diff --git a/test/src/core/pro_video_editor/ui/widgets/audio/audio_range_selection_overlay_test.dart b/test/src/features/media_editor/video/ui/widgets/audio/audio_range_selection_overlay_test.dart similarity index 98% rename from test/src/core/pro_video_editor/ui/widgets/audio/audio_range_selection_overlay_test.dart rename to test/src/features/media_editor/video/ui/widgets/audio/audio_range_selection_overlay_test.dart index c0dbbf8..ef8dbe2 100644 --- a/test/src/core/pro_video_editor/ui/widgets/audio/audio_range_selection_overlay_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/audio/audio_range_selection_overlay_test.dart @@ -5,8 +5,8 @@ import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/components/atoms/buttons/app_button.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/models/audio_audition_timing.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_range_selection_overlay.dart'; +import 'package:spark/src/features/sound/models/audio_audition_timing.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/audio/audio_range_selection_overlay.dart'; void main() { test( diff --git a/test/src/core/pro_video_editor/ui/widgets/layout/video_editor_bottom_section_test.dart b/test/src/features/media_editor/video/ui/widgets/layout/video_editor_bottom_section_test.dart similarity index 93% rename from test/src/core/pro_video_editor/ui/widgets/layout/video_editor_bottom_section_test.dart rename to test/src/features/media_editor/video/ui/widgets/layout/video_editor_bottom_section_test.dart index c268d8b..9f37439 100644 --- a/test/src/core/pro_video_editor/ui/widgets/layout/video_editor_bottom_section_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/layout/video_editor_bottom_section_test.dart @@ -3,10 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_bottom_section.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_bottom_section.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; void main() { testWidgets('keeps one selection owner across timeline and editor changes', ( diff --git a/test/src/core/pro_video_editor/ui/widgets/layout/video_editor_reveal_layout_test.dart b/test/src/features/media_editor/video/ui/widgets/layout/video_editor_reveal_layout_test.dart similarity index 98% rename from test/src/core/pro_video_editor/ui/widgets/layout/video_editor_reveal_layout_test.dart rename to test/src/features/media_editor/video/ui/widgets/layout/video_editor_reveal_layout_test.dart index 05a1721..d18006f 100644 --- a/test/src/core/pro_video_editor/ui/widgets/layout/video_editor_reveal_layout_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/layout/video_editor_reveal_layout_test.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/recording_layout.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_regular_chrome.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_reveal_layout.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_regular_chrome.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_reveal_layout.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; void main() { test( diff --git a/test/src/core/pro_video_editor/ui/widgets/layout/video_editor_toolbar_test.dart b/test/src/features/media_editor/video/ui/widgets/layout/video_editor_toolbar_test.dart similarity index 94% rename from test/src/core/pro_video_editor/ui/widgets/layout/video_editor_toolbar_test.dart rename to test/src/features/media_editor/video/ui/widgets/layout/video_editor_toolbar_test.dart index 0c7f3ab..c91d63c 100644 --- a/test/src/core/pro_video_editor/ui/widgets/layout/video_editor_toolbar_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/layout/video_editor_toolbar_test.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/layout/video_editor_toolbar.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/layout/video_editor_toolbar.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; void main() { testWidgets('text layers expose edit, arrange, and delete actions', ( diff --git a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_rendering_test.dart b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_rendering_test.dart similarity index 96% rename from test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_rendering_test.dart rename to test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_rendering_test.dart index 69e0282..3657b16 100644 --- a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_rendering_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_rendering_test.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection_handle.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection_handle.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; import 'scrollable_timeline_test_harness.dart'; diff --git a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_scrolling_test.dart b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_scrolling_test.dart similarity index 96% rename from test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_scrolling_test.dart rename to test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_scrolling_test.dart index 1893285..3d074c9 100644 --- a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_scrolling_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_scrolling_test.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; import 'scrollable_timeline_test_harness.dart'; diff --git a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_selection_test.dart b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_selection_test.dart similarity index 92% rename from test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_selection_test.dart rename to test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_selection_test.dart index 9fb093c..5986c75 100644 --- a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_selection_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_selection_test.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection_handle.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection_handle.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; import 'scrollable_timeline_test_harness.dart'; diff --git a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_test.dart b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_test.dart similarity index 99% rename from test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_test.dart rename to test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_test.dart index c3dd80c..9591d72 100644 --- a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_test.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/tokens/colors.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timed_track_range.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; import 'scrollable_timeline_test_harness.dart'; diff --git a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_test_harness.dart b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_test_harness.dart similarity index 90% rename from test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_test_harness.dart rename to test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_test_harness.dart index 653d665..563de74 100644 --- a/test/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline_test_harness.dart +++ b/test/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline_test_harness.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timed_track_range.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timed_track_range.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; TimedTrackRange rangeWithin(WidgetTester tester, Finder parent) { return tester.widget( diff --git a/test/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state_test.dart b/test/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state_test.dart similarity index 96% rename from test/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state_test.dart rename to test/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state_test.dart index 36a73a4..0c26838 100644 --- a/test/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; void main() { group('VideoTimelineState trimming', () { diff --git a/test/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_test.dart b/test/src/features/media_editor/video/ui/widgets/timeline/video_timeline_test.dart similarity index 74% rename from test/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_test.dart rename to test/src/features/media_editor/video/ui/widgets/timeline/video_timeline_test.dart index 30ddf66..3d07e18 100644 --- a/test/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_test.dart +++ b/test/src/features/media_editor/video/ui/widgets/timeline/video_timeline_test.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/scrollable_timeline.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/timeline_selection.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/timeline/video_timeline_state.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/scrollable_timeline.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/timeline_selection.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline.dart'; +import 'package:spark/src/features/media_editor/video/ui/widgets/timeline/video_timeline_state.dart'; void main() { testWidgets('timeline extends across the full editor width', (tester) async { diff --git a/test/src/features/posting/utils/captured_photo_flow_test.dart b/test/src/features/posting/utils/captured_photo_flow_test.dart index e698517..c22bb4c 100644 --- a/test/src/features/posting/utils/captured_photo_flow_test.dart +++ b/test/src/features/posting/utils/captured_photo_flow_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:spark/src/core/pro_image_editor/models/story_image_editor_result.dart'; +import 'package:spark/src/features/media_editor/story/models/story_image_editor_result.dart'; import 'package:spark/src/features/posting/utils/captured_photo_flow.dart'; void main() { diff --git a/test/src/core/pro_video_editor/ui/controllers/audio_audition_controller_test.dart b/test/src/features/sound/controllers/audio_audition_controller_test.dart similarity index 98% rename from test/src/core/pro_video_editor/ui/controllers/audio_audition_controller_test.dart rename to test/src/features/sound/controllers/audio_audition_controller_test.dart index d82de9d..446d644 100644 --- a/test/src/core/pro_video_editor/ui/controllers/audio_audition_controller_test.dart +++ b/test/src/features/sound/controllers/audio_audition_controller_test.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_controller.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_playback.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_controller.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_playback.dart'; void main() { test( diff --git a/test/src/core/network/atproto/data/models/sound_models_test.dart b/test/src/features/sound/models/sound_audio_track_test.dart similarity index 99% rename from test/src/core/network/atproto/data/models/sound_models_test.dart rename to test/src/features/sound/models/sound_audio_track_test.dart index f3174bc..d2fb040 100644 --- a/test/src/core/network/atproto/data/models/sound_models_test.dart +++ b/test/src/features/sound/models/sound_audio_track_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:spark/src/core/network/atproto/data/models/models.dart'; -import 'package:spark/src/core/pro_video_editor/models/sound_audio_track.dart'; +import 'package:spark/src/features/sound/models/sound_audio_track.dart'; void main() { group('AudioView', () { diff --git a/test/src/core/pro_video_editor/providers/sound_picker_search_provider_test.dart b/test/src/features/sound/providers/sound_picker_search_provider_test.dart similarity index 97% rename from test/src/core/pro_video_editor/providers/sound_picker_search_provider_test.dart rename to test/src/features/sound/providers/sound_picker_search_provider_test.dart index 29e85d3..d6e3832 100644 --- a/test/src/core/pro_video_editor/providers/sound_picker_search_provider_test.dart +++ b/test/src/features/sound/providers/sound_picker_search_provider_test.dart @@ -10,8 +10,8 @@ import 'package:poptart_lex/com/atproto/repo/strong_ref.dart'; import 'package:spark/src/core/network/atproto/data/models/models.dart'; import 'package:spark/src/core/network/atproto/data/repositories/sound_repository.dart'; import 'package:spark/src/core/providers/debounce_scheduler.dart'; -import 'package:spark/src/core/pro_video_editor/providers/sound_picker_search_provider.dart'; -import 'package:spark/src/core/pro_video_editor/providers/sound_picker_search_state.dart'; +import 'package:spark/src/features/sound/providers/sound_picker_search_provider.dart'; +import 'package:spark/src/features/sound/providers/sound_picker_search_state.dart'; import 'package:spark/src/core/utils/logging/log_service.dart'; import 'package:sprk_poptart/so/sprk/sound/defs/audio_details.dart'; diff --git a/test/src/core/pro_video_editor/ui/widgets/audio/audio_selection_bottom_sheet_test.dart b/test/src/features/sound/ui/widgets/audio_selection_bottom_sheet_test.dart similarity index 94% rename from test/src/core/pro_video_editor/ui/widgets/audio/audio_selection_bottom_sheet_test.dart rename to test/src/features/sound/ui/widgets/audio_selection_bottom_sheet_test.dart index ff79267..1704e8e 100644 --- a/test/src/core/pro_video_editor/ui/widgets/audio/audio_selection_bottom_sheet_test.dart +++ b/test/src/features/sound/ui/widgets/audio_selection_bottom_sheet_test.dart @@ -6,12 +6,12 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:pro_image_editor/pro_image_editor.dart'; import 'package:spark/src/core/design_system/components/atoms/buttons/app_button.dart'; import 'package:spark/src/core/l10n/app_localizations.dart'; -import 'package:spark/src/core/pro_video_editor/providers/sound_picker_search_provider.dart'; -import 'package:spark/src/core/pro_video_editor/providers/sound_picker_search_state.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_controller.dart'; -import 'package:spark/src/core/pro_video_editor/ui/controllers/audio_audition_playback.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_selection_bottom_sheet.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/audio_track_list_section.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_controller.dart'; +import 'package:spark/src/features/sound/controllers/audio_audition_playback.dart'; +import 'package:spark/src/features/sound/ui/widgets/audio_selection_bottom_sheet.dart'; +import 'package:spark/src/features/sound/providers/sound_picker_search_provider.dart'; +import 'package:spark/src/features/sound/providers/sound_picker_search_state.dart'; +import 'package:spark/src/features/sound/ui/widgets/audio_track_list_section.dart'; void main() { testWidgets('cancel while opening does not present a stale picker', ( diff --git a/test/src/core/pro_video_editor/ui/widgets/audio/sound_picker_sheet_scaffold_test.dart b/test/src/features/sound/ui/widgets/sound_picker_sheet_scaffold_test.dart similarity index 92% rename from test/src/core/pro_video_editor/ui/widgets/audio/sound_picker_sheet_scaffold_test.dart rename to test/src/features/sound/ui/widgets/sound_picker_sheet_scaffold_test.dart index d49ba29..75bde22 100644 --- a/test/src/core/pro_video_editor/ui/widgets/audio/sound_picker_sheet_scaffold_test.dart +++ b/test/src/features/sound/ui/widgets/sound_picker_sheet_scaffold_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:spark/src/core/pro_video_editor/ui/widgets/audio/sound_picker_sheet_scaffold.dart'; +import 'package:spark/src/features/sound/ui/widgets/sound_picker_sheet_scaffold.dart'; void main() { testWidgets('provides a visible Material surface for ListTile effects', ( -- 2.51.2