#962 video: A-B repeat
This commit is contained in:
parent
8555322460
commit
46aef919be
16 changed files with 572 additions and 124 deletions
|
@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
- Collection: support for Fairphone burst pattern
|
||||
- Collection: allow using tags/make/model when bulk renaming
|
||||
- Video: A-B repeat
|
||||
- Settings: hidden items can be toggled
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
"actionRemove": "Remove",
|
||||
"resetTooltip": "Reset",
|
||||
"saveTooltip": "Save",
|
||||
"stopTooltip": "Stop",
|
||||
"pickTooltip": "Pick",
|
||||
|
||||
"doubleBackExitMessage": "Tap “back” again to exit.",
|
||||
|
@ -127,6 +128,10 @@
|
|||
"videoActionSkip10": "Seek forward 10 seconds",
|
||||
"videoActionSelectStreams": "Select tracks",
|
||||
"videoActionSetSpeed": "Playback speed",
|
||||
"videoActionABRepeat": "A-B repeat",
|
||||
|
||||
"videoRepeatActionSetStart": "Set start",
|
||||
"videoRepeatActionSetEnd": "Set end",
|
||||
|
||||
"viewerActionSettings": "Settings",
|
||||
"viewerActionLock": "Lock viewer",
|
||||
|
|
|
@ -119,7 +119,10 @@ class AIcons {
|
|||
static const pause = Icons.pause;
|
||||
static const print = Icons.print_outlined;
|
||||
static const refresh = Icons.refresh_outlined;
|
||||
static const repeat = Icons.repeat_outlined;
|
||||
static final repeatOff = MdiIcons.repeatOff;
|
||||
static const replay10 = Icons.replay_10_outlined;
|
||||
static final resetBounds = MdiIcons.rayStartEnd;
|
||||
static const reverse = Icons.invert_colors_outlined;
|
||||
static const skip10 = Icons.forward_10_outlined;
|
||||
static const reset = Icons.restart_alt_outlined;
|
||||
|
@ -131,6 +134,8 @@ class AIcons {
|
|||
static const select = Icons.select_all_outlined;
|
||||
static const setAs = Icons.wallpaper_outlined;
|
||||
static final setCover = MdiIcons.imageEditOutline;
|
||||
static final setEnd = MdiIcons.rayEnd;
|
||||
static final setStart = MdiIcons.rayStart;
|
||||
static const share = Icons.share_outlined;
|
||||
static const show = Icons.visibility_outlined;
|
||||
static final showFullscreen = MdiIcons.arrowExpand;
|
||||
|
|
|
@ -36,6 +36,7 @@ extension ExtraEntryActionView on EntryAction {
|
|||
l10n.videoActionMute,
|
||||
EntryAction.videoSelectStreams => l10n.videoActionSelectStreams,
|
||||
EntryAction.videoSetSpeed => l10n.videoActionSetSpeed,
|
||||
EntryAction.videoABRepeat => l10n.videoActionABRepeat,
|
||||
EntryAction.videoSettings => l10n.viewerActionSettings,
|
||||
EntryAction.videoTogglePlay =>
|
||||
// different data depending on toggle state
|
||||
|
@ -110,6 +111,7 @@ extension ExtraEntryActionView on EntryAction {
|
|||
AIcons.mute,
|
||||
EntryAction.videoSelectStreams => AIcons.streams,
|
||||
EntryAction.videoSetSpeed => AIcons.speed,
|
||||
EntryAction.videoABRepeat => AIcons.repeat,
|
||||
EntryAction.videoSettings => AIcons.videoSettings,
|
||||
EntryAction.videoTogglePlay =>
|
||||
// different data depending on toggle state
|
||||
|
|
|
@ -94,6 +94,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
return !settings.useTvLayout && targetEntry.isPureVideo;
|
||||
case EntryAction.videoSelectStreams:
|
||||
case EntryAction.videoSetSpeed:
|
||||
case EntryAction.videoABRepeat:
|
||||
case EntryAction.videoSettings:
|
||||
case EntryAction.videoTogglePlay:
|
||||
case EntryAction.videoReplay10:
|
||||
|
@ -229,6 +230,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
case EntryAction.videoToggleMute:
|
||||
case EntryAction.videoSelectStreams:
|
||||
case EntryAction.videoSetSpeed:
|
||||
case EntryAction.videoABRepeat:
|
||||
case EntryAction.videoSettings:
|
||||
case EntryAction.videoTogglePlay:
|
||||
case EntryAction.videoReplay10:
|
||||
|
|
|
@ -64,6 +64,8 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
await _showStreamSelectionDialog(context, controller);
|
||||
case EntryAction.videoSetSpeed:
|
||||
await _showSpeedDialog(context, controller);
|
||||
case EntryAction.videoABRepeat:
|
||||
controller.toggleABRepeat();
|
||||
case EntryAction.videoSettings:
|
||||
await _showSettings(context, controller);
|
||||
case EntryAction.videoTogglePlay:
|
||||
|
|
78
lib/widgets/viewer/overlay/video/ab_repeat.dart
Normal file
78
lib/widgets/viewer/overlay/video/ab_repeat.dart
Normal file
|
@ -0,0 +1,78 @@
|
|||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
|
||||
import 'package:aves_video/aves_video.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class VideoABRepeatOverlay extends StatefulWidget {
|
||||
final AvesVideoController? controller;
|
||||
final Animation<double> scale;
|
||||
|
||||
const VideoABRepeatOverlay({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.scale,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _VideoABRepeatOverlayState();
|
||||
}
|
||||
|
||||
class _VideoABRepeatOverlayState extends State<VideoABRepeatOverlay> {
|
||||
Animation<double> get scale => widget.scale;
|
||||
|
||||
AvesVideoController? get controller => widget.controller;
|
||||
|
||||
ValueNotifier<ABRepeat?> get abRepeatNotifier => controller?.abRepeatNotifier ?? ValueNotifier(null);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
|
||||
return ValueListenableBuilder<ABRepeat?>(
|
||||
valueListenable: abRepeatNotifier,
|
||||
builder: (context, abRepeat, child) {
|
||||
if (abRepeat == null) return const SizedBox();
|
||||
|
||||
Widget boundButton;
|
||||
if (abRepeat.start == null) {
|
||||
boundButton = IconButton(
|
||||
icon: Icon(AIcons.setStart),
|
||||
onPressed: controller?.setABRepeatStart,
|
||||
tooltip: l10n.videoRepeatActionSetStart,
|
||||
);
|
||||
} else if (abRepeat.end == null) {
|
||||
boundButton = IconButton(
|
||||
icon: Icon(AIcons.setEnd),
|
||||
onPressed: controller?.setABRepeatEnd,
|
||||
tooltip: l10n.videoRepeatActionSetEnd,
|
||||
);
|
||||
} else {
|
||||
boundButton = IconButton(
|
||||
icon: Icon(AIcons.resetBounds),
|
||||
onPressed: controller?.resetABRepeat,
|
||||
tooltip: l10n.resetTooltip,
|
||||
);
|
||||
}
|
||||
return Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
OverlayButton(
|
||||
scale: scale,
|
||||
child: boundButton,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
OverlayButton(
|
||||
scale: scale,
|
||||
child: IconButton(
|
||||
icon: Icon(AIcons.repeatOff),
|
||||
onPressed: () => controller?.toggleABRepeat(),
|
||||
tooltip: l10n.stopTooltip,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -37,6 +37,8 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
|
|||
|
||||
bool get isPlaying => controller?.isPlaying ?? false;
|
||||
|
||||
ValueNotifier<ABRepeat?> get abRepeatNotifier => controller?.abRepeatNotifier ?? ValueNotifier(null);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final blurred = settings.enableBlurEffect;
|
||||
|
@ -69,8 +71,7 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
|
|||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: kMinInteractiveDimension),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Themes.overlayBackgroundColor(brightness: theme.brightness, blurred: blurred),
|
||||
border: AvesBorder.border(context),
|
||||
|
@ -80,8 +81,21 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
|
|||
data: MediaQuery.of(context).copyWith(
|
||||
textScaler: TextScaler.noScaling,
|
||||
),
|
||||
child: Column(
|
||||
child: ValueListenableBuilder<ABRepeat?>(
|
||||
valueListenable: abRepeatNotifier,
|
||||
builder: (context, abRepeat, child) {
|
||||
return Stack(
|
||||
fit: StackFit.passthrough,
|
||||
children: [
|
||||
if (abRepeat != null) ...[
|
||||
_buildABRepeatMark(context, abRepeat.start),
|
||||
_buildABRepeatMark(context, abRepeat.end),
|
||||
],
|
||||
Container(
|
||||
key: _progressBarKey,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
|
@ -138,10 +152,29 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
|
|||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildABRepeatMark(BuildContext context, int? position) {
|
||||
if (controller == null || position == null) return const SizedBox();
|
||||
return Positioned(
|
||||
left: _progressToDx(position / controller!.duration),
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(left: AvesBorder.straightSide(context, width: 2)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -175,11 +208,20 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
|
|||
},
|
||||
);
|
||||
|
||||
RenderBox? _getProgressBarRenderBox() {
|
||||
return _progressBarKey.currentContext?.findRenderObject() as RenderBox?;
|
||||
}
|
||||
|
||||
void _seekFromTap(Offset globalPosition) async {
|
||||
if (controller == null) return;
|
||||
final keyContext = _progressBarKey.currentContext!;
|
||||
final box = keyContext.findRenderObject() as RenderBox;
|
||||
final localPosition = box.globalToLocal(globalPosition);
|
||||
await controller!.seekToProgress(localPosition.dx / box.size.width);
|
||||
final box = _getProgressBarRenderBox();
|
||||
if (controller == null || box == null) return;
|
||||
|
||||
final dx = box.globalToLocal(globalPosition).dx;
|
||||
await controller!.seekToProgress(dx / box.size.width);
|
||||
}
|
||||
|
||||
double? _progressToDx(double progress) {
|
||||
final box = _getProgressBarRenderBox();
|
||||
return box == null ? null : progress * box.size.width;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/video/ab_repeat.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/video/controls.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/video/progress_bar.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
|
@ -64,7 +65,14 @@ class _VideoControlOverlayState extends State<VideoControlOverlay> with SingleTi
|
|||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
return Column(
|
||||
children: [
|
||||
VideoABRepeatOverlay(
|
||||
controller: controller,
|
||||
scale: scale,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: VideoProgressBar(
|
||||
|
@ -79,6 +87,8 @@ class _VideoControlOverlayState extends State<VideoControlOverlay> with SingleTi
|
|||
onActionSelected: widget.onActionSelected,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -22,6 +22,7 @@ enum EntryAction {
|
|||
videoCaptureFrame,
|
||||
videoSelectStreams,
|
||||
videoSetSpeed,
|
||||
videoABRepeat,
|
||||
videoToggleMute,
|
||||
videoSettings,
|
||||
videoTogglePlay,
|
||||
|
@ -90,6 +91,7 @@ class EntryActions {
|
|||
EntryAction.videoCaptureFrame,
|
||||
EntryAction.videoSelectStreams,
|
||||
EntryAction.videoSetSpeed,
|
||||
EntryAction.videoABRepeat,
|
||||
EntryAction.videoToggleMute,
|
||||
EntryAction.videoSettings,
|
||||
EntryAction.videoTogglePlay,
|
||||
|
@ -110,6 +112,7 @@ class EntryActions {
|
|||
EntryAction.videoCaptureFrame,
|
||||
EntryAction.videoToggleMute,
|
||||
EntryAction.videoSetSpeed,
|
||||
EntryAction.videoABRepeat,
|
||||
EntryAction.videoSelectStreams,
|
||||
EntryAction.videoSettings,
|
||||
EntryAction.lockViewer,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
library aves_video;
|
||||
|
||||
export 'src/ab_repeat.dart';
|
||||
export 'src/controller.dart';
|
||||
export 'src/metadata.dart';
|
||||
export 'src/settings/subtitles.dart';
|
||||
|
|
33
plugins/aves_video/lib/src/ab_repeat.dart
Normal file
33
plugins/aves_video/lib/src/ab_repeat.dart
Normal file
|
@ -0,0 +1,33 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ABRepeat {
|
||||
final int? start, end;
|
||||
|
||||
ABRepeat({this.start, this.end});
|
||||
|
||||
ABRepeat sanitize() {
|
||||
if (start != null && end != null && start! > end!) {
|
||||
return ABRepeat(start: end, end: start);
|
||||
}
|
||||
return ABRepeat(start: start, end: end);
|
||||
}
|
||||
|
||||
int clamp(int position) => (start != null && end != null) ? position.clamp(start!, end!) : position;
|
||||
}
|
||||
|
||||
mixin ABRepeatMixin {
|
||||
int get currentPosition;
|
||||
|
||||
ValueNotifier<ABRepeat?> abRepeatNotifier = ValueNotifier(null);
|
||||
|
||||
void toggleABRepeat() => _setAbRepeat(abRepeatNotifier.value != null ? null : ABRepeat());
|
||||
|
||||
void resetABRepeat() => _setAbRepeat(ABRepeat());
|
||||
|
||||
void setABRepeatStart() => _setAbRepeat(ABRepeat(start: currentPosition, end: abRepeatNotifier.value?.end));
|
||||
|
||||
void setABRepeatEnd() => _setAbRepeat(ABRepeat(start: abRepeatNotifier.value?.start, end: currentPosition));
|
||||
|
||||
void _setAbRepeat(ABRepeat? v) => abRepeatNotifier.value = v?.sanitize();
|
||||
}
|
|
@ -15,7 +15,7 @@ abstract class AvesVideoControllerFactory {
|
|||
});
|
||||
}
|
||||
|
||||
abstract class AvesVideoController {
|
||||
abstract class AvesVideoController with ABRepeatMixin {
|
||||
final AvesEntryBase _entry;
|
||||
final PlaybackStateHandler playbackStateHandler;
|
||||
final VideoSettings settings;
|
||||
|
@ -96,6 +96,7 @@ abstract class AvesVideoController {
|
|||
|
||||
int get duration;
|
||||
|
||||
@override
|
||||
int get currentPosition;
|
||||
|
||||
double get progress {
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
import 'package:aves_video/aves_video.dart';
|
||||
import 'package:aves_video_mpv/src/tracks.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -12,7 +13,7 @@ import 'package:media_kit_video/media_kit_video.dart';
|
|||
class MpvVideoController extends AvesVideoController {
|
||||
late Player _instance;
|
||||
late VideoStatus _status;
|
||||
bool _firstFrameRendered = false;
|
||||
bool _firstFrameRendered = false, _abRepeatSeeking = false;
|
||||
final ValueNotifier<VideoController?> _controllerNotifier = ValueNotifier(null);
|
||||
final List<StreamSubscription> _subscriptions = [];
|
||||
final StreamController<VideoStatus> _statusStreamController = StreamController.broadcast();
|
||||
|
@ -82,22 +83,41 @@ class MpvVideoController extends AvesVideoController {
|
|||
|
||||
void _startListening() {
|
||||
_subscriptions.add(statusStream.listen((v) => _status = v));
|
||||
_subscriptions.add(_instance.stream.completed.listen((v) {
|
||||
if (v) {
|
||||
|
||||
final playerStream = _instance.stream;
|
||||
_subscriptions.add(playerStream.completed.listen((completed) {
|
||||
if (completed) {
|
||||
_statusStreamController.add(VideoStatus.completed);
|
||||
_completedNotifier.notify();
|
||||
}
|
||||
}));
|
||||
_subscriptions.add(_instance.stream.playing.listen((v) {
|
||||
_subscriptions.add(playerStream.playing.listen((playing) {
|
||||
if (status == VideoStatus.idle) return;
|
||||
_statusStreamController.add(v ? VideoStatus.playing : VideoStatus.paused);
|
||||
_statusStreamController.add(playing ? VideoStatus.playing : VideoStatus.paused);
|
||||
}));
|
||||
_subscriptions.add(_instance.stream.subtitle.listen((v) => _timedTextStreamController.add(v.isEmpty ? null : v[0])));
|
||||
_subscriptions.add(_instance.stream.videoParams.listen((v) => sarNotifier.value = v.par));
|
||||
_subscriptions.add(_instance.stream.log.listen((v) => debugPrint('libmpv log: $v')));
|
||||
_subscriptions.add(_instance.stream.error.listen((v) => debugPrint('libmpv error: $v')));
|
||||
_subscriptions.add(settings.updateStream.where((event) => event.key == SettingKeys.enableVideoHardwareAccelerationKey).listen((_) => _initController()));
|
||||
_subscriptions.add(settings.updateStream.where((event) => event.key == SettingKeys.videoLoopModeKey).listen((_) => _applyLoop()));
|
||||
_subscriptions.add(playerStream.position.listen((v) {
|
||||
final abRepeat = abRepeatNotifier.value;
|
||||
if (abRepeat != null && status == VideoStatus.playing) {
|
||||
final start = abRepeat.start;
|
||||
final end = abRepeat.end;
|
||||
if (start != null && end != null) {
|
||||
if (v.inMilliseconds < end) {
|
||||
_abRepeatSeeking = false;
|
||||
} else if (!_abRepeatSeeking) {
|
||||
_abRepeatSeeking = true;
|
||||
_instance.seek(Duration(milliseconds: start));
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
_subscriptions.add(playerStream.subtitle.listen((v) => _timedTextStreamController.add(v.isEmpty ? null : v[0])));
|
||||
_subscriptions.add(playerStream.videoParams.listen((v) => sarNotifier.value = v.par));
|
||||
_subscriptions.add(playerStream.log.listen((v) => debugPrint('libmpv log: $v')));
|
||||
_subscriptions.add(playerStream.error.listen((v) => debugPrint('libmpv error: $v')));
|
||||
|
||||
final settingsStream = settings.updateStream;
|
||||
_subscriptions.add(settingsStream.where((event) => event.key == SettingKeys.enableVideoHardwareAccelerationKey).listen((_) => _initController()));
|
||||
_subscriptions.add(settingsStream.where((event) => event.key == SettingKeys.videoLoopModeKey).listen((_) => _applyLoop()));
|
||||
}
|
||||
|
||||
void _stopListening() {
|
||||
|
@ -160,6 +180,7 @@ class MpvVideoController extends AvesVideoController {
|
|||
// and `PlayerConfiguration.ready` hook is useless.
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
}
|
||||
targetMillis = abRepeatNotifier.value?.clamp(targetMillis) ?? targetMillis;
|
||||
await _instance.seek(Duration(milliseconds: targetMillis));
|
||||
}
|
||||
|
||||
|
@ -355,45 +376,3 @@ class MpvVideoController extends AvesVideoController {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ExtraVideoTrack on VideoTrack {
|
||||
MediaStreamSummary toAves(int index) {
|
||||
return MediaStreamSummary(
|
||||
type: MediaStreamType.video,
|
||||
index: index,
|
||||
codecName: null,
|
||||
language: language,
|
||||
title: title,
|
||||
width: null,
|
||||
height: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ExtraAudioTrack on AudioTrack {
|
||||
MediaStreamSummary toAves(int index) {
|
||||
return MediaStreamSummary(
|
||||
type: MediaStreamType.audio,
|
||||
index: index,
|
||||
codecName: null,
|
||||
language: language,
|
||||
title: title,
|
||||
width: null,
|
||||
height: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ExtraSubtitleTrack on SubtitleTrack {
|
||||
MediaStreamSummary toAves(int index) {
|
||||
return MediaStreamSummary(
|
||||
type: MediaStreamType.text,
|
||||
index: index,
|
||||
codecName: null,
|
||||
language: language,
|
||||
title: title,
|
||||
width: null,
|
||||
height: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
44
plugins/aves_video_mpv/lib/src/tracks.dart
Normal file
44
plugins/aves_video_mpv/lib/src/tracks.dart
Normal file
|
@ -0,0 +1,44 @@
|
|||
import 'package:aves_video/aves_video.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
|
||||
extension ExtraVideoTrack on VideoTrack {
|
||||
MediaStreamSummary toAves(int index) {
|
||||
return MediaStreamSummary(
|
||||
type: MediaStreamType.video,
|
||||
index: index,
|
||||
codecName: null,
|
||||
language: language,
|
||||
title: title,
|
||||
width: null,
|
||||
height: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ExtraAudioTrack on AudioTrack {
|
||||
MediaStreamSummary toAves(int index) {
|
||||
return MediaStreamSummary(
|
||||
type: MediaStreamType.audio,
|
||||
index: index,
|
||||
codecName: null,
|
||||
language: language,
|
||||
title: title,
|
||||
width: null,
|
||||
height: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ExtraSubtitleTrack on SubtitleTrack {
|
||||
MediaStreamSummary toAves(int index) {
|
||||
return MediaStreamSummary(
|
||||
type: MediaStreamType.text,
|
||||
index: index,
|
||||
codecName: null,
|
||||
language: language,
|
||||
title: title,
|
||||
width: null,
|
||||
height: null,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,18 @@
|
|||
{
|
||||
"ar": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"be": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"bn": [
|
||||
"itemCount",
|
||||
"columnCount",
|
||||
|
@ -7,6 +21,7 @@
|
|||
"timeDays",
|
||||
"focalLength",
|
||||
"resetTooltip",
|
||||
"stopTooltip",
|
||||
"doubleBackExitMessage",
|
||||
"sourceStateLocatingCountries",
|
||||
"sourceStateLocatingPlaces",
|
||||
|
@ -62,6 +77,9 @@
|
|||
"videoActionSkip10",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionSetSpeed",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionSettings",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
|
@ -659,7 +677,15 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"ca": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"ckb": [
|
||||
"stopTooltip",
|
||||
"chipActionGoToPlacePage",
|
||||
"chipActionShowCountryStates",
|
||||
"entryActionFlip",
|
||||
|
@ -668,6 +694,9 @@
|
|||
"entryActionCast",
|
||||
"videoActionCaptureFrame",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"slideshowActionResume",
|
||||
|
@ -1199,6 +1228,10 @@
|
|||
],
|
||||
|
||||
"cs": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"collectionActionSetHome",
|
||||
"setHomeCustomCollection",
|
||||
"settingsThumbnailShowHdrIcon"
|
||||
|
@ -1233,6 +1266,7 @@
|
|||
"actionRemove",
|
||||
"resetTooltip",
|
||||
"saveTooltip",
|
||||
"stopTooltip",
|
||||
"pickTooltip",
|
||||
"doubleBackExitMessage",
|
||||
"doNotAskAgain",
|
||||
|
@ -1292,6 +1326,9 @@
|
|||
"videoActionSkip10",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionSetSpeed",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionSettings",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
|
@ -1889,8 +1926,19 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"de": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"el": [
|
||||
"stopTooltip",
|
||||
"entryActionCast",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
|
@ -1906,7 +1954,25 @@
|
|||
"settingsViewerShowHistogram"
|
||||
],
|
||||
|
||||
"es": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"eu": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"fa": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"coordinateFormatDms",
|
||||
"coordinateDms",
|
||||
"coordinateDmsNorth",
|
||||
|
@ -2346,11 +2412,15 @@
|
|||
"fi": [
|
||||
"focalLength",
|
||||
"clearTooltip",
|
||||
"stopTooltip",
|
||||
"chipActionFilterIn",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"videoActionUnmute",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"filterTypeRawLabel",
|
||||
"filterTypeSphericalVideoLabel",
|
||||
"filterTypeGeotiffLabel",
|
||||
|
@ -2887,10 +2957,18 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"gl": [
|
||||
"columnCount",
|
||||
"saveCopyButtonLabel",
|
||||
"applyTooltip",
|
||||
"stopTooltip",
|
||||
"chipActionGoToPlacePage",
|
||||
"chipActionLock",
|
||||
"chipActionShowCountryStates",
|
||||
|
@ -2899,6 +2977,9 @@
|
|||
"entryActionShareImageOnly",
|
||||
"entryActionShareVideoOnly",
|
||||
"entryActionCast",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"entryInfoActionExportMetadata",
|
||||
|
@ -3465,6 +3546,7 @@
|
|||
"actionRemove",
|
||||
"resetTooltip",
|
||||
"saveTooltip",
|
||||
"stopTooltip",
|
||||
"pickTooltip",
|
||||
"doubleBackExitMessage",
|
||||
"doNotAskAgain",
|
||||
|
@ -3524,6 +3606,9 @@
|
|||
"videoActionSkip10",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionSetSpeed",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionSettings",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
|
@ -4122,6 +4207,7 @@
|
|||
],
|
||||
|
||||
"hi": [
|
||||
"stopTooltip",
|
||||
"sourceStateCataloguing",
|
||||
"sourceStateLocatingCountries",
|
||||
"sourceStateLocatingPlaces",
|
||||
|
@ -4171,6 +4257,9 @@
|
|||
"videoActionSkip10",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionSetSpeed",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionSettings",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
|
@ -4768,7 +4857,32 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"hu": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"id": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"is": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"it": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"collectionActionSetHome",
|
||||
"setHomeCustomCollection",
|
||||
"settingsThumbnailShowHdrIcon"
|
||||
|
@ -4776,8 +4890,12 @@
|
|||
|
||||
"ja": [
|
||||
"applyTooltip",
|
||||
"stopTooltip",
|
||||
"chipActionCreateVault",
|
||||
"chipActionConfigureVault",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -4844,6 +4962,7 @@
|
|||
"timeMinutes",
|
||||
"timeDays",
|
||||
"focalLength",
|
||||
"stopTooltip",
|
||||
"chipActionGoToCountryPage",
|
||||
"chipActionGoToPlacePage",
|
||||
"chipActionGoToTagPage",
|
||||
|
@ -4894,6 +5013,9 @@
|
|||
"videoActionSkip10",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionSetSpeed",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionSettings",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
|
@ -5491,16 +5613,27 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"ko": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"lt": [
|
||||
"columnCount",
|
||||
"saveCopyButtonLabel",
|
||||
"applyTooltip",
|
||||
"stopTooltip",
|
||||
"chipActionGoToPlacePage",
|
||||
"chipActionLock",
|
||||
"chipActionShowCountryStates",
|
||||
"chipActionCreateVault",
|
||||
"chipActionConfigureVault",
|
||||
"entryActionCast",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -5613,6 +5746,7 @@
|
|||
"actionRemove",
|
||||
"resetTooltip",
|
||||
"saveTooltip",
|
||||
"stopTooltip",
|
||||
"pickTooltip",
|
||||
"doubleBackExitMessage",
|
||||
"doNotAskAgain",
|
||||
|
@ -5672,6 +5806,9 @@
|
|||
"videoActionSkip10",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionSetSpeed",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionSettings",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
|
@ -6270,7 +6407,11 @@
|
|||
],
|
||||
|
||||
"my": [
|
||||
"stopTooltip",
|
||||
"entryActionCast",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"accessibilityAnimationsRemove",
|
||||
"accessibilityAnimationsKeep",
|
||||
"overlayHistogramLuminance",
|
||||
|
@ -6385,7 +6526,11 @@
|
|||
],
|
||||
|
||||
"nb": [
|
||||
"stopTooltip",
|
||||
"entryActionCast",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -6412,12 +6557,16 @@
|
|||
|
||||
"nl": [
|
||||
"columnCount",
|
||||
"stopTooltip",
|
||||
"chipActionGoToPlacePage",
|
||||
"chipActionLock",
|
||||
"chipActionShowCountryStates",
|
||||
"entryActionShareImageOnly",
|
||||
"entryActionShareVideoOnly",
|
||||
"entryActionCast",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -6500,8 +6649,12 @@
|
|||
],
|
||||
|
||||
"nn": [
|
||||
"stopTooltip",
|
||||
"sourceStateCataloguing",
|
||||
"entryActionCast",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"accessibilityAnimationsKeep",
|
||||
"overlayHistogramNone",
|
||||
"overlayHistogramRGB",
|
||||
|
@ -6566,6 +6719,7 @@
|
|||
"actionRemove",
|
||||
"resetTooltip",
|
||||
"saveTooltip",
|
||||
"stopTooltip",
|
||||
"pickTooltip",
|
||||
"doubleBackExitMessage",
|
||||
"doNotAskAgain",
|
||||
|
@ -6621,6 +6775,9 @@
|
|||
"videoActionSkip10",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionSetSpeed",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionSettings",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
|
@ -7154,12 +7311,37 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"pl": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"pt": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"collectionActionSetHome",
|
||||
"setHomeCustomCollection",
|
||||
"settingsThumbnailShowHdrIcon"
|
||||
],
|
||||
|
||||
"ro": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"sat": [
|
||||
"welcomeOptional",
|
||||
"welcomeTermsToggle",
|
||||
|
@ -7187,6 +7369,7 @@
|
|||
"actionRemove",
|
||||
"resetTooltip",
|
||||
"saveTooltip",
|
||||
"stopTooltip",
|
||||
"pickTooltip",
|
||||
"doubleBackExitMessage",
|
||||
"doNotAskAgain",
|
||||
|
@ -7246,6 +7429,9 @@
|
|||
"videoActionSkip10",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionSetSpeed",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionSettings",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
|
@ -7843,6 +8029,13 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"sk": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"sl": [
|
||||
"itemCount",
|
||||
"columnCount",
|
||||
|
@ -7868,6 +8061,7 @@
|
|||
"actionRemove",
|
||||
"resetTooltip",
|
||||
"saveTooltip",
|
||||
"stopTooltip",
|
||||
"pickTooltip",
|
||||
"doubleBackExitMessage",
|
||||
"doNotAskAgain",
|
||||
|
@ -7927,6 +8121,9 @@
|
|||
"videoActionSkip10",
|
||||
"videoActionSelectStreams",
|
||||
"videoActionSetSpeed",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionSettings",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
|
@ -8525,6 +8722,10 @@
|
|||
],
|
||||
|
||||
"sv": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"widgetOpenPageViewer",
|
||||
"rootDirectoryDescription",
|
||||
"restrictedAccessDialogMessage",
|
||||
|
@ -8841,12 +9042,16 @@
|
|||
"applyButtonLabel",
|
||||
"saveCopyButtonLabel",
|
||||
"applyTooltip",
|
||||
"stopTooltip",
|
||||
"chipActionGoToPlacePage",
|
||||
"chipActionLock",
|
||||
"chipActionShowCountryStates",
|
||||
"chipActionCreateVault",
|
||||
"chipActionConfigureVault",
|
||||
"entryActionCast",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -9234,5 +9439,40 @@
|
|||
"filePickerOpenFrom",
|
||||
"filePickerNoItems",
|
||||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"tr": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"uk": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"vi": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
],
|
||||
|
||||
"zh_Hant": [
|
||||
"stopTooltip",
|
||||
"videoActionABRepeat",
|
||||
"videoRepeatActionSetStart",
|
||||
"videoRepeatActionSetEnd"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue