Merge branch 'feature/PSDK-1155/json_api' into 'develop'

PSDK-1155 - Поддержка json-провайдера

See merge request mobile/Flutter/nut_player!94
This commit is contained in:
Андрей Геращенко
2023-12-11 18:49:15 +03:00
14 changed files with 247 additions and 9 deletions
@@ -13,8 +13,8 @@ class StatisticRecord {
return StatisticRecord(
name: data['name'],
urlTemplate: data['url_template'],
start: data['start'],
delay: data['delay'],
start: data['start'] == null ? 0.0 : data['start'].toDouble(),
delay: data['delay'] == null ? 0.0 : data['delay'].toDouble(),
count: data['count'],
method: data['method'],
body: data['body'],
@@ -143,6 +143,7 @@ class UseLinkExampleState extends JsonState {
const UseLinkExampleState(
String title,
Key key,
String? urlPath,
List<OptionData> options,
int? initialIndex,
int? currentIndex,
@@ -153,24 +154,35 @@ class UseLinkExampleState extends JsonState {
): super(
title: title,
key: key,
urlPath: urlPath,
options: options,
initialIndex: initialIndex,
currentIndex: currentIndex,
parent: parent,
isFinal: isFinal,
isFinal: isFinal,
uiType: uiType,
createIndexedEvent: createIndexedEvent
);
factory UseLinkExampleState.create({JsonState? parent, int? selectedIndex}) {
var options = const [
OptionData(key: Key('ListElementUrl1ID'), title: 'Статистика (GET)', value: 'http://chest-101.gc.nut.team:8000/play/opt/5edd1215b2e34a3eb70654a117ea2935'),
OptionData(key: Key('ListElementUrl2ID'), title: 'Статистика (POST)', value: 'http://chest-101.gc.nut.team:8000/play/opt/5edd1215b2e34a3eb70654a117ea2937'),
OptionData(key: Key('ListElementUrl3ID'), title: 'Субтитры (SRT)', value: 'http://chest-101.gc.nut.team:8000/play/opt/e1dc888381e04b4290924dd9ec7b33ed'),
];
final String? currentUrlPath;
if (selectedIndex != null) {
currentUrlPath = options[selectedIndex].value;
} else {
currentUrlPath = null;
}
return UseLinkExampleState(
'3. Выберите пример ссылки',
const Key('ChooseUrlExampleListID'),
const [
OptionData(key: Key('ListElementUrl1ID'), title: 'Статистика (GET)', value: 'http://chest-101.gc.nut.team:8000/play/opt/5edd1215b2e34a3eb70654a117ea2935'),
OptionData(key: Key('ListElementUrl2ID'), title: 'Статистика (POST)', value: 'http://chest-101.gc.nut.team:8000/play/opt/5edd1215b2e34a3eb70654a117ea2937'),
OptionData(key: Key('ListElementUrl3ID'), title: 'Субтитры (SRT)', value: 'http://chest-101.gc.nut.team:8000/play/opt/e1dc888381e04b4290924dd9ec7b33ed'),
],
currentUrlPath,
options,
0,
selectedIndex,
parent,
@@ -206,7 +218,7 @@ class UseJsonOwnLinkState extends JsonState {
);
factory UseJsonOwnLinkState.create({JsonState? parent, String? urlPath, bool? isLive}) {
var finalUrlPath = urlPath ?? 'https://cloud.nut.tech/index.php/s/iY3KtaL7bekWnwM/download/IMG_3059.MP4';
var finalUrlPath = urlPath ?? '';
return UseJsonOwnLinkState(
'3. Укажите ссылку',
const Key('TypeOwnUrlViewID'),
@@ -40,6 +40,8 @@ class MainBloc extends Bloc<MainEvent, MainState> {
if (event.jsonState.isFinal && jsonUrlPath != null) {
if (event.jsonState is FirebaseChosenOptionState) {
provider = FirebaseProvider(jsonUrlPath);
} else if (event.jsonState is UseJsonOwnLinkState || event.jsonState is UseLinkExampleState) {
provider = JsonProvider(jsonUrlPath);
}
// TODO: Поддежка Json-конфигов
} else if (event.urlState.isFinal && urlUrlPath != null) {
+1
View File
@@ -7,6 +7,7 @@ export 'src/controller/video_player_controller.dart';
//
export 'src/provider/provider.dart';
export 'src/provider/common_provider.dart';
export 'src/provider/json_provider/json_provider.dart';
export 'src/model/content_type.dart';
export 'src/model/player_content.dart';
export 'src/model/player_statistic_record.dart';
@@ -0,0 +1,78 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:nut_player/nut_player.dart';
import 'package:nut_player/src/provider/json_provider/model/json_content.dart';
import 'package:nut_player/src/provider/json_provider/model/json_error.dart';
import 'package:nut_player/src/provider/json_provider/model/json_statistics.dart';
import 'package:nut_player/src/provider/json_provider/model/json_subtitles.dart';
import 'package:nut_player/src/provider/json_provider/parsing/response.dart';
class JsonProvider extends Provider {
final String urlPath;
JsonProvider(this.urlPath);
@override
Future<PlayerContent> retrieveContent() async {
final response = await http.get(Uri.parse(urlPath));
if (response.statusCode == 200) {
return _handleSucceedResponse(response);
} else {
throw JsonBadStatusCodeError();
}
}
JsonContent _handleSucceedResponse(http.Response httpResponse) {
var response = Response.fromJson(jsonDecode(httpResponse.body) as Map<String, dynamic>);
if (response.playbacks.isEmpty) { throw JsonNoPlaybacksError(); }
var uri = Uri.tryParse(response.playbacks.first.streamUrl);
if (uri != null) {
final statistics = response.statistics.map((stat) =>
JsonStatistics(
stat.name,
stat.urlTemplate,
stat.start,
stat.delay,
stat.count,
stat.method == "get" ? HTTPMethod.get : HTTPMethod.post,
stat.body
)
);
final subtitles = response.subtitles.map((sub) =>
JsonSubtitles(
sub.title,
sub.type == "srt" ? SubtitleType.srt : SubtitleType.unknown,
sub.url,
sub.language
)
);
final ContentType? contentType;
final urlPath = uri.toString();
if (urlPath.endsWith('.mp4')) {
contentType = Mp4ContentType(urlPath: urlPath);
} else if (urlPath.endsWith('.m3u8')) {
final isLive = response.playbacks.first.isLive;
contentType = HlsContentType(urlPath: urlPath, isLive: isLive);
} else {
contentType = null;
}
if (contentType != null) {
return JsonContent(
contentType,
statistics.toList(),
subtitles.toList()
);
} else {
throw JsonUnknownFormatError();
}
} else {
throw JsonIncorrectUrlError();
}
}
}
@@ -0,0 +1,14 @@
import 'package:nut_player/nut_player.dart';
class JsonContent extends PlayerContent {
@override
ContentType content;
@override
List<PlayerStatisticRecord> statistics;
@override
List<PlayerSubtitleRecord> subtitles;
JsonContent(this.content, this.statistics, this.subtitles);
}
@@ -0,0 +1,5 @@
class JsonBadStatusCodeError extends Error {}
class JsonNoPlaybacksError extends Error {}
class JsonIncorrectUrlError extends Error {}
class JsonUnknownFormatError extends Error {}
@@ -0,0 +1,26 @@
import 'package:nut_player/nut_player.dart';
class JsonStatistics extends PlayerStatisticRecord {
@override
String name;
@override
String urlTemplate;
@override
double start;
@override
double delay;
@override
int count;
@override
HTTPMethod method;
@override
String? body;
JsonStatistics(this.name, this.urlTemplate, this.start, this.delay, this.count, this.method, this.body);
}
@@ -0,0 +1,17 @@
import 'package:nut_player/nut_player.dart';
class JsonSubtitles extends PlayerSubtitleRecord {
@override
String title;
@override
SubtitleType type;
@override
String url;
@override
String language;
JsonSubtitles(this.title, this.type, this.url, this.language);
}
@@ -0,0 +1,19 @@
class PlaybackRecord {
final String streamType;
final String streamUrl;
final bool isLive;
final bool isVideo;
final bool isAudio;
PlaybackRecord({required this.streamType, required this.streamUrl, required this.isLive, required this.isVideo, required this.isAudio});
factory PlaybackRecord.fromJson(dynamic data) {
return PlaybackRecord(
streamType: data['stream_type'],
streamUrl: data['stream_url'],
isLive: data['is_live'],
isVideo: data['is_video'],
isAudio: data['is_audio']
);
}
}
@@ -0,0 +1,23 @@
import 'statistic_record.dart';
import 'subtitle_record.dart';
import 'playback_record.dart';
class Response {
final List<PlaybackRecord> playbacks;
final List<StatisticRecord> statistics;
final List<PlayerSubtitleRecord> subtitles;
Response({required this.playbacks, required this.statistics, required this.subtitles});
factory Response.fromJson(dynamic data) {
var playbacks = data['playback'].map((item) => PlaybackRecord.fromJson(item)).toList();
var statistics = data['stat'].map((item) => StatisticRecord.fromJson(item)).toList();
var subtitles = data['subtitle'].map((item) => PlayerSubtitleRecord.fromJson(item)).toList();
return Response(
playbacks: List<PlaybackRecord>.from(playbacks),
statistics: List<StatisticRecord>.from(statistics),
subtitles: List<PlayerSubtitleRecord>.from(subtitles)
);
}
}
@@ -0,0 +1,23 @@
class StatisticRecord {
final String name;
final String urlTemplate;
final double start;
final double delay;
final int count;
final String method;
final String? body;
StatisticRecord({required this.name, required this.urlTemplate, required this.start, required this.delay, required this.count, required this.method, this.body});
factory StatisticRecord.fromJson(dynamic data) {
return StatisticRecord(
name: data['name'],
urlTemplate: data['url_template'],
start: data['start'] == null ? 0.0 : data['start'].toDouble(),
delay: data['delay'] == null ? 0.0 : data['delay'].toDouble(),
count: data['count'],
method: data['method'],
body: data['body'],
);
}
}
@@ -0,0 +1,17 @@
class PlayerSubtitleRecord {
final String title;
final String type;
final String url;
final String language;
PlayerSubtitleRecord({required this.title, required this.type, required this.url, required this.language});
factory PlayerSubtitleRecord.fromJson(dynamic data) {
return PlayerSubtitleRecord(
title: data['title'],
type: data['type'],
url: data['url'],
language: data['language']
);
}
}
+1
View File
@@ -21,6 +21,7 @@ dependencies:
sdk: flutter
plugin_platform_interface: ^2.0.2
screen_brightness: ^0.2.2
http: ^1.1.0
nut_player_platform_interface:
path: ../nut_player_platform_interface
nut_player_ios: