fix/refactor: IOS тоже члены общества!!! Не полноценные пока что но все-же. Теперь при нажатии по кнопке входа bottom sheet с условиями открывается сам.

This commit is contained in:
Jganenokk
2026-08-20 17:21:59 +07:00
parent 3e4edc535c
commit 0030ded1f4
32 changed files with 1242 additions and 99 deletions
@@ -4,17 +4,18 @@ import 'package:flutter/services.dart';
import '../utils/logger.dart';
/// Нативная запись видео-кружка (Android, Camera2 + MediaRecorder): пишет
/// квадрат сразу при съёмке — как официальный клиент (по умолчанию 480×480@30,
/// размер и fps настраиваются в дев-меню). Превью отдаётся через Flutter
/// [Texture] по [textureId]. media3-перекод не используется (серверный
/// Нативная запись видео-кружка: пишет квадрат сразу при съёмке — как
/// официальный клиент (по умолчанию 480×480@30, размер и fps настраиваются
/// в дев-меню). На Android — Camera2 + MediaRecorder, на iOS —
/// AVCaptureSession + AVAssetWriter. Превью отдаётся через Flutter
/// [Texture] по [textureId]. Перекодирование не используется (серверный
/// валидатор принимает только нативно записанный MP4).
class NativeVideoNoteRecorder {
static const _channel = MethodChannel('ru.komet.app/video_note');
int? textureId;
bool hasFlash = false;
bool get isAvailable => Platform.isAndroid;
bool get isAvailable => Platform.isAndroid || Platform.isIOS;
Future<bool> requestPermission() async {
if (!isAvailable) return false;
+10 -8
View File
@@ -26,22 +26,24 @@ class OpusOggEncoder {
/// Лениво загружает libopus и инициализирует opus_dart: на Windows —
/// вендоренную `opus.dll` рядом с exe, на Android — через
/// `opus_flutter_android`. Возвращает `false`, если кодек недоступен.
/// `opus_flutter_android`, на iOS/macOS — статически слинкованную
/// `ogg_opus_player` (см. `-force_load` в ios/Podfile).
/// Возвращает `false`, если кодек недоступен.
static Future<bool> ensureAvailable() async {
if (_initialized) return _available;
_initialized = true;
try {
// libopus.so на Android бандлится плагином opus_flutter_android,
// opus.dll — вендоренная рядом с exe на Windows.
final String libName;
if (Platform.isWindows) {
libName = 'opus.dll';
final DynamicLibrary lib;
if (Platform.isIOS || Platform.isMacOS) {
lib = DynamicLibrary.process();
} else if (Platform.isWindows) {
lib = DynamicLibrary.open('opus.dll');
} else if (Platform.isAndroid) {
libName = 'libopus.so';
lib = DynamicLibrary.open('libopus.so');
} else {
return false;
}
initOpus(DynamicLibrary.open(libName) as dynamic);
initOpus(lib as dynamic);
_available = true;
} catch (e) {
logger.w('OpusOggEncoder: libopus недоступна: $e');
+4 -4
View File
@@ -5,14 +5,14 @@ import 'package:flutter/services.dart';
import '../utils/logger.dart';
/// Центр-кроп записанного видео в квадрат для видеосообщений-кружков.
/// На Android выполняется нативно (media3 Transformer, без искажений
/// заполняет квадрат и обрезает лишнее по бокам). На других платформах
/// возвращает `null` (кружки там не записываются).
/// Выполняется нативно: на Android — media3 Transformer, на iOS
/// AVAssetExportSession. Без искажений: заполняет квадрат и обрезает
/// лишнее по бокам. На других платформах возвращает `null`.
class VideoNoteCropper {
static const _channel = MethodChannel('ru.komet.app/video');
static Future<String?> cropSquare(String input, {int size = 480}) async {
if (!Platform.isAndroid) return null;
if (!Platform.isAndroid && !Platform.isIOS) return null;
try {
final dot = input.lastIndexOf('.');
final base = dot > 0 ? input.substring(0, dot) : input;
+11 -9
View File
@@ -68,23 +68,25 @@ class VideoExportSpec {
class VideoTranscoder {
static const _channel = MethodChannel('ru.komet.app/video');
static bool get _native => Platform.isAndroid || Platform.isIOS;
static Process? _desktopProcess;
static bool _desktopCancelled = false;
static bool get supported =>
Platform.isAndroid || (DesktopVideoProbe.supported && _ffmpegReady);
_native || (DesktopVideoProbe.supported && _ffmpegReady);
static bool _ffmpegReady = false;
static Future<bool> ensureAvailable() async {
if (Platform.isAndroid) return true;
if (_native) return true;
if (!DesktopVideoProbe.supported) return false;
_ffmpegReady = await DesktopVideoProbe.toolsAvailable();
return _ffmpegReady;
}
static Future<VideoInfo?> probe(String path) async {
if (Platform.isAndroid) {
if (_native) {
try {
final res = await _channel.invokeMapMethod<String, dynamic>('probe', {
'input': path,
@@ -113,7 +115,7 @@ class VideoTranscoder {
bool precise = false,
}) async {
if (timesMs.isEmpty) return const [];
if (Platform.isAndroid) {
if (_native) {
try {
final res = await _channel.invokeListMethod<Object?>('frames', {
'input': path,
@@ -150,13 +152,13 @@ class VideoTranscoder {
VideoExportSpec spec, {
void Function(double progress)? onProgress,
}) async {
if (Platform.isAndroid) return _exportAndroid(spec, onProgress);
if (_native) return _exportNative(spec, onProgress);
if (!await ensureAvailable()) return false;
return _exportFfmpeg(spec, onProgress);
}
static Future<void> cancel() async {
if (Platform.isAndroid) {
if (_native) {
try {
await _channel.invokeMethod<void>('editCancel');
} catch (_) {}
@@ -166,7 +168,7 @@ class VideoTranscoder {
_desktopProcess?.kill();
}
static Future<bool> _exportAndroid(
static Future<bool> _exportNative(
VideoExportSpec spec,
void Function(double)? onProgress,
) async {
@@ -179,7 +181,7 @@ class VideoTranscoder {
} catch (_) {}
});
try {
final ok = await _channel.invokeMethod<bool>('edit', _androidArgs(spec));
final ok = await _channel.invokeMethod<bool>('edit', _nativeArgs(spec));
return ok == true;
} catch (e) {
logger.w('VideoTranscoder.export: $e');
@@ -189,7 +191,7 @@ class VideoTranscoder {
}
}
static Map<String, dynamic> _androidArgs(VideoExportSpec spec) {
static Map<String, dynamic> _nativeArgs(VideoExportSpec spec) {
final crop = spec.crop;
return {
'input': spec.input,