mirror of
https://github.com/dstark5/Openlib.git
synced 2025-07-03 21:18:35 +08:00
fixed no result found error
This commit is contained in:
@ -19,13 +19,15 @@
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:label="Openlib"
|
||||
android:name="${applicationName}"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
@ -38,6 +40,7 @@
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
|
@ -2,7 +2,6 @@ import 'package:dio/dio.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:html/parser.dart' show parse;
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class BookData {
|
||||
final String title;
|
||||
@ -52,10 +51,12 @@ class BookInfoData extends BookData {
|
||||
class AnnasArchieve {
|
||||
String baseUrl = "https://annas-archive.org";
|
||||
|
||||
final Dio dio = Dio(BaseOptions(headers: {
|
||||
final Dio dio = Dio();
|
||||
|
||||
Map<String, dynamic> defaultDioHeaders = {
|
||||
"user-agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
|
||||
}));
|
||||
};
|
||||
|
||||
String getMd5(String url) {
|
||||
String md5 = url.toString().split('/').last;
|
||||
@ -66,7 +67,7 @@ class AnnasArchieve {
|
||||
var document =
|
||||
parse(resData.toString().replaceAll(RegExp(r"<!--|-->"), ''));
|
||||
var books = document.querySelectorAll(
|
||||
'a[class="js-vim-focus custom-a flex items-center relative left-[-10px] w-[calc(100%+20px)] px-[10px] py-2 outline-offset-[-2px] outline-2 rounded-[3px] hover:bg-[#00000011] focus:outline "]');
|
||||
'a[class="js-vim-focus custom-a flex items-center relative left-[-10px] w-[calc(100%+20px)] px-[10px] outline-offset-[-2px] outline-2 rounded-[3px] hover:bg-[#00000011] focus:outline "]');
|
||||
|
||||
List<BookData> bookList = [];
|
||||
|
||||
@ -75,16 +76,23 @@ class AnnasArchieve {
|
||||
'title': element.querySelector('h3')?.text,
|
||||
'thumbnail': element.querySelector('img')?.attributes['src'],
|
||||
'link': element.attributes['href'],
|
||||
'author': element.querySelector('div[class="truncate italic"]')?.text ??
|
||||
'author': element
|
||||
.querySelector(
|
||||
'div[class="max-lg:line-clamp-[2] lg:truncate leading-[1.2] lg:leading-[1.35] max-lg:text-sm italic"]')
|
||||
?.text ??
|
||||
'unknown',
|
||||
'publisher':
|
||||
element.querySelector('div[class="truncate text-sm"]')?.text ??
|
||||
"unknown",
|
||||
'publisher': element
|
||||
.querySelector(
|
||||
'div[class="truncate leading-[1.2] lg:leading-[1.35] max-lg:text-xs"]')
|
||||
?.text ??
|
||||
"unknown",
|
||||
'info': element
|
||||
.querySelector('div[class="truncate text-xs text-gray-500"]')
|
||||
.querySelector(
|
||||
'div[class="line-clamp-[2] leading-[1.2] text-[10px] lg:text-xs text-gray-500"]')
|
||||
?.text ??
|
||||
''
|
||||
};
|
||||
|
||||
if ((data['title'] != null && data['title'] != '') &&
|
||||
(data['link'] != null && data['link'] != '') &&
|
||||
(data['info'] != null &&
|
||||
@ -130,19 +138,29 @@ class AnnasArchieve {
|
||||
Future<String?> _getMirrorLink(
|
||||
String url, String userAgent, String cookie) async {
|
||||
try {
|
||||
final response = await http.get(Uri.parse(url),
|
||||
headers: {"Cookie": cookie, "User-Agent": userAgent});
|
||||
if (response.statusCode == 403) {
|
||||
throw jsonEncode({"code": "403", "url": url});
|
||||
}
|
||||
var document = parse(response.body.toString());
|
||||
final response = await dio.get(url,
|
||||
options: Options(extra: {
|
||||
'withCredentials': true
|
||||
}, headers: {
|
||||
"Host": "annas-archive.org",
|
||||
"Origin": "https://annas-archive.org",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"Sec-Fetch-Dest": "secure",
|
||||
"Sec-Fetch-Mode": "navigate",
|
||||
"Sec-Fetch-Site": "same-site",
|
||||
"Cookie": cookie,
|
||||
"User-Agent": userAgent
|
||||
}));
|
||||
|
||||
var document = parse(response.data.toString());
|
||||
|
||||
var pTag = document.querySelector('p[class="mb-4"]');
|
||||
String? link = pTag?.querySelector('a')?.attributes['href'];
|
||||
return link;
|
||||
} catch (e) {
|
||||
// print(e);
|
||||
// print('${url} ${e}');
|
||||
if (e.toString().contains("403")) {
|
||||
rethrow;
|
||||
throw jsonEncode({"code": "403", "url": url});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -238,7 +256,8 @@ class AnnasArchieve {
|
||||
? '$baseUrl/search?index=&q=$searchQuery&ext=$fileType&sort=$sort'
|
||||
: '$baseUrl/search?index=&q=$searchQuery&content=$content&ext=$fileType&sort=$sort';
|
||||
|
||||
final response = await dio.get(encodedURL);
|
||||
final response = await dio.get(encodedURL,
|
||||
options: Options(headers: defaultDioHeaders));
|
||||
return _parser(response.data, fileType);
|
||||
} on DioException catch (e) {
|
||||
if (e.type == DioExceptionType.unknown) {
|
||||
@ -253,7 +272,8 @@ class AnnasArchieve {
|
||||
required String userAgent,
|
||||
required String cookie}) async {
|
||||
try {
|
||||
final response = await dio.get(url);
|
||||
final response =
|
||||
await dio.get(url, options: Options(headers: defaultDioHeaders));
|
||||
BookInfoData? data =
|
||||
await _bookInfoParser(response.data, url, userAgent, cookie);
|
||||
if (data != null) {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'files.dart';
|
||||
|
||||
Future<String> _getFilePath(String fileName) async {
|
||||
@ -24,11 +23,11 @@ List<String> _reorderMirrors(List<String> mirrors) {
|
||||
return [...ipfsMirrors, ...httpsMirrors];
|
||||
}
|
||||
|
||||
Future<String?> _getAliveMirror(List<String> mirrors) async {
|
||||
Future<String?> _getAliveMirror(List<String> mirrors, Dio dio) async {
|
||||
for (var url in mirrors) {
|
||||
try {
|
||||
final response =
|
||||
await http.head(Uri.parse(url)).timeout(const Duration(seconds: 2));
|
||||
final response = await dio.head(url,
|
||||
options: Options(receiveTimeout: const Duration(seconds: 5)));
|
||||
if (response.statusCode == 200) {
|
||||
return url;
|
||||
}
|
||||
@ -48,10 +47,11 @@ Future<void> downloadFile(
|
||||
required Function mirrorStatus,
|
||||
required Function onDownlaodFailed}) async {
|
||||
Dio dio = Dio();
|
||||
|
||||
String path = await _getFilePath('$md5.$format');
|
||||
List<String> orderedMirrors = _reorderMirrors(mirrors);
|
||||
|
||||
String? workingMirror = await _getAliveMirror(orderedMirrors);
|
||||
String? workingMirror = await _getAliveMirror(orderedMirrors, dio);
|
||||
|
||||
// print(workingMirror);
|
||||
// print(path);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:openlib/services/database.dart';
|
||||
import 'package:openlib/ui/components/error_widget.dart';
|
||||
@ -48,23 +48,21 @@ class BookInfoPage extends ConsumerWidget {
|
||||
data: data, child: ActionButtonWidget(data: data));
|
||||
},
|
||||
error: (err, _) {
|
||||
// print(err);
|
||||
if (err.toString().contains("403")) {
|
||||
var errJson = jsonDecode(err.toString());
|
||||
|
||||
if (SchedulerBinding.instance.schedulerPhase ==
|
||||
SchedulerPhase.persistentCallbacks) {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return Webview(url: errJson["url"]);
|
||||
})).then((value) {
|
||||
// print("got Cf Clearance");
|
||||
// ignore: unused_result
|
||||
ref.refresh(bookInfoProvider(url));
|
||||
// });
|
||||
});
|
||||
Future.delayed(
|
||||
const Duration(seconds: 3),
|
||||
() => Navigator.pushReplacement(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return Webview(url: errJson["url"]);
|
||||
})));
|
||||
});
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@ -112,7 +110,7 @@ class BookInfoPage extends ConsumerWidget {
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Text(
|
||||
"If you have solved the captcha then you will be automatically redirected to the book page",
|
||||
"If you have solved the captcha then you will be automatically redirected to the results page . In case you seeing this page even after completing try using a VPN .",
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
|
@ -17,127 +17,133 @@ class TrendingPage extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final trendingBooks = ref.watch(getTrendingBooks);
|
||||
return trendingBooks.when(data: (data) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
const SliverToBoxAdapter(
|
||||
child: TitleText("Trending"),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 13.0,
|
||||
mainAxisExtent: 205,
|
||||
return trendingBooks.when(
|
||||
skipLoadingOnRefresh: false,
|
||||
data: (data) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, top: 10),
|
||||
child: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
const SliverToBoxAdapter(
|
||||
child: TitleText("Trending"),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (BuildContext context) {
|
||||
return ResultPage(
|
||||
searchQuery: data[index].title!,
|
||||
);
|
||||
}));
|
||||
},
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
imageUrl: data[index].thumbnail!,
|
||||
imageBuilder: (context, imageProvider) =>
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.grey,
|
||||
spreadRadius: 0.1,
|
||||
blurRadius: 1)
|
||||
],
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(5)),
|
||||
image: DecorationImage(
|
||||
image: imageProvider,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
),
|
||||
placeholder: (context, url) => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: "#E3E8E9".toColor(),
|
||||
),
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
),
|
||||
errorWidget: (context, url, error) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.grey,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 13.0,
|
||||
mainAxisExtent: 205,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return ResultPage(
|
||||
searchQuery: data[index].title!,
|
||||
);
|
||||
}));
|
||||
},
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
child: const Center(
|
||||
child: Icon(Icons.image_rounded),
|
||||
imageUrl: data[index].thumbnail!,
|
||||
imageBuilder: (context, imageProvider) =>
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.grey,
|
||||
spreadRadius: 0.1,
|
||||
blurRadius: 1)
|
||||
],
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(5)),
|
||||
image: DecorationImage(
|
||||
image: imageProvider,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: SizedBox(
|
||||
width: imageWidth,
|
||||
child: Text(
|
||||
data[index].title!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.displayMedium,
|
||||
maxLines: 2,
|
||||
placeholder: (context, url) => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: "#E3E8E9".toColor(),
|
||||
),
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
),
|
||||
errorWidget: (context, url, error) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(5),
|
||||
color: Colors.grey,
|
||||
),
|
||||
height: imageHeight,
|
||||
width: imageWidth,
|
||||
child: const Center(
|
||||
child: Icon(Icons.image_rounded),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: data.length,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: SizedBox(
|
||||
width: imageWidth,
|
||||
child: Text(
|
||||
data[index].title!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.displayMedium,
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: data.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}, error: (error, _) {
|
||||
return CustomErrorWidget(
|
||||
error: error,
|
||||
stackTrace: _,
|
||||
onRefresh: () {
|
||||
// ignore: unused_result
|
||||
ref.refresh(getTrendingBooks);
|
||||
);
|
||||
},
|
||||
);
|
||||
}, loading: () {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
));
|
||||
});
|
||||
error: (error, _) {
|
||||
return CustomErrorWidget(
|
||||
error: error,
|
||||
stackTrace: _,
|
||||
onRefresh: () {
|
||||
// ignore: unused_result
|
||||
ref.refresh(getTrendingBooks);
|
||||
},
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 25,
|
||||
height: 25,
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import 'package:webview_cookie_manager/webview_cookie_manager.dart'
|
||||
as cookiejar;
|
||||
|
||||
import 'package:openlib/state/state.dart'
|
||||
show cookieProvider, userAgentProvider, dbProvider;
|
||||
show cookieProvider, userAgentProvider, dbProvider, bookInfoProvider;
|
||||
|
||||
class Webview extends ConsumerStatefulWidget {
|
||||
const Webview({Key? key, required this.url}) : super(key: key);
|
||||
@ -38,12 +38,13 @@ class _WebviewState extends ConsumerState<Webview> {
|
||||
})
|
||||
..setNavigationDelegate(NavigationDelegate(
|
||||
onPageStarted: (url) async {
|
||||
var x = await controller.runJavaScriptReturningResult(
|
||||
var urlStatusCode = await controller.runJavaScriptReturningResult(
|
||||
"var xhr = new XMLHttpRequest();xhr.open('GET', window.location.href, false);xhr.send(null);xhr.status;");
|
||||
|
||||
if (x.toString().contains('200')) {
|
||||
if (urlStatusCode.toString().contains('200')) {
|
||||
final cookies = await cookieManager
|
||||
.getCookies("https://annas-archive.org");
|
||||
|
||||
List<String> cookie = [];
|
||||
for (var element in cookies) {
|
||||
if (element.name == 'cf_clearance' ||
|
||||
@ -51,10 +52,17 @@ class _WebviewState extends ConsumerState<Webview> {
|
||||
cookie.add(element.toString().split(';')[0]);
|
||||
}
|
||||
}
|
||||
ref.read(cookieProvider.notifier).state = cookie.join('; ');
|
||||
ref
|
||||
|
||||
String cfClearance = cookie.join('; ');
|
||||
|
||||
ref.read(cookieProvider.notifier).state = cfClearance;
|
||||
|
||||
await ref
|
||||
.read(dbProvider)
|
||||
.setBrowserOptions('cookie', cookie.join('; '));
|
||||
.setBrowserOptions('cookie', cfClearance);
|
||||
|
||||
ref.invalidate(bookInfoProvider);
|
||||
|
||||
// ignore: use_build_context_synchronously
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
@ -305,7 +305,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.15.4"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||
|
@ -47,7 +47,6 @@ dependencies:
|
||||
path_provider: ^2.0.15
|
||||
permission_handler: ^10.4.3
|
||||
open_file: ^3.3.2
|
||||
http: ^1.1.0
|
||||
webview_flutter: ^4.4.1
|
||||
webview_cookie_manager: ^2.0.6
|
||||
|
||||
|
Reference in New Issue
Block a user