Merge pull request #272 from yxxyun/quark

add UC extractor
This commit is contained in:
Moustapha Kodjo Amadou 2024-10-05 20:47:37 +01:00 committed by GitHub
commit 980f1e4c28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 156 additions and 32 deletions

View file

@ -346,6 +346,22 @@ class $MProvider extends MProvider with $Bridge<MProvider> {
false),
]),
),
'ucVideosExtractor': BridgeMethodDef(
BridgeFunctionDef(
returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.future, [
BridgeTypeRef(CoreTypes.list, [$MVideo.$type])
])),
params: [
BridgeParameter(
'url',
BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.string)),
false),
BridgeParameter(
'cookie',
BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.string)),
false),
]),
),
'quarkFilesExtractor': BridgeMethodDef(
BridgeFunctionDef(
returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.future, [
@ -368,6 +384,28 @@ class $MProvider extends MProvider with $Bridge<MProvider> {
false),
]),
),
'ucFilesExtractor': BridgeMethodDef(
BridgeFunctionDef(
returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.future, [
BridgeTypeRef(CoreTypes.list, [
BridgeTypeRef(CoreTypes.map, [
BridgeTypeRef(CoreTypes.string),
BridgeTypeRef(CoreTypes.string)
])
])
])),
params: [
BridgeParameter(
'url',
BridgeTypeAnnotation(BridgeTypeRef(
CoreTypes.list, [BridgeTypeRef(CoreTypes.string)])),
false),
BridgeParameter(
'cookie',
BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.string)),
false),
]),
),
'substringAfter': BridgeMethodDef(
BridgeFunctionDef(
returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.string)),
@ -946,6 +984,10 @@ class $MProvider extends MProvider with $Bridge<MProvider> {
.wrap(MBridge.quarkVideosExtractor(args[0]!.$value, args[1]!.$value)
.then((value) =>
$List.wrap(value.map((e) => _toMVideo(e)).toList())))),
"ucVideosExtractor" => $Function((_, __, List<$Value?> args) => $Future
.wrap(MBridge.ucVideosExtractor(args[0]!.$value, args[1]!.$value)
.then((value) =>
$List.wrap(value.map((e) => _toMVideo(e)).toList())))),
"quarkFilesExtractor" => $Function((_, __, List<$Value?> args) =>
$Future.wrap(
MBridge.quarkFilesExtractor(args[0]!.$value, args[1]!.$value)
@ -957,6 +999,16 @@ class $MProvider extends MProvider with $Bridge<MProvider> {
}))
.toList());
}))),
"ucFilesExtractor" => $Function((_, __, List<$Value?> args) => $Future
.wrap(MBridge.ucFilesExtractor(args[0]!.$value, args[1]!.$value)
.then((value) {
return $List.wrap(value
.map((e) => $Map.wrap({
$String('name'): $String(e['name'] ?? ''),
$String('url'): $String(e['url'] ?? ''),
}))
.toList());
}))),
"toVideo" => $Function((_, __, List<$Value?> args) {
final value = MBridge.toVideo(
args[0]!.$value,

View file

@ -33,7 +33,7 @@ import 'package:mangayomi/utils/extensions/string_extensions.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:xpath_selector_html_parser/xpath_selector_html_parser.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:mangayomi/services/anime_extractors/quark_extractor.dart';
import 'package:mangayomi/services/anime_extractors/quarkuc_extractor.dart';
class WordSet {
final List<String> words;
@ -347,18 +347,32 @@ class MBridge {
static Future<List<Map<String, String>>> quarkFilesExtractor(
List<String> url, String cookie) async {
QuarkExtractor quark = QuarkExtractor();
await quark.initQuark(cookie);
QuarkUcExtractor quark = QuarkUcExtractor();
await quark.initCloudDrive(cookie, CloudDriveType.quark);
return await quark.videoFilesFromUrl(url);
}
static Future<List<Map<String, String>>> ucFilesExtractor(
List<String> url, String cookie) async {
QuarkUcExtractor uc = QuarkUcExtractor();
await uc.initCloudDrive(cookie, CloudDriveType.uc);
return await uc.videoFilesFromUrl(url);
}
static Future<List<Video>> quarkVideosExtractor(
String url, String cookie) async {
QuarkExtractor quark = QuarkExtractor();
await quark.initQuark(cookie);
QuarkUcExtractor quark = QuarkUcExtractor();
await quark.initCloudDrive(cookie, CloudDriveType.quark);
return await quark.videosFromUrl(url);
}
static Future<List<Video>> ucVideosExtractor(
String url, String cookie) async {
QuarkUcExtractor uc = QuarkUcExtractor();
await uc.initCloudDrive(cookie, CloudDriveType.uc);
return await uc.videosFromUrl(url);
}
static Future<List<Video>> streamTapeExtractor(
String url, String? quality) async {
return await StreamTapeExtractor()

View file

@ -29,10 +29,17 @@ class JsVideosExtractors {
return (await MBridge.quarkVideosExtractor(args[0], args[1]))
.encodeToJson();
});
runtime.onMessage('ucVideosExtractor', (dynamic args) async {
return (await MBridge.ucVideosExtractor(args[0], args[1])).encodeToJson();
});
runtime.onMessage('quarkFilesExtractor', (dynamic args) async {
List<String> urls = (args[0] as List).cast<String>();
return (await MBridge.quarkFilesExtractor(urls, args[1]));
});
runtime.onMessage('ucFilesExtractor', (dynamic args) async {
List<String> urls = (args[0] as List).cast<String>();
return (await MBridge.ucFilesExtractor(urls, args[1]));
});
runtime.onMessage('streamlareExtractor', (dynamic args) async {
return (await MBridge.streamlareExtractor(
args[0], args[1] ?? "", args[2] ?? ""))
@ -193,6 +200,13 @@ async function quarkVideosExtractor(url, cookie) {
);
return JSON.parse(result);
}
async function ucVideosExtractor(url, cookie) {
const result = await sendMessage(
"ucVideosExtractor",
JSON.stringify([url, cookie])
);
return JSON.parse(result);
}
async function quarkFilesExtractor(urls, cookie) {
const result = await sendMessage(
"quarkFilesExtractor",
@ -200,6 +214,13 @@ async function quarkFilesExtractor(urls, cookie) {
);
return result;
}
async function ucFilesExtractor(urls, cookie) {
const result = await sendMessage(
"ucFilesExtractor",
JSON.stringify([urls, cookie])
);
return result;
}
''');
}
}

View file

@ -5,29 +5,55 @@ import 'package:http_interceptor/http_interceptor.dart';
import 'package:mangayomi/models/video.dart';
import 'package:mangayomi/services/http/m_client.dart';
class QuarkExtractor {
final String apiUrl = "https://drive-pc.quark.cn/1/clouddrive/";
enum CloudDriveType {
quark,
uc,
}
class QuarkUcExtractor {
late CloudDriveType cloudDriveType;
String apiUrl = ""; //"https://drive-pc.quark.cn/1/clouddrive/";
String cookie = "";
Map<String, dynamic> shareTokenCache = {};
final String pr = "pr=ucpro&fr=pc";
String pr = ""; //"pr=ucpro&fr=pc";
final List<String> subtitleExts = ['.srt', '.ass', '.scc', '.stl', '.ttml'];
Map<String, String> saveFileIdCaches = {};
String? saveDirId;
final String saveDirName = 'TV';
Future<void> initQuark(String cookie) async {
Future<void> initCloudDrive(
String cookie, CloudDriveType cloudDriveType) async {
this.cookie = cookie;
this.cloudDriveType = cloudDriveType;
if (cloudDriveType == CloudDriveType.quark) {
apiUrl = "https://drive-pc.quark.cn/1/clouddrive/";
pr = "pr=ucpro&fr=pc";
} else {
apiUrl = "https://pc-api.uc.cn/1/clouddrive/";
pr = "pr=UCBrowser&fr=pc";
}
}
Map<String, String> getHeaders() {
return {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch',
'Referer': 'https://pan.quark.cn/',
"Content-Type": "application/json",
"Cookie": cookie,
"Host": "drive-pc.quark.cn"
};
if (cloudDriveType == CloudDriveType.quark) {
return {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch',
'Referer': 'https://pan.quark.cn/',
"Content-Type": "application/json",
"Cookie": cookie,
"Host": "drive-pc.quark.cn"
};
} else {
return {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) uc-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch',
'Referer': 'https://drive.uc.cn/',
"Content-Type": "application/json",
"Cookie": cookie,
"Host": "pc-api.uc.cn"
};
}
}
Future<Map<String, dynamic>> api(
@ -60,7 +86,12 @@ class QuarkExtractor {
}
Map<String, String>? getShareData(String url) {
final regex = RegExp(r'https://pan\.quark\.cn/s/([^\\|#/]+)');
RegExp regex;
if (cloudDriveType == CloudDriveType.quark) {
regex = RegExp(r'https://pan\.quark\.cn/s/([^\\|#/]+)');
} else {
regex = RegExp(r'https://drive\.uc\.cn/s/([^?]+)');
}
final matches = regex.firstMatch(url);
if (matches != null) {
return {
@ -71,7 +102,7 @@ class QuarkExtractor {
return null;
}
List<String> getPlayFormtQuarkList() {
List<String> getPlayFormtList() {
return ["normal", "low", "high", "super", "2k", "4k"];
}
@ -114,10 +145,12 @@ class QuarkExtractor {
} else if (item['file'] == true && item['obj_category'] == 'video') {
if (item['size'] < 1024 * 1024 * 5) continue;
item['stoken'] = shareTokenCache[shareData['shareId']]['stoken'];
videos.add(Item.objectFrom(item, shareData['shareId']!, shareIndex));
videos.add(Item.objectFrom(
item, shareData['shareId']!, shareIndex, cloudDriveType));
} else if (item['type'] == 'file' &&
subtitleExts.any((x) => item['file_name'].endsWith(x))) {
subtitles.add(Item.objectFrom(item, shareData['shareId']!, shareIndex));
subtitles.add(Item.objectFrom(
item, shareData['shareId']!, shareIndex, cloudDriveType));
}
}
if (page < (listData['metadata']['_total'] / prePage).ceil()) {
@ -359,21 +392,21 @@ class QuarkExtractor {
Future<List<Video>> videosFromUrl(String url) async {
List<String> parts = url.split('++');
String fileId = parts[0];
String fileToken = parts[1];
String shareId = parts[2];
String stoken = parts[3];
String fileId = parts[1];
String fileToken = parts[2];
String shareId = parts[3];
String stoken = parts[4];
List<String> subtitleParts = parts.length > 4 ? parts[4].split('+') : [];
List<String> subtitleParts = parts.length > 5 ? parts[5].split('+') : [];
// normal
// var originalQuality =
// await getDownload(shareId, stoken, fileId, fileToken, true);
String? originalUrl =
String? originalUrl = //originalQuality?['download_url'];
await getLiveTranscoding(shareId, stoken, fileId, fileToken, 'normal');
//
List<String> qualities = getPlayFormtQuarkList();
List<String> qualities = getPlayFormtList();
List<Video> videos = [];
for (String quality in qualities) {
@ -504,9 +537,10 @@ class Item {
int shareIndex = 0;
int lastUpdateAt = 0;
dynamic subtitle;
late CloudDriveType cloudDriveType;
static Item objectFrom(
Map<String, dynamic> itemJson, String shareId, int shareIndex) {
static Item objectFrom(Map<String, dynamic> itemJson, String shareId,
int shareIndex, CloudDriveType cloudDriveType) {
Item item = Item();
item.fileId = itemJson['fid'] ?? "";
item.shareId = shareId;
@ -520,6 +554,7 @@ class Item {
item.parent = itemJson['pdir_fid'] ?? "";
item.lastUpdateAt = itemJson['last_update_at'] ?? 0;
item.shareIndex = shareIndex;
item.cloudDriveType = cloudDriveType;
return item;
}
@ -548,6 +583,8 @@ class Item {
}
String getDisplayName(String typeName) {
String drivePrefix =
cloudDriveType == CloudDriveType.quark ? '[quark]' : '[uc]';
String displayName = getName();
if (typeName == "电视剧") {
List<String> replaceNameList = ["4k", "4K"];
@ -567,11 +604,11 @@ class Item {
displayName = numbers[0]!;
}
}
return "$displayName ${getSize()}";
return "$drivePrefix $displayName ${getSize()}";
}
String getEpisodeUrl(String typeName) {
return "${getDisplayName(typeName)}\$${getFileId()}++$shareFileToken++$shareId++$shareToken";
return "${getDisplayName(typeName)}\$${cloudDriveType == CloudDriveType.quark ? "quark" : "uc"}++${getFileId()}++$shareFileToken++$shareId++$shareToken";
}
String getHumanReadableSize(int bytes) {