From 85d5cfeff815b206581b529affae602ffd92373a Mon Sep 17 00:00:00 2001 From: Joran Dob Date: Thu, 25 Jun 2020 12:54:34 +0200 Subject: [PATCH 1/4] Initial setup to make flutter subtitle wrapper work better. Added bloc and did some cleanup. --- example/.flutter-plugins-dependencies | 2 +- example/lib/main.dart | 2 +- example/pubspec.lock | 37 +++++- lib/bloc/subtitle_bloc.dart | 62 ++++++++++ lib/bloc/subtitle_event.dart | 19 ++++ lib/bloc/subtitle_state.dart | 36 ++++++ .../models/style/subtitle_border_style.dart | 0 .../models/style/subtitle_position.dart | 0 lib/data/models/style/subtitle_style.dart | 19 ++++ lib/{ => data}/models/subtitle.dart | 0 lib/{ => data}/models/subtitles.dart | 2 +- lib/data/repository/subtitle_repository.dart | 89 +++++++++++++++ lib/models/style/subtitle_style.dart | 18 --- lib/subtitle_controller.dart | 72 ------------ lib/subtitle_text_view.dart | 106 +++++------------- lib/subtitle_wrapper_package.dart | 23 +++- pubspec.lock | 37 +++++- pubspec.yaml | 5 +- 18 files changed, 351 insertions(+), 178 deletions(-) create mode 100644 lib/bloc/subtitle_bloc.dart create mode 100644 lib/bloc/subtitle_event.dart create mode 100644 lib/bloc/subtitle_state.dart rename lib/{ => data}/models/style/subtitle_border_style.dart (100%) rename lib/{ => data}/models/style/subtitle_position.dart (100%) create mode 100644 lib/data/models/style/subtitle_style.dart rename lib/{ => data}/models/subtitle.dart (100%) rename lib/{ => data}/models/subtitles.dart (55%) create mode 100644 lib/data/repository/subtitle_repository.dart delete mode 100644 lib/models/style/subtitle_style.dart diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index ea378ca..cce9ba4 100644 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"android":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"video_player_web","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player_web-0.1.2+3/","dependencies":[]}]},"dependencyGraph":[{"name":"video_player","dependencies":["video_player_web"]},{"name":"video_player_web","dependencies":[]},{"name":"wakelock","dependencies":[]}],"date_created":"2020-06-25 12:11:29.170164","version":"1.19.0-4.1.pre"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"android":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"video_player_web","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player_web-0.1.2+3/","dependencies":[]}]},"dependencyGraph":[{"name":"video_player","dependencies":["video_player_web"]},{"name":"video_player_web","dependencies":[]},{"name":"wakelock","dependencies":[]}],"date_created":"2020-06-25 12:52:02.340923","version":"1.19.0-4.1.pre"} \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index face541..fb2ec9d 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,7 +2,7 @@ import 'package:chewie/chewie.dart'; import 'package:flutter/material.dart'; import 'package:subtitle_wrapper_package/subtitle_controller.dart'; import 'package:subtitle_wrapper_package/subtitle_wrapper_package.dart'; -import 'package:subtitle_wrapper_package/models/style/subtitle_style.dart'; +import 'package:subtitle_wrapper_package/data/models/style/subtitle_style.dart'; import 'package:video_player/video_player.dart'; void main() => runApp(MyApp()); diff --git a/example/pubspec.lock b/example/pubspec.lock index 2acf883..18241ba 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.4.1" + bloc: + dependency: transitive + description: + name: bloc + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" boolean_selector: dependency: transitive description: @@ -50,6 +57,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.2" + equatable: + dependency: transitive + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" fake_async: dependency: transitive description: @@ -62,6 +76,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: transitive + description: + name: flutter_bloc + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -100,6 +121,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.8" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" open_iconic_flutter: dependency: transitive description: @@ -121,6 +149,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0+1" + provider: + dependency: transitive + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.3" sky_engine: dependency: transitive description: flutter @@ -219,4 +254,4 @@ packages: version: "0.1.4+1" sdks: dart: ">=2.7.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5 <2.0.0" + flutter: ">=1.16.0 <2.0.0" diff --git a/lib/bloc/subtitle_bloc.dart b/lib/bloc/subtitle_bloc.dart new file mode 100644 index 0000000..5ad6f13 --- /dev/null +++ b/lib/bloc/subtitle_bloc.dart @@ -0,0 +1,62 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; +import 'package:subtitle_wrapper_package/data/models/subtitle.dart'; +import 'package:subtitle_wrapper_package/data/models/subtitles.dart'; +import 'package:subtitle_wrapper_package/data/repository/subtitle_repository.dart'; +import 'package:subtitle_wrapper_package/subtitle_controller.dart'; +import 'package:video_player/video_player.dart'; + +part 'subtitle_event.dart'; +part 'subtitle_state.dart'; + +class SubtitleBloc extends Bloc { + final VideoPlayerController videoPlayerController; + final SubtitleRepository subtitleRepository; + + Subtitles subtitles; + + SubtitleBloc({ + this.videoPlayerController, + this.subtitleRepository, + }); + + @override + SubtitleState get initialState => SubtitleInitial(); + + @override + Stream mapEventToState( + SubtitleEvent event, + ) async* { + if (event is LoadSubtitle) { + yield* loadSubtitle(); + } else if (event is InitSubtitles) { + yield* initSubtitles(); + } + } + + Stream initSubtitles() async* { + yield SubtitleInitializating(); + yield SubtitleInitialized(); + } + + Stream loadSubtitle() async* { + yield LoadingSubtitle(); + VideoPlayerValue latestValue = videoPlayerController.value; + Duration videoPlayerPosition = latestValue.position; + if (videoPlayerPosition != null) { + Subtitle subtitle; + subtitles.subtitles.forEach((subtitleItem) { + if (videoPlayerPosition.inMilliseconds > + subtitleItem.startTime.inMilliseconds && + videoPlayerPosition.inMilliseconds < + subtitleItem.endTime.inMilliseconds) { + subtitle = subtitleItem; + } + }); + yield LoadedSubtitle(subtitle); + } + } +} diff --git a/lib/bloc/subtitle_event.dart b/lib/bloc/subtitle_event.dart new file mode 100644 index 0000000..94c3fe9 --- /dev/null +++ b/lib/bloc/subtitle_event.dart @@ -0,0 +1,19 @@ +part of 'subtitle_bloc.dart'; + +abstract class SubtitleEvent extends Equatable { + const SubtitleEvent(); +} + +class InitSubtitles extends SubtitleEvent { + final SubtitleController subtitleController; + + InitSubtitles({@required this.subtitleController}); + + @override + List get props => [this.subtitleController]; +} + +class LoadSubtitle extends SubtitleEvent { + @override + List get props => []; +} diff --git a/lib/bloc/subtitle_state.dart b/lib/bloc/subtitle_state.dart new file mode 100644 index 0000000..0bed3aa --- /dev/null +++ b/lib/bloc/subtitle_state.dart @@ -0,0 +1,36 @@ +part of 'subtitle_bloc.dart'; + +abstract class SubtitleState extends Equatable { + const SubtitleState(); +} + +class SubtitleInitial extends SubtitleState { + @override + List get props => []; +} + +class SubtitleInitializating extends SubtitleState { + @override + List get props => []; +} + +class SubtitleInitialized extends SubtitleState { + @override + List get props => []; +} + +class LoadingSubtitle extends SubtitleState { + @override + List get props => []; +} + +class LoadedSubtitle extends SubtitleState { + final Subtitle subtitle; + + LoadedSubtitle(this.subtitle); + + @override + List get props => [ + this.subtitle, + ]; +} diff --git a/lib/models/style/subtitle_border_style.dart b/lib/data/models/style/subtitle_border_style.dart similarity index 100% rename from lib/models/style/subtitle_border_style.dart rename to lib/data/models/style/subtitle_border_style.dart diff --git a/lib/models/style/subtitle_position.dart b/lib/data/models/style/subtitle_position.dart similarity index 100% rename from lib/models/style/subtitle_position.dart rename to lib/data/models/style/subtitle_position.dart diff --git a/lib/data/models/style/subtitle_style.dart b/lib/data/models/style/subtitle_style.dart new file mode 100644 index 0000000..f92cb66 --- /dev/null +++ b/lib/data/models/style/subtitle_style.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:subtitle_wrapper_package/data/models/style/subtitle_border_style.dart'; +import 'package:subtitle_wrapper_package/data/models/style/subtitle_position.dart'; + +class SubtitleStyle { + final bool hasBorder; + final SubtitleBorderStyle borderStyle; + final double fontSize; + final Color textColor; + final SubtitlePosition position; + + const SubtitleStyle({ + this.hasBorder = false, + this.borderStyle = const SubtitleBorderStyle(), + this.fontSize = 16, + this.textColor = Colors.black, + this.position = const SubtitlePosition(), + }); +} diff --git a/lib/models/subtitle.dart b/lib/data/models/subtitle.dart similarity index 100% rename from lib/models/subtitle.dart rename to lib/data/models/subtitle.dart diff --git a/lib/models/subtitles.dart b/lib/data/models/subtitles.dart similarity index 55% rename from lib/models/subtitles.dart rename to lib/data/models/subtitles.dart index d3e91bd..5034fab 100644 --- a/lib/models/subtitles.dart +++ b/lib/data/models/subtitles.dart @@ -1,4 +1,4 @@ -import 'package:subtitle_wrapper_package/models/subtitle.dart'; +import 'package:subtitle_wrapper_package/data/models/subtitle.dart'; class Subtitles { final List subtitles; diff --git a/lib/data/repository/subtitle_repository.dart b/lib/data/repository/subtitle_repository.dart new file mode 100644 index 0000000..89f55a1 --- /dev/null +++ b/lib/data/repository/subtitle_repository.dart @@ -0,0 +1,89 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:subtitle_wrapper_package/data/models/subtitle.dart'; +import 'package:subtitle_wrapper_package/data/models/subtitles.dart'; + +import 'package:http/http.dart' as http; +import 'package:subtitle_wrapper_package/subtitle_controller.dart'; + +abstract class SubtitleRepository { + Future getSubtitles(); +} + +class SubtitleDataRepository extends SubtitleRepository { + final SubtitleController subtitleController; + + SubtitleDataRepository({@required this.subtitleController}); + + @override + Future getSubtitles() async { + String subtitlesContent = subtitleController.subtitlesContent; + String subtitleUrl = subtitleController.subtitleUrl; + SubtitleDecoder subtitleDecoder = subtitleController.subtitleDecoder; + + RegExp regExp = new RegExp( + r"((\d{2}):(\d{2}):(\d{2})\.(\d+)) +--> +((\d{2}):(\d{2}):(\d{2})\.(\d{3})).*[\r\n]+\s*((?:(?!\r?\n\r?).)*(\r\n|\r|\n)(?:.*))", + caseSensitive: false, + multiLine: true, + ); + + if (subtitlesContent == null && subtitleUrl != null) { + http.Response response = await http.get(subtitleUrl); + if (response.statusCode == 200) { + subtitlesContent = subtitleDecoder == SubtitleDecoder.utf8 + ? utf8.decode( + response.bodyBytes, + allowMalformed: true, + ) + : latin1.decode(response.bodyBytes, allowInvalid: true); + } + } + + List matches = regExp.allMatches(subtitlesContent).toList(); + List subtitleList = List(); + + matches.forEach((RegExpMatch regExpMatch) { + int startTimeHours = int.parse(regExpMatch.group(2)); + int startTimeMinutes = int.parse(regExpMatch.group(3)); + int startTimeSeconds = int.parse(regExpMatch.group(4)); + int startTimeMilliseconds = int.parse(regExpMatch.group(5)); + + int endTimeHours = int.parse(regExpMatch.group(7)); + int endTimeMinutes = int.parse(regExpMatch.group(8)); + int endTimeSeconds = int.parse(regExpMatch.group(9)); + int endTimeMilliseconds = int.parse(regExpMatch.group(10)); + String text = removeAllHtmlTags(regExpMatch.group(11)); + + Duration startTime = Duration( + hours: startTimeHours, + minutes: startTimeMinutes, + seconds: startTimeSeconds, + milliseconds: startTimeMilliseconds); + Duration endTime = Duration( + hours: endTimeHours, + minutes: endTimeMinutes, + seconds: endTimeSeconds, + milliseconds: endTimeMilliseconds); + + subtitleList.add( + Subtitle(startTime: startTime, endTime: endTime, text: text.trim())); + }); + + Subtitles subtitles = Subtitles(subtitles: subtitleList); + return subtitles; + } + + String removeAllHtmlTags(String htmlText) { + RegExp exp = RegExp(r"(<[^>]*>)", multiLine: true, caseSensitive: true); + String newHtmlText = htmlText; + exp.allMatches(htmlText).toList().forEach((RegExpMatch regExpMathc) { + if (regExpMathc.group(0) == "
") { + newHtmlText = newHtmlText.replaceAll(regExpMathc.group(0), '\n'); + } else { + newHtmlText = newHtmlText.replaceAll(regExpMathc.group(0), ''); + } + }); + return newHtmlText; + } +} diff --git a/lib/models/style/subtitle_style.dart b/lib/models/style/subtitle_style.dart deleted file mode 100644 index 07ff347..0000000 --- a/lib/models/style/subtitle_style.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:subtitle_wrapper_package/models/style/subtitle_border_style.dart'; -import 'package:subtitle_wrapper_package/models/style/subtitle_position.dart'; - -class SubtitleStyle { - final bool hasBorder; - final SubtitleBorderStyle borderStyle; - final double fontSize; - final Color textColor; - final SubtitlePosition position; - - const SubtitleStyle( - {this.hasBorder = false, - this.borderStyle = const SubtitleBorderStyle(), - this.fontSize = 16, - this.textColor = Colors.black, - this.position = const SubtitlePosition()}); -} diff --git a/lib/subtitle_controller.dart b/lib/subtitle_controller.dart index fb43b4f..5cf364c 100644 --- a/lib/subtitle_controller.dart +++ b/lib/subtitle_controller.dart @@ -1,9 +1,3 @@ -import 'dart:convert'; - -import 'package:subtitle_wrapper_package/models/subtitle.dart'; -import 'package:subtitle_wrapper_package/models/subtitles.dart'; -import 'package:http/http.dart' as http; - class SubtitleController { String subtitlesContent; String subtitleUrl; @@ -16,72 +10,6 @@ class SubtitleController { this.showSubtitles = true, this.subtitleDecoder = SubtitleDecoder.utf8, }); - - Future getSubtitles() async { - RegExp regExp = new RegExp( - r"((\d{2}):(\d{2}):(\d{2})\.(\d+)) +--> +((\d{2}):(\d{2}):(\d{2})\.(\d{3})).*[\r\n]+\s*((?:(?!\r?\n\r?).)*(\r\n|\r|\n)(?:.*))", - caseSensitive: false, - multiLine: true, - ); - - if (subtitlesContent == null && subtitleUrl != null) { - http.Response response = await http.get(subtitleUrl); - if (response.statusCode == 200) { - subtitlesContent = subtitleDecoder == SubtitleDecoder.utf8 - ? utf8.decode( - response.bodyBytes, - allowMalformed: true, - ) - : latin1.decode(response.bodyBytes, allowInvalid: true); - } - } - - List matches = regExp.allMatches(subtitlesContent).toList(); - List subtitleList = List(); - - matches.forEach((RegExpMatch regExpMatch) { - int startTimeHours = int.parse(regExpMatch.group(2)); - int startTimeMinutes = int.parse(regExpMatch.group(3)); - int startTimeSeconds = int.parse(regExpMatch.group(4)); - int startTimeMilliseconds = int.parse(regExpMatch.group(5)); - - int endTimeHours = int.parse(regExpMatch.group(7)); - int endTimeMinutes = int.parse(regExpMatch.group(8)); - int endTimeSeconds = int.parse(regExpMatch.group(9)); - int endTimeMilliseconds = int.parse(regExpMatch.group(10)); - String text = removeAllHtmlTags(regExpMatch.group(11)); - - Duration startTime = Duration( - hours: startTimeHours, - minutes: startTimeMinutes, - seconds: startTimeSeconds, - milliseconds: startTimeMilliseconds); - Duration endTime = Duration( - hours: endTimeHours, - minutes: endTimeMinutes, - seconds: endTimeSeconds, - milliseconds: endTimeMilliseconds); - - subtitleList.add( - Subtitle(startTime: startTime, endTime: endTime, text: text.trim())); - }); - - Subtitles subtitles = Subtitles(subtitles: subtitleList); - return subtitles; - } - - String removeAllHtmlTags(String htmlText) { - RegExp exp = RegExp(r"(<[^>]*>)", multiLine: true, caseSensitive: true); - String newHtmlText = htmlText; - exp.allMatches(htmlText).toList().forEach((RegExpMatch regExpMathc) { - if (regExpMathc.group(0) == "
") { - newHtmlText = newHtmlText.replaceAll(regExpMathc.group(0), '\n'); - } else { - newHtmlText = newHtmlText.replaceAll(regExpMathc.group(0), ''); - } - }); - return newHtmlText; - } } enum SubtitleDecoder { diff --git a/lib/subtitle_text_view.dart b/lib/subtitle_text_view.dart index e18d20d..b68ffb2 100644 --- a/lib/subtitle_text_view.dart +++ b/lib/subtitle_text_view.dart @@ -1,90 +1,40 @@ import 'package:flutter/material.dart'; -import 'package:subtitle_wrapper_package/models/style/subtitle_style.dart'; -import 'package:subtitle_wrapper_package/models/subtitle.dart'; -import 'package:subtitle_wrapper_package/models/subtitles.dart'; -import 'package:subtitle_wrapper_package/subtitle_controller.dart'; -import 'package:video_player/video_player.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:subtitle_wrapper_package/bloc/subtitle_bloc.dart'; +import 'package:subtitle_wrapper_package/data/models/style/subtitle_style.dart'; -class SubtitleTextView extends StatefulWidget { - final SubtitleController subtitleController; - final VideoPlayerController videoPlayerController; +class SubtitleTextView extends StatelessWidget { final SubtitleStyle subtitleStyle; - const SubtitleTextView( - {Key key, - @required this.subtitleController, - this.videoPlayerController, - this.subtitleStyle}) + const SubtitleTextView({Key key, @required this.subtitleStyle}) : super(key: key); - @override - _SubtitleTextViewState createState() => - _SubtitleTextViewState(videoPlayerController); -} - -class _SubtitleTextViewState extends State { - final VideoPlayerController videoPlayerController; - Subtitle subtitle; - Function listener; - Subtitles subtitles; - - _SubtitleTextViewState(this.videoPlayerController); - - @override - void initState() { - listener = () => _subtitleWatcher(videoPlayerController); - videoPlayerController.addListener(listener); - - _subtitleWatcher(videoPlayerController); - super.initState(); - } - - _subtitleWatcher(VideoPlayerController videoPlayerController) async { - if (subtitles == null) { - subtitles = await widget.subtitleController.getSubtitles(); - } - VideoPlayerValue latestValue = videoPlayerController.value; - Duration videoPlayerPosition = latestValue.position; - if (videoPlayerPosition != null) { - subtitles.subtitles.forEach((Subtitle subtitleItem) { - if (videoPlayerPosition.inMilliseconds > - subtitleItem.startTime.inMilliseconds && - videoPlayerPosition.inMilliseconds < - subtitleItem.endTime.inMilliseconds) { - if (this.mounted) { - setState(() { - subtitle = subtitleItem; - }); - } - } - }); - } - } - - @override - void dispose() { - videoPlayerController.removeListener(listener); - super.dispose(); - } - @override Widget build(BuildContext context) { - return subtitle != null - ? Container( + SubtitleBloc subtitleBloc = BlocProvider.of(context); + return BlocConsumer( + listener: (context, state) { + if (state is SubtitleInitialized) { + subtitleBloc.add(LoadSubtitle()); + } + }, + builder: (context, state) { + if (state is LoadedSubtitle) { + return Container( child: Stack( children: [ - widget.subtitleStyle.hasBorder + subtitleStyle.hasBorder ? Center( child: Text( - subtitle.text, + state.subtitle.text, textAlign: TextAlign.center, style: TextStyle( - fontSize: widget.subtitleStyle.fontSize, + fontSize: subtitleStyle.fontSize, foreground: Paint() - ..style = widget.subtitleStyle.borderStyle.style + ..style = subtitleStyle.borderStyle.style ..strokeWidth = - widget.subtitleStyle.borderStyle.strokeWidth - ..color = widget.subtitleStyle.borderStyle.color, + subtitleStyle.borderStyle.strokeWidth + ..color = subtitleStyle.borderStyle.color, ), ), ) @@ -93,19 +43,21 @@ class _SubtitleTextViewState extends State { ), Center( child: Text( - subtitle.text, + state.subtitle.text, textAlign: TextAlign.center, style: TextStyle( - fontSize: widget.subtitleStyle.fontSize, - color: widget.subtitleStyle.textColor, + fontSize: subtitleStyle.fontSize, + color: subtitleStyle.textColor, ), ), ), ], ), - ) - : Container( - child: null, ); + } else { + return Container(); + } + }, + ); } } diff --git a/lib/subtitle_wrapper_package.dart b/lib/subtitle_wrapper_package.dart index edcbb3b..e774555 100644 --- a/lib/subtitle_wrapper_package.dart +++ b/lib/subtitle_wrapper_package.dart @@ -1,7 +1,10 @@ library subtitle_wrapper_package; import 'package:flutter/material.dart'; -import 'package:subtitle_wrapper_package/models/style/subtitle_style.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:subtitle_wrapper_package/bloc/subtitle_bloc.dart'; +import 'package:subtitle_wrapper_package/data/models/style/subtitle_style.dart'; +import 'package:subtitle_wrapper_package/data/repository/subtitle_repository.dart'; import 'package:subtitle_wrapper_package/subtitle_controller.dart'; import 'package:subtitle_wrapper_package/subtitle_text_view.dart'; import 'package:video_player/video_player.dart'; @@ -31,10 +34,20 @@ class SubTitleWrapper extends StatelessWidget { bottom: subtitleStyle.position.bottom, left: subtitleStyle.position.left, right: subtitleStyle.position.right, - child: SubtitleTextView( - subtitleController: subtitleController, - videoPlayerController: videoPlayerController, - subtitleStyle: subtitleStyle, + child: BlocProvider( + create: (context) => SubtitleBloc( + videoPlayerController: videoPlayerController, + subtitleRepository: SubtitleDataRepository( + subtitleController: subtitleController, + ), + )..add( + InitSubtitles( + subtitleController: subtitleController, + ), + ), + child: SubtitleTextView( + subtitleStyle: subtitleStyle, + ), ), ) : Container( diff --git a/pubspec.lock b/pubspec.lock index a4af3e5..479b008 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.4.1" + bloc: + dependency: "direct main" + description: + name: bloc + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" boolean_selector: dependency: transitive description: @@ -36,6 +43,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.14.12" + equatable: + dependency: "direct main" + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" fake_async: dependency: transitive description: @@ -48,6 +62,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -86,6 +107,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.8" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" path: dependency: transitive description: @@ -100,6 +128,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0+1" + provider: + dependency: transitive + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.3" sky_engine: dependency: transitive description: flutter @@ -184,4 +219,4 @@ packages: version: "0.1.2" sdks: dart: ">=2.7.0 <3.0.0" - flutter: ">=1.12.13+hotfix.4 <2.0.0" + flutter: ">=1.16.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index ea3cabe..5180522 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,10 +12,13 @@ dependencies: sdk: flutter video_player: ^0.10.2+1 http: ^0.12.0+2 + bloc: ^4.0.0 + flutter_bloc: ^4.0.0 + equatable: ^1.2.0 dev_dependencies: flutter_test: sdk: flutter # The following section is specific to Flutter. -flutter: \ No newline at end of file +flutter: From 2b746bfb45683d7880aae024130f7b89d01dcae7 Mon Sep 17 00:00:00 2001 From: Joran Dob Date: Fri, 26 Jun 2020 10:10:39 +0200 Subject: [PATCH 2/4] Made inital bloc version working. --- example/.flutter-plugins-dependencies | 2 +- example/lib/main.dart | 9 +++++---- lib/bloc/subtitle_bloc.dart | 28 ++++++++++++++------------- lib/bloc/subtitle_event.dart | 8 ++++++++ 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index cce9ba4..0235d95 100644 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"android":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"video_player_web","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player_web-0.1.2+3/","dependencies":[]}]},"dependencyGraph":[{"name":"video_player","dependencies":["video_player_web"]},{"name":"video_player_web","dependencies":[]},{"name":"wakelock","dependencies":[]}],"date_created":"2020-06-25 12:52:02.340923","version":"1.19.0-4.1.pre"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"android":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"video_player_web","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player_web-0.1.2+3/","dependencies":[]}]},"dependencyGraph":[{"name":"video_player","dependencies":["video_player_web"]},{"name":"video_player_web","dependencies":[]},{"name":"wakelock","dependencies":[]}],"date_created":"2020-06-26 09:37:29.012602","version":"1.19.0-4.1.pre"} \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index fb2ec9d..ae1b76e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -47,7 +47,7 @@ class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState( "https://d11b76aq44vj33.cloudfront.net/media/720/video/5def7824adbbc.mp4", - "https://duoidi6ujfbv.cloudfront.net/media/1337/subtitles/5e0f5802cb870.vtt"); + "https://pastebin.com/raw/ZWWAL7fK"); } class _MyHomePageState extends State { @@ -88,9 +88,10 @@ class _MyHomePageState extends State { child: SubTitleWrapper( videoPlayerController: chewieController.videoPlayerController, subtitleController: SubtitleController( - subtitleUrl: subtitleUrl, - showSubtitles: true, - subtitleDecoder: SubtitleDecoder.latin1), + subtitleUrl: subtitleUrl, + showSubtitles: true, + subtitleDecoder: SubtitleDecoder.utf8, + ), subtitleStyle: SubtitleStyle(textColor: Colors.white, hasBorder: true), videoChild: Chewie( diff --git a/lib/bloc/subtitle_bloc.dart b/lib/bloc/subtitle_bloc.dart index 5ad6f13..f25a38d 100644 --- a/lib/bloc/subtitle_bloc.dart +++ b/lib/bloc/subtitle_bloc.dart @@ -34,29 +34,31 @@ class SubtitleBloc extends Bloc { yield* loadSubtitle(); } else if (event is InitSubtitles) { yield* initSubtitles(); + } else if (event is UpdateLoadedSubtitle) { + yield LoadedSubtitle(event.subtitle); } } Stream initSubtitles() async* { yield SubtitleInitializating(); + subtitles = await subtitleRepository.getSubtitles(); yield SubtitleInitialized(); } Stream loadSubtitle() async* { yield LoadingSubtitle(); - VideoPlayerValue latestValue = videoPlayerController.value; - Duration videoPlayerPosition = latestValue.position; - if (videoPlayerPosition != null) { - Subtitle subtitle; - subtitles.subtitles.forEach((subtitleItem) { - if (videoPlayerPosition.inMilliseconds > - subtitleItem.startTime.inMilliseconds && - videoPlayerPosition.inMilliseconds < - subtitleItem.endTime.inMilliseconds) { - subtitle = subtitleItem; + videoPlayerController.addListener(() { + Duration videoPlayerPosition = videoPlayerController.value.position; + if (videoPlayerPosition != null) { + for (Subtitle subtitleItem in subtitles.subtitles) { + if (videoPlayerPosition.inMilliseconds > + subtitleItem.startTime.inMilliseconds && + videoPlayerPosition.inMilliseconds < + subtitleItem.endTime.inMilliseconds) { + add(UpdateLoadedSubtitle(subtitle: subtitleItem)); + } } - }); - yield LoadedSubtitle(subtitle); - } + } + }); } } diff --git a/lib/bloc/subtitle_event.dart b/lib/bloc/subtitle_event.dart index 94c3fe9..9b47289 100644 --- a/lib/bloc/subtitle_event.dart +++ b/lib/bloc/subtitle_event.dart @@ -17,3 +17,11 @@ class LoadSubtitle extends SubtitleEvent { @override List get props => []; } + +class UpdateLoadedSubtitle extends SubtitleEvent { + final Subtitle subtitle; + + UpdateLoadedSubtitle({this.subtitle}); + @override + List get props => []; +} From 3dfe037f3d63cb95dec4529903cb9a3a27f55365 Mon Sep 17 00:00:00 2001 From: Joran Dob Date: Tue, 28 Jul 2020 22:05:06 +0200 Subject: [PATCH 3/4] Update default codec Version 0.1.6 * Added support to select an specific decoder. Defaults to utf8 * Added support for dynamic setting of decoder depending on server site charset, Defaults to utf8 * Fixed issue related to fallback to utf8 --- lib/data/repository/subtitle_repository.dart | 51 +++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/data/repository/subtitle_repository.dart b/lib/data/repository/subtitle_repository.dart index 89f55a1..5ddc6cf 100644 --- a/lib/data/repository/subtitle_repository.dart +++ b/lib/data/repository/subtitle_repository.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; +import 'package:http_parser/http_parser.dart'; import 'package:subtitle_wrapper_package/data/models/subtitle.dart'; import 'package:subtitle_wrapper_package/data/models/subtitles.dart'; @@ -16,6 +17,29 @@ class SubtitleDataRepository extends SubtitleRepository { SubtitleDataRepository({@required this.subtitleController}); + SubtitleDecoder requestContentType(Map headers) { + Encoding encoding = _encodingForHeaders(headers); + if (encoding == latin1) { + return SubtitleDecoder.latin1; + } else { + return SubtitleDecoder.utf8; + } + } + + Encoding _encodingForHeaders(Map headers) => + encodingForCharset(_contentTypeForHeaders(headers).parameters['charset']); + + MediaType _contentTypeForHeaders(Map headers) { + var contentType = headers['content-type']; + if (contentType != null) return MediaType.parse(contentType); + return MediaType('application', 'octet-stream'); + } + + Encoding encodingForCharset(String charset, [Encoding fallback = utf8]) { + if (charset == null) return fallback; + return Encoding.getByName(charset) ?? fallback; + } + @override Future getSubtitles() async { String subtitlesContent = subtitleController.subtitlesContent; @@ -31,12 +55,27 @@ class SubtitleDataRepository extends SubtitleRepository { if (subtitlesContent == null && subtitleUrl != null) { http.Response response = await http.get(subtitleUrl); if (response.statusCode == 200) { - subtitlesContent = subtitleDecoder == SubtitleDecoder.utf8 - ? utf8.decode( - response.bodyBytes, - allowMalformed: true, - ) - : latin1.decode(response.bodyBytes, allowInvalid: true); + if (subtitleDecoder == SubtitleDecoder.utf8) { + subtitlesContent = utf8.decode( + response.bodyBytes, + allowMalformed: true, + ); + } else if (subtitleDecoder == SubtitleDecoder.latin1) { + subtitlesContent = + latin1.decode(response.bodyBytes, allowInvalid: true); + } else { + SubtitleDecoder subtitleServerDecoder = + requestContentType(response.headers); + if (subtitleServerDecoder == SubtitleDecoder.utf8) { + subtitlesContent = utf8.decode( + response.bodyBytes, + allowMalformed: true, + ); + } else if (subtitleServerDecoder == SubtitleDecoder.latin1) { + subtitlesContent = + latin1.decode(response.bodyBytes, allowInvalid: true); + } + } } } From 460f58328720539e74caf95d6400aad345f4b272 Mon Sep 17 00:00:00 2001 From: Joran Dob Date: Sat, 1 Aug 2020 19:36:10 +0200 Subject: [PATCH 4/4] Finalised version 1.0.0. Implemented: * Implemented BLoC pattern for handling subtitles and state changes. * Added support for loading local subtitles by using subtitle content on the controller. * Added support for srt. --- CHANGELOG.md | 6 +++ README.md | 18 ++++--- example/.flutter-plugins-dependencies | 2 +- example/lib/main.dart | 49 ++++++++++++-------- example/pubspec.lock | 6 +-- lib/bloc/subtitle_bloc.dart | 5 +- lib/data/repository/subtitle_repository.dart | 37 ++++++++++++--- lib/subtitle_controller.dart | 7 +++ pubspec.lock | 4 +- pubspec.yaml | 6 +-- 10 files changed, 94 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afa8c4f..47b6423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [1.0.0] - 1 august 2020. + +* Implemented BLoC pattern for handling subtitles and state changes. +* Added support for loading local subtitles by using subtitle content on the controller. +* Added support for srt. + ## [0.1.6] - 26 june 2020. * Added support to select an specific decoder. Defaults to utf8 diff --git a/README.md b/README.md index 5930353..f68b3c8 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,23 @@ # subtitle_wrapper_package -[![](https://img.shields.io/badge/pub-v0.0.5-brightgreen.svg)](https://pub.dev/packages/subtitle_wrapper_package) - +[![](https://img.shields.io/badge/pub-v1.0.0-brightgreen.svg)](https://pub.dev/packages/subtitle_wrapper_package) Subtitle Wrapper Plugin. ## Features -* Displaying of vtt subtitles -* Loading of vtt subtitles from network + +* Displaying of webvtt/srt subtitles +* Loading of webvtt/srt subtitles from network * Subtitle styling ## Installation This widget wraps the video player and displays the vtt subtitles on top. -```dart +``` dart @override Widget build(BuildContext context) { + return Padding( padding: const EdgeInsets.all(12.0), child: Card( @@ -31,11 +32,14 @@ This widget wraps the video player and displays the vtt subtitles on top. SubtitleStyle(textColor: Colors.white, hasBorder: true), videoChild: Chewie( controller: chewieController, - ))), + ), + ), + ), ); + } -``` +``` ## Example diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index 0a7333a..1ec52ea 100644 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"android":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"video_player_web","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player_web-0.1.2+3/","dependencies":[]}]},"dependencyGraph":[{"name":"video_player","dependencies":["video_player_web"]},{"name":"video_player_web","dependencies":[]},{"name":"wakelock","dependencies":[]}],"date_created":"2020-07-28 22:01:46.506896","version":"1.20.0-7.2.pre"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"android":[{"name":"video_player","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player-0.10.9+1/","dependencies":[]},{"name":"wakelock","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/wakelock-0.1.4+1/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"video_player_web","path":"/Users/joran.dob/Flutter/flutter/.pub-cache/hosted/pub.dartlang.org/video_player_web-0.1.2+3/","dependencies":[]}]},"dependencyGraph":[{"name":"video_player","dependencies":["video_player_web"]},{"name":"video_player_web","dependencies":[]},{"name":"wakelock","dependencies":[]}],"date_created":"2020-08-01 19:33:48.038110","version":"1.20.0-7.2.pre"} \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index ae1b76e..2369efc 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -81,23 +81,32 @@ class _MyHomePageState extends State { Widget build(BuildContext context) { ChewieController chewieController = getChewieController(); - return Padding( - padding: const EdgeInsets.all(12.0), - child: Card( - elevation: 2.0, - child: SubTitleWrapper( - videoPlayerController: chewieController.videoPlayerController, - subtitleController: SubtitleController( - subtitleUrl: subtitleUrl, - showSubtitles: true, - subtitleDecoder: SubtitleDecoder.utf8, + return Scaffold( + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(12.0), + child: Card( + elevation: 2.0, + child: SubTitleWrapper( + videoPlayerController: chewieController.videoPlayerController, + subtitleController: SubtitleController( + subtitleUrl: subtitleUrl, + showSubtitles: true, + subtitleDecoder: SubtitleDecoder.utf8, + subtitleType: SubtitleType.webvtt, + ), + subtitleStyle: SubtitleStyle( + textColor: Colors.white, + hasBorder: true, + ), + videoChild: Chewie( + controller: chewieController, + ), + ), + ), ), - subtitleStyle: - SubtitleStyle(textColor: Colors.white, hasBorder: true), - videoChild: Chewie( - controller: chewieController, - ), - ), + ], ), ); } @@ -105,10 +114,10 @@ class _MyHomePageState extends State { @override void dispose() { super.dispose(); -// if (videoPlayerController != null && chewieController != null) { -// videoPlayerController?.dispose(); -// chewieController?.dispose(); -// } + if (videoPlayerController != null && chewieController != null) { + videoPlayerController?.dispose(); + chewieController?.dispose(); + } debugPrint('videoPlayerController - dispose()'); } } diff --git a/example/pubspec.lock b/example/pubspec.lock index 79c4680..809342a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -14,7 +14,7 @@ packages: name: bloc url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "6.0.1" boolean_selector: dependency: transitive description: @@ -89,7 +89,7 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" + version: "6.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -202,7 +202,7 @@ packages: path: ".." relative: true source: path - version: "0.1.6" + version: "1.0.0" term_glyph: dependency: transitive description: diff --git a/lib/bloc/subtitle_bloc.dart b/lib/bloc/subtitle_bloc.dart index f25a38d..984deff 100644 --- a/lib/bloc/subtitle_bloc.dart +++ b/lib/bloc/subtitle_bloc.dart @@ -21,10 +21,7 @@ class SubtitleBloc extends Bloc { SubtitleBloc({ this.videoPlayerController, this.subtitleRepository, - }); - - @override - SubtitleState get initialState => SubtitleInitial(); + }) : super(SubtitleInitial()); @override Stream mapEventToState( diff --git a/lib/data/repository/subtitle_repository.dart b/lib/data/repository/subtitle_repository.dart index 5ddc6cf..d2673b7 100644 --- a/lib/data/repository/subtitle_repository.dart +++ b/lib/data/repository/subtitle_repository.dart @@ -46,12 +46,6 @@ class SubtitleDataRepository extends SubtitleRepository { String subtitleUrl = subtitleController.subtitleUrl; SubtitleDecoder subtitleDecoder = subtitleController.subtitleDecoder; - RegExp regExp = new RegExp( - r"((\d{2}):(\d{2}):(\d{2})\.(\d+)) +--> +((\d{2}):(\d{2}):(\d{2})\.(\d{3})).*[\r\n]+\s*((?:(?!\r?\n\r?).)*(\r\n|\r|\n)(?:.*))", - caseSensitive: false, - multiLine: true, - ); - if (subtitlesContent == null && subtitleUrl != null) { http.Response response = await http.get(subtitleUrl); if (response.statusCode == 200) { @@ -78,6 +72,37 @@ class SubtitleDataRepository extends SubtitleRepository { } } } + try { + if (subtitleController.subtitleType == SubtitleType.webvtt) { + return getSubtitlesData( + subtitlesContent, subtitleController.subtitleType); + } else if (subtitleController.subtitleType == SubtitleType.srt) { + return getSubtitlesData( + subtitlesContent, subtitleController.subtitleType); + } + } catch (e) { + throw "Error parsing subtitles $e"; + } + } + + Subtitles getSubtitlesData( + String subtitlesContent, SubtitleType subtitleType) { + RegExp regExp; + if (subtitleType == SubtitleType.webvtt) { + regExp = new RegExp( + r"((\d{2}):(\d{2}):(\d{2})\.(\d+)) +--> +((\d{2}):(\d{2}):(\d{2})\.(\d{3})).*[\r\n]+\s*((?:(?!\r?\n\r?).)*(\r\n|\r|\n)(?:.*))", + caseSensitive: false, + multiLine: true, + ); + } else if (subtitleType == SubtitleType.srt) { + regExp = new RegExp( + r"((\d{2}):(\d{2}):(\d{2})\,(\d+)) +--> +((\d{2}):(\d{2}):(\d{2})\,(\d{3})).*[\r\n]+\s*((?:(?!\r?\n\r?).)*(\r\n|\r|\n)(?:.*))", + caseSensitive: false, + multiLine: true, + ); + } else { + throw ("Incorrect subtitle type"); + } List matches = regExp.allMatches(subtitlesContent).toList(); List subtitleList = List(); diff --git a/lib/subtitle_controller.dart b/lib/subtitle_controller.dart index de01c62..bbd2068 100644 --- a/lib/subtitle_controller.dart +++ b/lib/subtitle_controller.dart @@ -3,12 +3,14 @@ class SubtitleController { String subtitleUrl; final bool showSubtitles; SubtitleDecoder subtitleDecoder; + SubtitleType subtitleType; SubtitleController({ this.subtitleUrl, this.subtitlesContent, this.showSubtitles = true, this.subtitleDecoder, + this.subtitleType = SubtitleType.webvtt, }); } @@ -16,3 +18,8 @@ enum SubtitleDecoder { utf8, latin1, } + +enum SubtitleType { + webvtt, + srt, +} diff --git a/pubspec.lock b/pubspec.lock index 4cec255..abbaaa4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,7 +14,7 @@ packages: name: bloc url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "6.0.1" boolean_selector: dependency: transitive description: @@ -75,7 +75,7 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" + version: "6.0.1" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index a38b019..784b134 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: subtitle_wrapper_package description: A Subtitle Wrapper package, this subtitle wrapper package displays subtitles for a video player. -version: 0.1.6 +version: 1.0.0 author: Joran Dob homepage: https://github.com/Joran-Dob/flutter_subtitle_wrapper @@ -12,8 +12,8 @@ dependencies: sdk: flutter video_player: ^0.10.2+1 http: ^0.12.0+2 - bloc: ^4.0.0 - flutter_bloc: ^4.0.0 + bloc: ^6.0.0 + flutter_bloc: ^6.0.0 equatable: ^1.2.0 http_parser: ^3.1.4