new anime server extractor

This commit is contained in:
kodjomoustapha 2023-08-03 22:50:48 +01:00
parent 0c4b2f7360
commit df8be849ac
13 changed files with 390 additions and 251 deletions

View file

@ -14,6 +14,7 @@ import 'package:json_path/json_path.dart';
import 'package:mangayomi/eval/bridge_class/model.dart';
import 'package:mangayomi/eval/bridge_class/video_model.dart';
import 'package:mangayomi/services/anime_extractors/dood_extractor.dart';
import 'package:mangayomi/services/anime_extractors/filemoon.dart';
import 'package:mangayomi/services/anime_extractors/gogocdn_extractor.dart';
import 'package:mangayomi/services/anime_extractors/mp4upload_extractor.dart';
import 'package:mangayomi/services/anime_extractors/mytv_extractor.dart';
@ -606,6 +607,11 @@ class MBridge {
return await StreamWishExtractor().videosFromUrl(url, prefix);
}
static Future<List<Video>> filemoonExtractor(
String url, String prefix) async {
return await FilemoonExtractor().videosFromUrl(url, prefix);
}
static Future<List<Video>> mp4UploadExtractor(
String url, String? headers, String prefix, String suffix) async {
Map<String, String> newHeaders = {};
@ -1556,6 +1562,25 @@ class $MBridge extends MBridge with $Bridge {
],
namedParams: []),
isStatic: true),
'filemoonExtractor': BridgeMethodDef(
BridgeFunctionDef(
returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.future,
[BridgeTypeRef.type(RuntimeTypes.dynamicType)])),
params: [
BridgeParameter(
'url',
BridgeTypeAnnotation(
BridgeTypeRef.type(RuntimeTypes.stringType)),
false),
BridgeParameter(
'prefix',
BridgeTypeAnnotation(
BridgeTypeRef.type(RuntimeTypes.stringType),
nullable: true),
false),
],
namedParams: []),
isStatic: true),
'getHtmlViaWebview': BridgeMethodDef(
BridgeFunctionDef(
returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.future,
@ -1870,6 +1895,12 @@ class $MBridge extends MBridge with $Bridge {
.then((value) =>
$List.wrap(value.map((e) => _toVideoModel(e)).toList())));
static $Future $filemoonExtractor(
Runtime runtime, $Value? target, List<$Value?> args) =>
$Future.wrap(MBridge.filemoonExtractor(args[0]!.$value, args[1]!.$value)
.then((value) =>
$List.wrap(value.map((e) => _toVideoModel(e)).toList())));
static $Future $sendVidExtractor(
Runtime runtime, $Value? target, List<$Value?> args) =>
$Future.wrap(MBridge.sendVidExtractor(

View file

@ -30,6 +30,8 @@ Runtime runtimeEval(Uint8List bytecode) {
'MBridge.streamTapeExtractor', $MBridge.$streamTapeExtractor);
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
'MBridge.streamWishExtractor', $MBridge.$streamWishExtractor);
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
'MBridge.filemoonExtractor', $MBridge.$filemoonExtractor);
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
'MBridge.mp4UploadExtractor', $MBridge.$mp4UploadExtractor);
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',

View file

@ -0,0 +1,61 @@
import 'package:http/http.dart' as http;
import 'package:js_packer/js_packer.dart';
import 'package:mangayomi/models/video.dart';
import 'package:mangayomi/utils/extensions.dart';
import 'package:mangayomi/utils/xpath_selector.dart';
class FilemoonExtractor {
final http.Client client = http.Client();
Future<List<Video>> videosFromUrl(String url, String prefix) async {
prefix = prefix.isEmpty ? "Filemoon - " : prefix;
try {
final response = await client.get(Uri.parse(url));
final jsEval = xpathSelector(response.body)
.queryXPath('//script[contains(text(), "eval")]/text()')
.attr;
final unpacked = _evalJs(jsEval!) ?? '';
final masterUrl = unpacked.isNotEmpty
? unpacked.substringAfter('{file:"').substringBefore('"}')
: '';
if (masterUrl.isEmpty) {
return [];
}
final masterPlaylistResponse = await client.get(Uri.parse(masterUrl));
final masterPlaylist = masterPlaylistResponse.body;
final videoHeaders = {
'Referer': url,
'Origin': 'https://${Uri.parse(url).host}',
};
const separator = '#EXT-X-STREAM-INF:';
final playlists = masterPlaylist.split(separator).sublist(1);
return playlists.map((playlist) {
final resolution =
'${playlist.substringAfter('RESOLUTION=').substringAfter('x').substringBefore(',').trim()}p';
final videoUrl = playlist.split('\n')[1].trim();
return Video(
videoUrl,
prefix + resolution,
videoUrl,
headers: videoHeaders,
);
}).toList();
} catch (_) {
return [];
}
}
}
String? _evalJs(String script) {
final jsPacker = JSPacker(script);
return jsPacker.unpack();
}

View file

@ -12,41 +12,44 @@ class Mp4uploadExtractor {
{String prefix = '', String suffix = ''}) async {
final newHeaders = Map<String, String>.from(headers)
..addAll({'referer': referer});
try {
final response = await http.get(Uri.parse(url), headers: newHeaders);
String script = "";
final response = await http.get(Uri.parse(url), headers: newHeaders);
String script = "";
final scriptElementWithEval = xpathSelector(response.body)
.queryXPath(
'//script[contains(text(), "eval") and contains(text(), "p,a,c,k,e,d")]/text()')
.attrs;
if (scriptElementWithEval.isNotEmpty) {
script = _evalJs(script)!;
} else {
final scriptElementWithSrc = xpathSelector(response.body)
.queryXPath('//script[contains(text(), "player.src")]/text()')
final scriptElementWithEval = xpathSelector(response.body)
.queryXPath(
'//script[contains(text(), "eval") and contains(text(), "p,a,c,k,e,d")]/text()')
.attrs;
if (scriptElementWithSrc.isNotEmpty) {
script = scriptElementWithSrc.first!;
if (scriptElementWithEval.isNotEmpty) {
script = _evalJs(script)!;
} else {
return [];
final scriptElementWithSrc = xpathSelector(response.body)
.queryXPath('//script[contains(text(), "player.src")]/text()')
.attrs;
if (scriptElementWithSrc.isNotEmpty) {
script = scriptElementWithSrc.first!;
} else {
return [];
}
}
final videoUrl = script
.substringAfter('.src(')
.substringBefore(')')
.substringAfter('src:')
.substringAfter('"')
.substringBefore('"');
final resolutionMatch = qualityRegex.firstMatch(script);
final resolution = resolutionMatch?.group(1) ?? 'Unknown resolution';
final quality = '$prefix Mp4Upload - $resolution $suffix';
return [
Video(videoUrl, quality, videoUrl, headers: newHeaders),
];
} catch (_) {
return [];
}
final videoUrl = script
.substringAfter('.src(')
.substringBefore(')')
.substringAfter('src:')
.substringAfter('"')
.substringBefore('"');
final resolutionMatch = qualityRegex.firstMatch(script);
final resolution = resolutionMatch?.group(1) ?? 'Unknown resolution';
final quality = '$prefix Mp4Upload - $resolution $suffix';
return [
Video(videoUrl, quality, videoUrl, headers: newHeaders),
];
}
}

View file

@ -7,30 +7,34 @@ class MytvExtractor {
final http.Client client = http.Client();
Future<List<Video>> videosFromUrl(String url) async {
final response = await client.get(Uri.parse(url));
final document = parse(response.body);
final videoList = <Video>[];
try {
final response = await client.get(Uri.parse(url));
final document = parse(response.body);
final videoList = <Video>[];
document.querySelectorAll("script").forEach((script) {
if (script.text.contains("CreatePlayer(\"v")) {
final videosString = script.text;
final videoUrl = videosString
.substringAfter("\"v=")
.substringBefore("\\u0026tp=video")
.replaceAll("%26", "&")
.replaceAll("%3a", ":")
.replaceAll("%2f", "/")
.replaceAll("%3f", "?")
.replaceAll("%3d", "=");
document.querySelectorAll("script").forEach((script) {
if (script.text.contains("CreatePlayer(\"v")) {
final videosString = script.text;
final videoUrl = videosString
.substringAfter("\"v=")
.substringBefore("\\u0026tp=video")
.replaceAll("%26", "&")
.replaceAll("%3a", ":")
.replaceAll("%2f", "/")
.replaceAll("%3f", "?")
.replaceAll("%3d", "=");
if (!videoUrl.contains("https:")) {
videoList.add(Video(videoUrl, "Stream", videoUrl));
} else {
videoList.add(Video(videoUrl, "Mytv", videoUrl));
if (!videoUrl.contains("https:")) {
videoList.add(Video(videoUrl, "Stream", videoUrl));
} else {
videoList.add(Video(videoUrl, "Mytv", videoUrl));
}
}
}
});
});
return videoList;
return videoList;
} catch (_) {
return [];
}
}
}

View file

@ -22,31 +22,35 @@ class OkruExtractor {
Future<List<Video>> videosFromUrl(String url,
{String prefix = '', bool fixQualities = true}) async {
final response = await client.get(Uri.parse(url));
final document = parse(response.body);
final videosString = document
.querySelector('div[data-options]')
?.attributes['data-options']!
.substringAfter("\\\"videos\\\":[{\\\"name\\\":\\\"")
.substringBefore(']') ??
'';
try {
final response = await client.get(Uri.parse(url));
final document = parse(response.body);
final videosString = document
.querySelector('div[data-options]')
?.attributes['data-options']!
.substringAfter("\\\"videos\\\":[{\\\"name\\\":\\\"")
.substringBefore(']') ??
'';
List<Video> videoList = [];
List<String> values =
videosString.split("{\\\"name\\\":\\\"").reversed.toList();
for (var value in values) {
final videoUrl = value
.substringAfter("url\\\":\\\"")
.substringBefore("\\\"")
.replaceAll(r'\\\u0026', '&');
final quality = value.substringBefore("\\\"");
final fixedQuality = fixQualities ? fixQuality(quality) : quality;
final videoQuality =
'${prefix.isNotEmpty ? '$prefix ' : ''}Okru:$fixedQuality';
if (videoUrl.startsWith('https://')) {
videoList.add(Video(videoUrl, videoQuality, videoUrl));
List<Video> videoList = [];
List<String> values =
videosString.split("{\\\"name\\\":\\\"").reversed.toList();
for (var value in values) {
final videoUrl = value
.substringAfter("url\\\":\\\"")
.substringBefore("\\\"")
.replaceAll(r'\\\u0026', '&');
final quality = value.substringBefore("\\\"");
final fixedQuality = fixQualities ? fixQuality(quality) : quality;
final videoQuality =
'${prefix.isNotEmpty ? '$prefix ' : ''}Okru:$fixedQuality';
if (videoUrl.startsWith('https://')) {
videoList.add(Video(videoUrl, videoQuality, videoUrl));
}
}
return videoList;
} catch (_) {
return [];
}
return videoList;
}
}

View file

@ -10,52 +10,56 @@ class SendvidExtractor {
SendvidExtractor(this.headers);
Future<List<Video>> videosFromUrl(String url, {String prefix = ""}) async {
final videoList = <Video>[];
final response = await client.get(Uri.parse(url));
final document = parser.parse(response.body);
final masterUrl =
document.querySelector("source#video_source")?.attributes["src"];
try {
final videoList = <Video>[];
final response = await client.get(Uri.parse(url));
final document = parser.parse(response.body);
final masterUrl =
document.querySelector("source#video_source")?.attributes["src"];
if (masterUrl == null) {
return videoList;
}
if (masterUrl == null) {
return videoList;
}
final masterHeaders = Map<String, String>.from(headers)
..addAll({
"Accept": "*/*",
"Host": Uri.parse(masterUrl).host,
"Origin": "https://${Uri.parse(url).host}",
"Referer": "https://${Uri.parse(url).host}/",
});
final masterPlaylistResponse =
await client.get(Uri.parse(masterUrl), headers: masterHeaders);
final masterPlaylist = masterPlaylistResponse.body;
final masterBase =
"https://${Uri.parse(masterUrl).host}${Uri.parse(masterUrl).pathSegments.join("/")}/";
masterPlaylist
.substringAfter("#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:")
.forEach((it) {
final quality =
"Sendvid:${it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",")}p ";
final videoUrl =
masterBase + it.substringAfter("\n").substringBefore("\n");
final videoHeaders = Map<String, String>.from(headers)
final masterHeaders = Map<String, String>.from(headers)
..addAll({
"Accept": "*/*",
"Host": Uri.parse(videoUrl).host,
"Host": Uri.parse(masterUrl).host,
"Origin": "https://${Uri.parse(url).host}",
"Referer": "https://${Uri.parse(url).host}/",
});
videoList.add(
Video(videoUrl, prefix + quality, videoUrl, headers: videoHeaders));
});
final masterPlaylistResponse =
await client.get(Uri.parse(masterUrl), headers: masterHeaders);
final masterPlaylist = masterPlaylistResponse.body;
return videoList;
final masterBase =
"https://${Uri.parse(masterUrl).host}${Uri.parse(masterUrl).pathSegments.join("/")}/";
masterPlaylist
.substringAfter("#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:")
.forEach((it) {
final quality =
"Sendvid:${it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",")}p ";
final videoUrl =
masterBase + it.substringAfter("\n").substringBefore("\n");
final videoHeaders = Map<String, String>.from(headers)
..addAll({
"Accept": "*/*",
"Host": Uri.parse(videoUrl).host,
"Origin": "https://${Uri.parse(url).host}",
"Referer": "https://${Uri.parse(url).host}/",
});
videoList.add(
Video(videoUrl, prefix + quality, videoUrl, headers: videoHeaders));
});
return videoList;
} catch (_) {
return [];
}
}
}

View file

@ -7,30 +7,33 @@ class SibnetExtractor {
Future<List<Video>> videosFromUrl(String url) async {
List<Video> videoList = [];
try {
final response = await client.get(Uri.parse(url));
if (response.statusCode != 200) {
return [];
}
final response = await client.get(Uri.parse(url));
if (response.statusCode != 200) {
String script = response.body;
String slug = script
.substringAfter("player.src")
.substringAfter("src:")
.substringAfter("\"")
.substringBefore("\"");
String videoUrl =
slug.contains("http") ? slug : "https://${Uri.parse(url).host}$slug";
Map<String, String> videoHeaders = {
"Referer": url,
};
videoList.add(
Video(videoUrl, "Sibnet", videoUrl, headers: videoHeaders),
);
return videoList;
} catch (_) {
return [];
}
String script = response.body;
String slug = script
.substringAfter("player.src")
.substringAfter("src:")
.substringAfter("\"")
.substringBefore("\"");
String videoUrl =
slug.contains("http") ? slug : "https://${Uri.parse(url).host}$slug";
Map<String, String> videoHeaders = {
"Referer": url,
};
videoList.add(
Video(videoUrl, "Sibnet", videoUrl, headers: videoHeaders),
);
return videoList;
}
}

View file

@ -7,59 +7,64 @@ class StreamlareExtractor {
Future<List<Video>> videosFromUrl(String url,
{String prefix = "", String suffix = ""}) async {
final id = url.split('/').last;
final playlistResponse = await client.post(
Uri.parse('https://slwatch.co/api/video/stream/get'),
headers: {'Content-Type': 'application/json'},
body: '{"id":"$id"}',
);
try {
final id = url.split('/').last;
final playlistResponse = await client.post(
Uri.parse('https://slwatch.co/api/video/stream/get'),
headers: {'Content-Type': 'application/json'},
body: '{"id":"$id"}',
);
final playlist = playlistResponse.body;
final type = playlist.substringAfter('"type":"').substringBefore('"');
final playlist = playlistResponse.body;
final type = playlist.substringAfter('"type":"').substringBefore('"');
if (type == 'hls') {
final masterPlaylistUrl = playlist
.substringAfter('"file":"')
.substringBefore('"')
.replaceAll(r'\/', '/');
final masterPlaylistResponse =
await client.get(Uri.parse(masterPlaylistUrl));
final masterPlaylist = masterPlaylistResponse.body;
const separator = '#EXT-X-STREAM-INF';
return masterPlaylist
.substringAfter(separator)
.split(separator)
.map((value) {
final quality =
'${value.substringAfter('RESOLUTION=').substringAfter('x').substringBefore(',')}p';
final videoUrl =
value.substringAfter('\n').substringBefore('\n').let((urlPart) {
return !urlPart.startsWith('http')
? masterPlaylistUrl.substringBefore('master.m3u8') + urlPart
: urlPart;
});
return Video(
videoUrl, _buildQuality(quality, prefix, suffix), videoUrl);
}).toList();
} else {
const separator = '"label":"';
List<Video> videoList = [];
List<String> values = playlist.substringAfter(separator).split(separator);
for (var value in values) {
final quality = value.substringAfter(separator).substringBefore('",');
final apiUrl = value
if (type == 'hls') {
final masterPlaylistUrl = playlist
.substringAfter('"file":"')
.substringBefore('",')
.replaceAll('\\', '');
final response = await client.post(Uri.parse(apiUrl));
final videoUrl = response.request!.url.toString();
.substringBefore('"')
.replaceAll(r'\/', '/');
final masterPlaylistResponse =
await client.get(Uri.parse(masterPlaylistUrl));
final masterPlaylist = masterPlaylistResponse.body;
videoList.add(
Video(videoUrl, _buildQuality(quality, prefix, suffix), videoUrl));
const separator = '#EXT-X-STREAM-INF';
return masterPlaylist
.substringAfter(separator)
.split(separator)
.map((value) {
final quality =
'${value.substringAfter('RESOLUTION=').substringAfter('x').substringBefore(',')}p';
final videoUrl =
value.substringAfter('\n').substringBefore('\n').let((urlPart) {
return !urlPart.startsWith('http')
? masterPlaylistUrl.substringBefore('master.m3u8') + urlPart
: urlPart;
});
return Video(
videoUrl, _buildQuality(quality, prefix, suffix), videoUrl);
}).toList();
} else {
const separator = '"label":"';
List<Video> videoList = [];
List<String> values =
playlist.substringAfter(separator).split(separator);
for (var value in values) {
final quality = value.substringAfter(separator).substringBefore('",');
final apiUrl = value
.substringAfter('"file":"')
.substringBefore('",')
.replaceAll('\\', '');
final response = await client.post(Uri.parse(apiUrl));
final videoUrl = response.request!.url.toString();
videoList.add(Video(
videoUrl, _buildQuality(quality, prefix, suffix), videoUrl));
}
return videoList;
}
return videoList;
} catch (_) {
return [];
}
}

View file

@ -6,27 +6,31 @@ import 'package:mangayomi/utils/extensions.dart';
class StreamTapeExtractor {
Future<List<Video>> videosFromUrl(String url,
{String quality = "StreamTape"}) async {
const baseUrl = "https://streamtape.com/e/";
final newUrl =
!url.startsWith(baseUrl) ? "$baseUrl${url.split("/")[4]}" : url;
try {
const baseUrl = "https://streamtape.com/e/";
final newUrl =
!url.startsWith(baseUrl) ? "$baseUrl${url.split("/")[4]}" : url;
final response = await http.Client().get(Uri.parse(newUrl));
final document = parse(response.body);
final response = await http.Client().get(Uri.parse(newUrl));
final document = parse(response.body);
const targetLine = "document.getElementById('robotlink')";
String script = "";
final scri = document
.querySelectorAll("script")
.where((element) => element.innerHtml.contains(targetLine))
.map((e) => e.innerHtml)
.toList();
if (scri.isEmpty) {
const targetLine = "document.getElementById('robotlink')";
String script = "";
final scri = document
.querySelectorAll("script")
.where((element) => element.innerHtml.contains(targetLine))
.map((e) => e.innerHtml)
.toList();
if (scri.isEmpty) {
return [];
}
script = scri.first.split("$targetLine.innerHTML = '").last;
final videoUrl =
"https:${script.substringBefore("'")}${script.substringAfter("+ ('xcd").substringBefore("'")}";
return [Video(videoUrl, quality, videoUrl)];
} catch (_) {
return [];
}
script = scri.first.split("$targetLine.innerHTML = '").last;
final videoUrl =
"https:${script.substringBefore("'")}${script.substringAfter("+ ('xcd").substringBefore("'")}";
return [Video(videoUrl, quality, videoUrl)];
}
}

View file

@ -10,49 +10,52 @@ class StreamWishExtractor {
Future<List<Video>> videosFromUrl(String url, String prefix) async {
final videoList = <Video>[];
try {
final response = await client.get(Uri.parse(url), headers: headers);
final response = await client.get(Uri.parse(url), headers: headers);
final jsEval = xpathSelector(response.body)
.queryXPath('//script[contains(text(), "m3u8")]/text()')
.attrs;
if (jsEval.isEmpty) {
return [];
}
final jsEval = xpathSelector(response.body)
.queryXPath('//script[contains(text(), "m3u8")]/text()')
.attrs;
if (jsEval.isEmpty) {
return [];
}
String? masterUrl = _evalJs(jsEval.first!)
?.substringAfter('source')
.substringAfter('file:"')
.substringBefore('"');
String? masterUrl = _evalJs(jsEval.first!)
?.substringAfter('source')
.substringAfter('file:"')
.substringBefore('"');
if (masterUrl == null) return [];
if (masterUrl == null) return [];
final playlistHeaders = Map<String, String>.from(headers)
..addAll({
'Accept': '*/*',
'Host': Uri.parse(masterUrl).host,
'Origin': 'https://${Uri.parse(url).host}',
'Referer': 'https://${Uri.parse(url).host}/',
});
final playlistHeaders = Map<String, String>.from(headers)
..addAll({
'Accept': '*/*',
'Host': Uri.parse(masterUrl).host,
'Origin': 'https://${Uri.parse(url).host}',
'Referer': 'https://${Uri.parse(url).host}/',
final masterBase =
'${'https://${Uri.parse(masterUrl).host}${Uri.parse(masterUrl).path}'.substringBeforeLast('/')}/';
final masterPlaylistResponse =
await client.get(Uri.parse(masterUrl), headers: playlistHeaders);
final masterPlaylist = masterPlaylistResponse.body;
const separator = '#EXT-X-STREAM-INF:';
masterPlaylist.substringAfter(separator).split(separator).forEach((it) {
final quality =
'$prefix${it.substringAfter('RESOLUTION=').substringAfter('x').substringBefore(',')}p ';
final videoUrl =
masterBase + it.substringAfter('\n').substringBefore('\n');
videoList
.add(Video(videoUrl, quality, videoUrl, headers: playlistHeaders));
});
final masterBase =
'${'https://${Uri.parse(masterUrl).host}${Uri.parse(masterUrl).path}'.substringBeforeLast('/')}/';
final masterPlaylistResponse =
await client.get(Uri.parse(masterUrl), headers: playlistHeaders);
final masterPlaylist = masterPlaylistResponse.body;
const separator = '#EXT-X-STREAM-INF:';
masterPlaylist.substringAfter(separator).split(separator).forEach((it) {
final quality =
'$prefix${it.substringAfter('RESOLUTION=').substringAfter('x').substringBefore(',')}p ';
final videoUrl =
masterBase + it.substringAfter('\n').substringBefore('\n');
videoList
.add(Video(videoUrl, quality, videoUrl, headers: playlistHeaders));
});
return videoList;
return videoList;
} catch (_) {
return [];
}
}
}

View file

@ -7,22 +7,26 @@ class VidBomExtractor {
final http.Client client = http.Client();
Future<List<Video>> videosFromUrl(String url) async {
final response = await client.get(Uri.parse(url));
final script = xpathSelector(response.body)
.queryXPath('//script[contains(text(), "sources")]/text()')
.attrs;
try {
final response = await client.get(Uri.parse(url));
final script = xpathSelector(response.body)
.queryXPath('//script[contains(text(), "sources")]/text()')
.attrs;
final data =
script.first!.substringAfter('sources: [').substringBefore('],');
final data =
script.first!.substringAfter('sources: [').substringBefore('],');
return data.split('file:"').skip(1).map((source) {
final src = source.substringBefore('"');
var quality =
'Vidbom: ${source.substringAfter('label:"').substringBefore('"')}';
if (quality.length > 15) {
quality = 'Vidshare: 480p';
}
return Video(src, quality, src);
}).toList();
return data.split('file:"').skip(1).map((source) {
final src = source.substringBefore('"');
var quality =
'Vidbom: ${source.substringAfter('label:"').substringBefore('"')}';
if (quality.length > 15) {
quality = 'Vidshare: 480p';
}
return Video(src, quality, src);
}).toList();
} catch (_) {
return [];
}
}
}

View file

@ -20,6 +20,17 @@ extension StringExtensions on String {
return substring(0, endIndex);
}
String substringBetween(String left, String right) {
int startIndex = 0;
int index = indexOf(left, startIndex);
if (index == -1) return "";
int leftIndex = index + left.length;
int rightIndex = indexOf(right, leftIndex);
if (rightIndex == -1) return "";
startIndex = rightIndex + right.length;
return substring(leftIndex, rightIndex);
}
}
extension LetExtension<T> on T {