+
This commit is contained in:
parent
69c745238d
commit
cb103afbb0
10 changed files with 213 additions and 40 deletions
|
|
@ -641,6 +641,7 @@ class ChapterFilterBookmarked {
|
||||||
@embedded
|
@embedded
|
||||||
class ChapterPageurls {
|
class ChapterPageurls {
|
||||||
int? chapterId;
|
int? chapterId;
|
||||||
|
String? chapterUrl;
|
||||||
List<String>? urls;
|
List<String>? urls;
|
||||||
List<String>? headers;
|
List<String>? headers;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11674,13 +11674,18 @@ const ChapterPageurlsSchema = Schema(
|
||||||
name: r'chapterId',
|
name: r'chapterId',
|
||||||
type: IsarType.long,
|
type: IsarType.long,
|
||||||
),
|
),
|
||||||
r'headers': PropertySchema(
|
r'chapterUrl': PropertySchema(
|
||||||
id: 1,
|
id: 1,
|
||||||
|
name: r'chapterUrl',
|
||||||
|
type: IsarType.string,
|
||||||
|
),
|
||||||
|
r'headers': PropertySchema(
|
||||||
|
id: 2,
|
||||||
name: r'headers',
|
name: r'headers',
|
||||||
type: IsarType.stringList,
|
type: IsarType.stringList,
|
||||||
),
|
),
|
||||||
r'urls': PropertySchema(
|
r'urls': PropertySchema(
|
||||||
id: 2,
|
id: 3,
|
||||||
name: r'urls',
|
name: r'urls',
|
||||||
type: IsarType.stringList,
|
type: IsarType.stringList,
|
||||||
)
|
)
|
||||||
|
|
@ -11697,6 +11702,12 @@ int _chapterPageurlsEstimateSize(
|
||||||
Map<Type, List<int>> allOffsets,
|
Map<Type, List<int>> allOffsets,
|
||||||
) {
|
) {
|
||||||
var bytesCount = offsets.last;
|
var bytesCount = offsets.last;
|
||||||
|
{
|
||||||
|
final value = object.chapterUrl;
|
||||||
|
if (value != null) {
|
||||||
|
bytesCount += 3 + value.length * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
final list = object.headers;
|
final list = object.headers;
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
|
|
@ -11731,8 +11742,9 @@ void _chapterPageurlsSerialize(
|
||||||
Map<Type, List<int>> allOffsets,
|
Map<Type, List<int>> allOffsets,
|
||||||
) {
|
) {
|
||||||
writer.writeLong(offsets[0], object.chapterId);
|
writer.writeLong(offsets[0], object.chapterId);
|
||||||
writer.writeStringList(offsets[1], object.headers);
|
writer.writeString(offsets[1], object.chapterUrl);
|
||||||
writer.writeStringList(offsets[2], object.urls);
|
writer.writeStringList(offsets[2], object.headers);
|
||||||
|
writer.writeStringList(offsets[3], object.urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChapterPageurls _chapterPageurlsDeserialize(
|
ChapterPageurls _chapterPageurlsDeserialize(
|
||||||
|
|
@ -11743,9 +11755,10 @@ ChapterPageurls _chapterPageurlsDeserialize(
|
||||||
) {
|
) {
|
||||||
final object = ChapterPageurls(
|
final object = ChapterPageurls(
|
||||||
chapterId: reader.readLongOrNull(offsets[0]),
|
chapterId: reader.readLongOrNull(offsets[0]),
|
||||||
urls: reader.readStringList(offsets[2]),
|
urls: reader.readStringList(offsets[3]),
|
||||||
);
|
);
|
||||||
object.headers = reader.readStringList(offsets[1]);
|
object.chapterUrl = reader.readStringOrNull(offsets[1]);
|
||||||
|
object.headers = reader.readStringList(offsets[2]);
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -11759,9 +11772,11 @@ P _chapterPageurlsDeserializeProp<P>(
|
||||||
case 0:
|
case 0:
|
||||||
return (reader.readLongOrNull(offset)) as P;
|
return (reader.readLongOrNull(offset)) as P;
|
||||||
case 1:
|
case 1:
|
||||||
return (reader.readStringList(offset)) as P;
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
case 2:
|
case 2:
|
||||||
return (reader.readStringList(offset)) as P;
|
return (reader.readStringList(offset)) as P;
|
||||||
|
case 3:
|
||||||
|
return (reader.readStringList(offset)) as P;
|
||||||
default:
|
default:
|
||||||
throw IsarError('Unknown property with id $propertyId');
|
throw IsarError('Unknown property with id $propertyId');
|
||||||
}
|
}
|
||||||
|
|
@ -11843,6 +11858,160 @@ extension ChapterPageurlsQueryFilter
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlIsNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNull(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlIsNotNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlEqualTo(
|
||||||
|
String? value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlGreaterThan(
|
||||||
|
String? value, {
|
||||||
|
bool include = false,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||||
|
include: include,
|
||||||
|
property: r'chapterUrl',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlLessThan(
|
||||||
|
String? value, {
|
||||||
|
bool include = false,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.lessThan(
|
||||||
|
include: include,
|
||||||
|
property: r'chapterUrl',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlBetween(
|
||||||
|
String? lower,
|
||||||
|
String? upper, {
|
||||||
|
bool includeLower = true,
|
||||||
|
bool includeUpper = true,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.between(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
lower: lower,
|
||||||
|
includeLower: includeLower,
|
||||||
|
upper: upper,
|
||||||
|
includeUpper: includeUpper,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlStartsWith(
|
||||||
|
String value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.startsWith(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlEndsWith(
|
||||||
|
String value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.endsWith(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlContains(String value, {bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.contains(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlMatches(String pattern, {bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.matches(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
wildcard: pattern,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlIsEmpty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
value: '',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
|
chapterUrlIsNotEmpty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||||
|
property: r'chapterUrl',
|
||||||
|
value: '',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
QueryBuilder<ChapterPageurls, ChapterPageurls, QAfterFilterCondition>
|
||||||
headersIsNull() {
|
headersIsNull() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
|
|
||||||
|
|
@ -102,18 +102,14 @@ Future<dynamic> updateMangaDetail(Ref ref,
|
||||||
for (var i = 0; i < oldChapers.length; i++) {
|
for (var i = 0; i < oldChapers.length; i++) {
|
||||||
final oldChap = oldChapers[i];
|
final oldChap = oldChapers[i];
|
||||||
final newChap = chaps[i];
|
final newChap = chaps[i];
|
||||||
if (newChap.url != null &&
|
oldChap.name = newChap.name;
|
||||||
newChap.url!.isNotEmpty &&
|
oldChap.url = newChap.url;
|
||||||
newChap.url != oldChap.url &&
|
oldChap.scanlator = newChap.scanlator;
|
||||||
newChap.name == oldChap.name) {
|
ref
|
||||||
oldChap.url = newChap.url;
|
.read(changedItemsManagerProvider(managerId: 1).notifier)
|
||||||
oldChap.scanlator = newChap.scanlator;
|
.addUpdatedChapter(oldChap, false, false);
|
||||||
ref
|
isar.chapters.putSync(oldChap);
|
||||||
.read(changedItemsManagerProvider(managerId: 1).notifier)
|
oldChap.manga.saveSync();
|
||||||
.addUpdatedChapter(oldChap, false, false);
|
|
||||||
isar.chapters.putSync(oldChap);
|
|
||||||
oldChap.manga.saveSync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ part of 'update_manga_detail_providers.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$updateMangaDetailHash() => r'29a10d49454febb4fe88ca9c007d3512d812bf84';
|
String _$updateMangaDetailHash() => r'82c52aa8760fb455e6558be925d05f5f0703af98';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ Future<List<PageUrl>> downloadChapter(
|
||||||
chapterPageUrls.add(ChapterPageurls()
|
chapterPageUrls.add(ChapterPageurls()
|
||||||
..chapterId = chapter.id
|
..chapterId = chapter.id
|
||||||
..urls = pageUrls.map((e) => e.url).toList()
|
..urls = pageUrls.map((e) => e.url).toList()
|
||||||
|
..chapterUrl = chapter.url
|
||||||
..headers = chapterPageHeaders.first != null
|
..headers = chapterPageHeaders.first != null
|
||||||
? chapterPageHeaders.map((e) => e.toString()).toList()
|
? chapterPageHeaders.map((e) => e.toString()).toList()
|
||||||
: null);
|
: null);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ part of 'aniskip.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$aniSkipHash() => r'887869b54e2e151633efd46da83bde845e14f421';
|
String _$aniSkipHash() => r'2e5d19b025a2207ff64da7bf7908450ea9e5ff8c';
|
||||||
|
|
||||||
/// See also [AniSkip].
|
/// See also [AniSkip].
|
||||||
@ProviderFor(AniSkip)
|
@ProviderFor(AniSkip)
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,9 @@ Future<GetChapterPagesModel> getChapterPages(
|
||||||
final settings = isar.settings.getSync(227);
|
final settings = isar.settings.getSync(227);
|
||||||
List<ChapterPageurls>? chapterPageUrlsList =
|
List<ChapterPageurls>? chapterPageUrlsList =
|
||||||
settings!.chapterPageUrlsList ?? [];
|
settings!.chapterPageUrlsList ?? [];
|
||||||
final isarPageUrls =
|
final isarPageUrls = chapterPageUrlsList
|
||||||
chapterPageUrlsList.where((element) => element.chapterId == chapter.id);
|
.where((element) => element.chapterId == chapter.id)
|
||||||
|
.firstOrNull;
|
||||||
final incognitoMode = ref.watch(incognitoModeStateProvider);
|
final incognitoMode = ref.watch(incognitoModeStateProvider);
|
||||||
final storageProvider = StorageProvider();
|
final storageProvider = StorageProvider();
|
||||||
path = await storageProvider.getMangaChapterDirectory(chapter);
|
path = await storageProvider.getMangaChapterDirectory(chapter);
|
||||||
|
|
@ -55,16 +56,15 @@ Future<GetChapterPagesModel> getChapterPages(
|
||||||
if (!chapter.manga.value!.isLocalArchive!) {
|
if (!chapter.manga.value!.isLocalArchive!) {
|
||||||
final source =
|
final source =
|
||||||
getSource(chapter.manga.value!.lang!, chapter.manga.value!.source!)!;
|
getSource(chapter.manga.value!.lang!, chapter.manga.value!.source!)!;
|
||||||
if (isarPageUrls.isNotEmpty &&
|
if ((isarPageUrls?.urls?.isNotEmpty ?? false) &&
|
||||||
isarPageUrls.first.urls != null &&
|
(isarPageUrls?.chapterUrl ?? chapter.url) == chapter.url) {
|
||||||
isarPageUrls.first.urls!.isNotEmpty) {
|
for (var i = 0; i < isarPageUrls!.urls!.length; i++) {
|
||||||
for (var i = 0; i < isarPageUrls.first.urls!.length; i++) {
|
|
||||||
Map<String, String>? headers;
|
Map<String, String>? headers;
|
||||||
if (isarPageUrls.first.headers?.isNotEmpty ?? false) {
|
if (isarPageUrls.headers?.isNotEmpty ?? false) {
|
||||||
headers = (jsonDecode(isarPageUrls.first.headers![i]) as Map?)
|
headers =
|
||||||
?.toMapStringString;
|
(jsonDecode(isarPageUrls.headers![i]) as Map?)?.toMapStringString;
|
||||||
}
|
}
|
||||||
pageUrls.add(PageUrl(isarPageUrls.first.urls![i], headers: headers));
|
pageUrls.add(PageUrl(isarPageUrls.urls![i], headers: headers));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pageUrls = await getExtensionService(source).getPageList(chapter.url!);
|
pageUrls = await getExtensionService(source).getPageList(chapter.url!);
|
||||||
|
|
@ -111,6 +111,7 @@ Future<GetChapterPagesModel> getChapterPages(
|
||||||
chapterPageUrls.add(ChapterPageurls()
|
chapterPageUrls.add(ChapterPageurls()
|
||||||
..chapterId = chapter.id
|
..chapterId = chapter.id
|
||||||
..urls = pageUrls.map((e) => e.url).toList()
|
..urls = pageUrls.map((e) => e.url).toList()
|
||||||
|
..chapterUrl = chapter.url
|
||||||
..headers = chapterPageHeaders.first != null
|
..headers = chapterPageHeaders.first != null
|
||||||
? chapterPageHeaders.map((e) => e.toString()).toList()
|
? chapterPageHeaders.map((e) => e.toString()).toList()
|
||||||
: null);
|
: null);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:http_interceptor/http_interceptor.dart';
|
import 'package:http_interceptor/http_interceptor.dart';
|
||||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
@ -173,9 +174,13 @@ class LoggerInterceptor extends InterceptorContract {
|
||||||
Future<BaseRequest> interceptRequest({
|
Future<BaseRequest> interceptRequest({
|
||||||
required BaseRequest request,
|
required BaseRequest request,
|
||||||
}) async {
|
}) async {
|
||||||
|
final content =
|
||||||
|
"----- Request -----\n${request.toString()}\nheaders: ${request.headers.toString()}";
|
||||||
|
if (kDebugMode) {
|
||||||
|
print(content);
|
||||||
|
}
|
||||||
if (useLogger) {
|
if (useLogger) {
|
||||||
Logger.add(LoggerLevel.info,
|
Logger.add(LoggerLevel.info, content);
|
||||||
'----- Request -----\n${request.toString()}\nheaders: ${request.headers.toString()}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
|
|
@ -189,9 +194,13 @@ class LoggerInterceptor extends InterceptorContract {
|
||||||
final cloudflare = [403, 503].contains(response.statusCode) &&
|
final cloudflare = [403, 503].contains(response.statusCode) &&
|
||||||
["cloudflare-nginx", "cloudflare"]
|
["cloudflare-nginx", "cloudflare"]
|
||||||
.contains(response.headers["server"]);
|
.contains(response.headers["server"]);
|
||||||
|
final content =
|
||||||
|
"----- Response -----\n${response.request?.method}: ${response.request?.url}, statusCode: ${response.statusCode} ${cloudflare ? "Failed to bypass Cloudflare" : ""}";
|
||||||
|
if (kDebugMode) {
|
||||||
|
print(content);
|
||||||
|
}
|
||||||
if (useLogger) {
|
if (useLogger) {
|
||||||
Logger.add(LoggerLevel.info,
|
Logger.add(LoggerLevel.info, content);
|
||||||
"----- Response -----\n${response.request?.method}: ${response.request?.url}, statusCode: ${response.statusCode} ${cloudflare ? "Failed to bypass Cloudflare" : ""}");
|
|
||||||
}
|
}
|
||||||
if (cloudflare) {
|
if (cloudflare) {
|
||||||
botToast("${response.statusCode} Failed to bypass Cloudflare",
|
botToast("${response.statusCode} Failed to bypass Cloudflare",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ part of 'anilist.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$anilistHash() => r'87784b25bccf5b1c61d6c7494f0569a28494dd8b';
|
String _$anilistHash() => r'd3a8852d689b13c3bde46ec05b464e7779149e58';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
bool useLogger = false;
|
bool useLogger = false;
|
||||||
|
|
@ -16,9 +15,6 @@ class Logger {
|
||||||
static List<(LoggerLevel, String, DateTime)> get logs => _logs;
|
static List<(LoggerLevel, String, DateTime)> get logs => _logs;
|
||||||
|
|
||||||
static void add(LoggerLevel level, String content) {
|
static void add(LoggerLevel level, String content) {
|
||||||
if (kDebugMode) {
|
|
||||||
print(content);
|
|
||||||
}
|
|
||||||
_logStreamController.add((level, content, DateTime.now()));
|
_logStreamController.add((level, content, DateTime.now()));
|
||||||
_logs.add((level, content, DateTime.now()));
|
_logs.add((level, content, DateTime.now()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue