sojorn/sojorn_app/lib/services/video_stitching_service.dart
Patrick Britton 3c4680bdd7 Initial commit: Complete threaded conversation system with inline replies
**Major Features Added:**
- **Inline Reply System**: Replace compose screen with inline reply boxes
- **Thread Navigation**: Parent/child navigation with jump functionality
- **Chain Flow UI**: Reply counts, expand/collapse animations, visual hierarchy
- **Enhanced Animations**: Smooth transitions, hover effects, micro-interactions

 **Frontend Changes:**
- **ThreadedCommentWidget**: Complete rewrite with animations and navigation
- **ThreadNode Model**: Added parent references and descendant counting
- **ThreadedConversationScreen**: Integrated navigation handlers
- **PostDetailScreen**: Replaced with threaded conversation view
- **ComposeScreen**: Added reply indicators and context
- **PostActions**: Fixed visibility checks for chain buttons

 **Backend Changes:**
- **API Route**: Added /posts/:id/thread endpoint
- **Post Repository**: Include allow_chain and visibility fields in feed
- **Thread Handler**: Support for fetching post chains

 **UI/UX Improvements:**
- **Reply Context**: Clear indication when replying to specific posts
- **Character Counting**: 500 character limit with live counter
- **Visual Hierarchy**: Depth-based indentation and styling
- **Smooth Animations**: SizeTransition, FadeTransition, hover states
- **Chain Navigation**: Parent/child buttons with visual feedback

 **Technical Enhancements:**
- **Animation Controllers**: Proper lifecycle management
- **State Management**: Clean separation of concerns
- **Navigation Callbacks**: Reusable navigation system
- **Error Handling**: Graceful fallbacks and user feedback

This creates a Reddit-style threaded conversation experience with smooth
animations, inline replies, and intuitive navigation between posts in a chain.
2026-01-30 07:40:19 -06:00

58 lines
2.2 KiB
Dart

import 'dart:io';
import 'package:ffmpeg_kit_flutter_new/ffmpeg_kit.dart';
import 'package:ffmpeg_kit_flutter_new/ffmpeg_session.dart';
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
import 'package:path_provider/path_provider.dart';
class VideoStitchingService {
/// Stitches multiple video files into a single video file using FFmpeg.
///
/// Returns the stitched file, or null if stitching failed or input is empty.
static Future<File?> stitchVideos(List<File> segments) async {
if (segments.isEmpty) return null;
if (segments.length == 1) return segments.first;
try {
// 1. Create a temporary file listing all segments for FFmpeg concat demuxer
final tempDir = await getTemporaryDirectory();
final listFile = File('${tempDir.path}/segments_list.txt');
final buffer = StringBuffer();
for (final segment in segments) {
// FFmpeg requires safe paths (escaping special chars might be needed, but usually basic paths are fine)
// IMPORTANT: pathways in list file for concat demuxer must be absolute.
buffer.writeln("file '${segment.path}'");
}
await listFile.writeAsString(buffer.toString());
// 2. Define output path
final outputFile = File('${tempDir.path}/stitched_${DateTime.now().millisecondsSinceEpoch}.mp4');
// 3. Execute FFmpeg command
// -f concat: format
// -safe 0: allow unsafe paths (required for absolute paths)
// -i listFile: input list
// -c copy: stream copy (fast, no re-encoding)
final command = "-f concat -safe 0 -i '${listFile.path}' -c copy '${outputFile.path}'";
final session = await FFmpegKit.execute(command);
final returnCode = await session.getReturnCode();
if (ReturnCode.isSuccess(returnCode)) {
return outputFile;
} else {
print("Stitching failed with return code: $returnCode");
// Fallback: return the last segment or first one to at least save something?
// For strict correctness, return null or throw.
// Let's print logs.
final logs = await session.getOutput();
print("FFmpeg Logs: $logs");
return null;
}
} catch (e) {
print("Stitching Error: $e");
return null;
}
}
}