feat: add custom streaming torrent
This commit is contained in:
parent
98612f3511
commit
7741beb8ac
10 changed files with 536 additions and 89 deletions
|
|
@ -326,5 +326,10 @@
|
|||
"edit_code": "Edit code",
|
||||
"use_libass": "Enable libass",
|
||||
"use_libass_info": "Use libass based subtitle rendering for native backend.",
|
||||
"libass_not_disable_message": "Disable `use libass` in player settings to be able to customize the subtitles."
|
||||
"libass_not_disable_message": "Disable `use libass` in player settings to be able to customize the subtitles.",
|
||||
"torrent_stream": "Torrent Stream",
|
||||
"add_torrent": "Add torrent",
|
||||
"enter_torrent_hint_text": "Enter magnet or torrent file url",
|
||||
"torrent_url": "Torrent url",
|
||||
"or": "OR"
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import 'package:mangayomi/models/download.dart';
|
|||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/library/providers/add_torrent.dart';
|
||||
import 'package:mangayomi/modules/library/providers/local_archive.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/update_manga_detail_providers.dart';
|
||||
import 'package:mangayomi/modules/more/categories/providers/isar_providers.dart';
|
||||
|
|
@ -1767,6 +1768,9 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
value: 1, child: Text(l10n.open_random_entry)),
|
||||
PopupMenuItem<int>(
|
||||
value: 2, child: Text(l10n.import)),
|
||||
if (!widget.isManga)
|
||||
PopupMenuItem<int>(
|
||||
value: 3, child: Text(l10n.torrent_stream)),
|
||||
];
|
||||
},
|
||||
onSelected: (value) {
|
||||
|
|
@ -1786,8 +1790,13 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
mangaM: randomManga,
|
||||
source: randomManga.source!);
|
||||
});
|
||||
} else {
|
||||
}
|
||||
if (value == 2) {
|
||||
_importLocal(context, widget.isManga);
|
||||
} else {
|
||||
if (!widget.isManga) {
|
||||
addTorrent(context);
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
|
|
@ -1795,7 +1804,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
}
|
||||
}
|
||||
|
||||
_importLocal(BuildContext context, bool isManga) {
|
||||
void _importLocal(BuildContext context, bool isManga) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
bool isLoading = false;
|
||||
showDialog(
|
||||
|
|
@ -1899,3 +1908,173 @@ _importLocal(BuildContext context, bool isManga) {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
void addTorrent(BuildContext context, {Manga? manga}) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
String torrentUrl = "";
|
||||
bool isLoading = false;
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: !isLoading,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.add_torrent,
|
||||
),
|
||||
content: StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return Consumer(builder: (context, ref, _) {
|
||||
return SizedBox(
|
||||
height: 150,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
torrentUrl = value;
|
||||
});
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.enter_torrent_hint_text,
|
||||
labelText: l10n.torrent_url,
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Colors.transparent,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: context.secondaryColor)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: context.secondaryColor)),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: context.secondaryColor))),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: isLoading
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
try {
|
||||
await ref.watch(
|
||||
addTorrentFromUrlOrFromFileProvider(
|
||||
manga,
|
||||
init: true,
|
||||
url: torrentUrl)
|
||||
.future);
|
||||
} catch (_) {}
|
||||
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(l10n.add))
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Text(l10n.or),
|
||||
),
|
||||
Stack(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(10))),
|
||||
onPressed: isLoading
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
try {
|
||||
await ref.watch(
|
||||
addTorrentFromUrlOrFromFileProvider(
|
||||
manga,
|
||||
init: true)
|
||||
.future);
|
||||
} catch (_) {}
|
||||
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
const Icon(Icons.archive_outlined),
|
||||
Text("import .torrent file",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.color,
|
||||
fontSize: 10))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isLoading)
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
width: 300,
|
||||
height: 150,
|
||||
color: Colors.transparent,
|
||||
child: UnconstrainedBox(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context)
|
||||
.scaffoldBackgroundColor,
|
||||
),
|
||||
height: 50,
|
||||
width: 50,
|
||||
child: const Center(
|
||||
child: ProgressCenter())),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(l10n.cancel)),
|
||||
const SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
82
lib/modules/library/providers/add_torrent.dart
Normal file
82
lib/modules/library/providers/add_torrent.dart
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/services/torrent_server.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'add_torrent.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future addTorrentFromUrlOrFromFile(
|
||||
AddTorrentFromUrlOrFromFileRef ref, Manga? mManga,
|
||||
{required bool init, String? url}) async {
|
||||
FilePickerResult? result;
|
||||
if (url == null) {
|
||||
result = await FilePicker.platform.pickFiles(
|
||||
allowMultiple: true,
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ['torrent']);
|
||||
}
|
||||
|
||||
if (result != null || url != null) {
|
||||
String torrentName = "";
|
||||
if (url != null) {
|
||||
torrentName = (await MTorrentServer().getTorrentPlaylist(url, null))
|
||||
.$1
|
||||
.first
|
||||
.quality;
|
||||
}
|
||||
final dateNow = DateTime.now().millisecondsSinceEpoch;
|
||||
final manga = mManga ??
|
||||
Manga(
|
||||
favorite: true,
|
||||
source: 'torrent',
|
||||
author: '',
|
||||
isManga: false,
|
||||
genre: [],
|
||||
imageUrl: '',
|
||||
lang: '',
|
||||
link: '',
|
||||
name: url != null ? torrentName : _getName(result!.files.first.path!),
|
||||
dateAdded: dateNow,
|
||||
lastUpdate: dateNow,
|
||||
status: Status.unknown,
|
||||
description: '',
|
||||
isLocalArchive: true,
|
||||
artist: '',
|
||||
);
|
||||
if (url != null) {
|
||||
manga.customCoverImage = null;
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.putSync(manga);
|
||||
final chapters = Chapter(name: torrentName, url: url, mangaId: manga.id)
|
||||
..manga.value = manga;
|
||||
isar.chapters.putSync(chapters);
|
||||
chapters.manga.saveSync();
|
||||
});
|
||||
} else {
|
||||
for (var file in result!.files.reversed.toList()) {
|
||||
String name = _getName(file.path!);
|
||||
|
||||
if (init) {
|
||||
manga.customCoverImage = null;
|
||||
}
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.putSync(manga);
|
||||
final chapters =
|
||||
Chapter(name: name, archivePath: file.path, mangaId: manga.id)
|
||||
..manga.value = manga;
|
||||
isar.chapters.putSync(chapters);
|
||||
chapters.manga.saveSync();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
String _getName(String path) {
|
||||
return path.split('/').last.split("\\").last.replaceAll(
|
||||
RegExp(r'\.(mp4|mov|avi|flv|wmv|mpeg|mkv|cbz|zip|cbt|tar|torrent)'), '');
|
||||
}
|
||||
194
lib/modules/library/providers/add_torrent.g.dart
Normal file
194
lib/modules/library/providers/add_torrent.g.dart
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'add_torrent.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$addTorrentFromUrlOrFromFileHash() =>
|
||||
r'473a3494fd8c5089afdd460637f37faf2a498400';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
_SystemHash._();
|
||||
|
||||
static int combine(int hash, int value) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + value);
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||
return hash ^ (hash >> 6);
|
||||
}
|
||||
|
||||
static int finish(int hash) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||
// ignore: parameter_assignments
|
||||
hash = hash ^ (hash >> 11);
|
||||
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [addTorrentFromUrlOrFromFile].
|
||||
@ProviderFor(addTorrentFromUrlOrFromFile)
|
||||
const addTorrentFromUrlOrFromFileProvider = AddTorrentFromUrlOrFromFileFamily();
|
||||
|
||||
/// See also [addTorrentFromUrlOrFromFile].
|
||||
class AddTorrentFromUrlOrFromFileFamily extends Family<AsyncValue> {
|
||||
/// See also [addTorrentFromUrlOrFromFile].
|
||||
const AddTorrentFromUrlOrFromFileFamily();
|
||||
|
||||
/// See also [addTorrentFromUrlOrFromFile].
|
||||
AddTorrentFromUrlOrFromFileProvider call(
|
||||
Manga? mManga, {
|
||||
required bool init,
|
||||
String? url,
|
||||
}) {
|
||||
return AddTorrentFromUrlOrFromFileProvider(
|
||||
mManga,
|
||||
init: init,
|
||||
url: url,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AddTorrentFromUrlOrFromFileProvider getProviderOverride(
|
||||
covariant AddTorrentFromUrlOrFromFileProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.mManga,
|
||||
init: provider.init,
|
||||
url: provider.url,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'addTorrentFromUrlOrFromFileProvider';
|
||||
}
|
||||
|
||||
/// See also [addTorrentFromUrlOrFromFile].
|
||||
class AddTorrentFromUrlOrFromFileProvider
|
||||
extends AutoDisposeFutureProvider<Object?> {
|
||||
/// See also [addTorrentFromUrlOrFromFile].
|
||||
AddTorrentFromUrlOrFromFileProvider(
|
||||
Manga? mManga, {
|
||||
required bool init,
|
||||
String? url,
|
||||
}) : this._internal(
|
||||
(ref) => addTorrentFromUrlOrFromFile(
|
||||
ref as AddTorrentFromUrlOrFromFileRef,
|
||||
mManga,
|
||||
init: init,
|
||||
url: url,
|
||||
),
|
||||
from: addTorrentFromUrlOrFromFileProvider,
|
||||
name: r'addTorrentFromUrlOrFromFileProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$addTorrentFromUrlOrFromFileHash,
|
||||
dependencies: AddTorrentFromUrlOrFromFileFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
AddTorrentFromUrlOrFromFileFamily._allTransitiveDependencies,
|
||||
mManga: mManga,
|
||||
init: init,
|
||||
url: url,
|
||||
);
|
||||
|
||||
AddTorrentFromUrlOrFromFileProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.mManga,
|
||||
required this.init,
|
||||
required this.url,
|
||||
}) : super.internal();
|
||||
|
||||
final Manga? mManga;
|
||||
final bool init;
|
||||
final String? url;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<Object?> Function(AddTorrentFromUrlOrFromFileRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: AddTorrentFromUrlOrFromFileProvider._internal(
|
||||
(ref) => create(ref as AddTorrentFromUrlOrFromFileRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
mManga: mManga,
|
||||
init: init,
|
||||
url: url,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<Object?> createElement() {
|
||||
return _AddTorrentFromUrlOrFromFileProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is AddTorrentFromUrlOrFromFileProvider &&
|
||||
other.mManga == mManga &&
|
||||
other.init == init &&
|
||||
other.url == url;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, mManga.hashCode);
|
||||
hash = _SystemHash.combine(hash, init.hashCode);
|
||||
hash = _SystemHash.combine(hash, url.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
mixin AddTorrentFromUrlOrFromFileRef on AutoDisposeFutureProviderRef<Object?> {
|
||||
/// The parameter `mManga` of this provider.
|
||||
Manga? get mManga;
|
||||
|
||||
/// The parameter `init` of this provider.
|
||||
bool get init;
|
||||
|
||||
/// The parameter `url` of this provider.
|
||||
String? get url;
|
||||
}
|
||||
|
||||
class _AddTorrentFromUrlOrFromFileProviderElement
|
||||
extends AutoDisposeFutureProviderElement<Object?>
|
||||
with AddTorrentFromUrlOrFromFileRef {
|
||||
_AddTorrentFromUrlOrFromFileProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
Manga? get mManga => (origin as AddTorrentFromUrlOrFromFileProvider).mManga;
|
||||
@override
|
||||
bool get init => (origin as AddTorrentFromUrlOrFromFileProvider).init;
|
||||
@override
|
||||
String? get url => (origin as AddTorrentFromUrlOrFromFileProvider).url;
|
||||
}
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
|
|
@ -15,6 +15,7 @@ import 'package:mangayomi/models/manga.dart';
|
|||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/models/track_search.dart';
|
||||
import 'package:mangayomi/modules/library/library_screen.dart';
|
||||
import 'package:mangayomi/modules/library/providers/local_archive.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/tracker_search_widget.dart';
|
||||
|
|
@ -624,14 +625,24 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
.secondaryColor),
|
||||
),
|
||||
onPressed: () async {
|
||||
await ref.watch(importArchivesFromFileProvider(
|
||||
isManga: widget
|
||||
.manga!
|
||||
.isManga!,
|
||||
widget
|
||||
.manga,
|
||||
init: false)
|
||||
.future);
|
||||
final manga =
|
||||
widget.manga;
|
||||
if ((manga!.isLocalArchive ??
|
||||
false) &&
|
||||
manga.source ==
|
||||
"torrent") {
|
||||
addTorrent(
|
||||
context,
|
||||
manga: manga);
|
||||
} else {
|
||||
await ref.watch(importArchivesFromFileProvider(
|
||||
isManga: manga
|
||||
.isManga!,
|
||||
manga,
|
||||
init:
|
||||
false)
|
||||
.future);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ final aniSkipTimeoutLengthStateProvider =
|
|||
);
|
||||
|
||||
typedef _$AniSkipTimeoutLengthState = AutoDisposeNotifier<int>;
|
||||
String _$useLibassStateHash() => r'09c661f72c8777f360f48f2203d767b9caf6e4e7';
|
||||
String _$useLibassStateHash() => r'91e5bbde72651f57f8775bf0fec14145ea42ced6';
|
||||
|
||||
/// See also [UseLibassState].
|
||||
@ProviderFor(UseLibassState)
|
||||
|
|
|
|||
|
|
@ -18,24 +18,24 @@ Future<(List<Video>, bool, String?)> getVideoList(
|
|||
}) async {
|
||||
final storageProvider = StorageProvider();
|
||||
final mangaDirectory = await storageProvider.getMangaMainDirectory(episode);
|
||||
final isLocalArchive = episode.manga.value!.isLocalArchive!;
|
||||
final isLocalArchive = episode.manga.value!.isLocalArchive! &&
|
||||
episode.manga.value!.source != "torrent";
|
||||
final mp4animePath = "${mangaDirectory!.path}${episode.name}.mp4";
|
||||
|
||||
if (await File(mp4animePath).exists() || isLocalArchive) {
|
||||
final path = isLocalArchive ? episode.archivePath : mp4animePath;
|
||||
return ([Video(path!, episode.name!, path, subtitles: [])], true, null);
|
||||
}
|
||||
|
||||
final source =
|
||||
getSource(episode.manga.value!.lang!, episode.manga.value!.source!)!;
|
||||
getSource(episode.manga.value!.lang!, episode.manga.value!.source!);
|
||||
|
||||
if (source.isTorrent) {
|
||||
final (videos, infohash) =
|
||||
await MTorrentServer().getTorrentPlaylist(episode.url!);
|
||||
if (source?.isTorrent ?? false || episode.manga.value!.source == "torrent") {
|
||||
final (videos, infohash) = await MTorrentServer()
|
||||
.getTorrentPlaylist(episode.url, episode.archivePath);
|
||||
return (videos, false, infohash);
|
||||
}
|
||||
List<Video> list = [];
|
||||
if (source.sourceCodeLanguage == SourceCodeLanguage.dart) {
|
||||
if (source?.sourceCodeLanguage == SourceCodeLanguage.dart) {
|
||||
list = await DartExtensionService(source).getVideoList(episode.url!);
|
||||
} else {
|
||||
list = await JsExtensionService(source).getVideoList(episode.url!);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'get_video_list.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$getVideoListHash() => r'c95e1f62e30989547ee977fdd9faad84f49673fd';
|
||||
String _$getVideoListHash() => r'46918505b5cf3401ea9f41a5c287f8746b93b1b8';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@ import 'package:mangayomi/providers/storage_provider.dart';
|
|||
import 'package:mangayomi/services/http/m_client.dart';
|
||||
import 'package:mangayomi/utils/extensions/string_extensions.dart';
|
||||
import 'package:mangayomi/ffi/torrent_server_ffi.dart' as libmtorrentserver_ffi;
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'torrent_server.g.dart';
|
||||
|
||||
class MTorrentServer {
|
||||
final http = MClient.init();
|
||||
Future<bool> removeTorrent(String? inforHash) async {
|
||||
|
|
@ -42,9 +39,11 @@ class MTorrentServer {
|
|||
}
|
||||
}
|
||||
|
||||
Future<String> getInfohash(String url) async {
|
||||
Future<String> getInfohash(String url, bool isFilePath) async {
|
||||
try {
|
||||
final torrentByte = (await http.get(Uri.parse(url))).bodyBytes;
|
||||
final torrentByte = isFilePath
|
||||
? File(url).readAsBytesSync()
|
||||
: (await http.get(Uri.parse(url))).bodyBytes;
|
||||
var request =
|
||||
MultipartRequest('POST', Uri.parse('$_baseUrl/torrent/add'));
|
||||
|
||||
|
|
@ -57,45 +56,52 @@ class MTorrentServer {
|
|||
}
|
||||
}
|
||||
|
||||
Future<(List<Video>, String?)> getTorrentPlaylist(String url) async {
|
||||
final isRunning = await check();
|
||||
if (!isRunning) {
|
||||
final path = (await StorageProvider().getBtDirectory())!.path;
|
||||
final config = jsonEncode({"path": path, "address": "127.0.0.1:0"});
|
||||
int port = 0;
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
const channel =
|
||||
MethodChannel('com.kodjodevf.mangayomi.libmtorrentserver');
|
||||
port = await channel.invokeMethod('start', {"config": config});
|
||||
Future<(List<Video>, String?)> getTorrentPlaylist(
|
||||
String? url, String? archivePath) async {
|
||||
try {
|
||||
final isFilePath = archivePath?.isNotEmpty ?? false;
|
||||
final isRunning = await check();
|
||||
if (!isRunning) {
|
||||
final path = (await StorageProvider().getBtDirectory())!.path;
|
||||
final config = jsonEncode({"path": path, "address": "127.0.0.1:0"});
|
||||
int port = 0;
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
const channel =
|
||||
MethodChannel('com.kodjodevf.mangayomi.libmtorrentserver');
|
||||
port = await channel.invokeMethod('start', {"config": config});
|
||||
} else {
|
||||
port = await Isolate.run(() async {
|
||||
return libmtorrentserver_ffi.start(config);
|
||||
});
|
||||
}
|
||||
_setBtServerPort(port);
|
||||
}
|
||||
url = isFilePath ? archivePath! : url!;
|
||||
bool isMagnet = url.startsWith("magnet:?");
|
||||
String finalUrl = "";
|
||||
String? infohash;
|
||||
if (!isMagnet) {
|
||||
infohash = await getInfohash(url, isFilePath);
|
||||
finalUrl = "$_baseUrl/torrent/play?infohash=$infohash";
|
||||
} else {
|
||||
port = await Isolate.run(() async {
|
||||
return libmtorrentserver_ffi.start(config);
|
||||
});
|
||||
finalUrl = "$_baseUrl/torrent/play?magnet=$url";
|
||||
}
|
||||
_setBtServerPort(port);
|
||||
}
|
||||
bool isMagnet = !url.startsWith("http");
|
||||
String finalUrl = "";
|
||||
String? infohash;
|
||||
if (!isMagnet) {
|
||||
infohash = await getInfohash(url);
|
||||
finalUrl = "$_baseUrl/torrent/play?infohash=$infohash";
|
||||
} else {
|
||||
finalUrl = "$_baseUrl/torrent/play?magnet=$url";
|
||||
}
|
||||
|
||||
final masterPlaylist = (await http.get(Uri.parse(finalUrl))).body;
|
||||
final videoList = <Video>[];
|
||||
const separator = "#EXTINF:";
|
||||
for (var e in masterPlaylist.substringAfter(separator).split(separator)) {
|
||||
final fileName = e.substringAfter("-1,").substringBefore("\n");
|
||||
if (fileName.isMediaVideo()) {
|
||||
var videoUrl = e.substringAfter("\n").substringBefore("\n");
|
||||
videoList.add(Video(videoUrl, fileName, videoUrl));
|
||||
final masterPlaylist = (await http.get(Uri.parse(finalUrl))).body;
|
||||
final videoList = <Video>[];
|
||||
const separator = "#EXTINF:";
|
||||
for (var e in masterPlaylist.substringAfter(separator).split(separator)) {
|
||||
final fileName = e.substringAfter("-1,").substringBefore("\n");
|
||||
if (fileName.isMediaVideo()) {
|
||||
var videoUrl = e.substringAfter("\n").substringBefore("\n");
|
||||
videoList.add(Video(videoUrl, fileName, videoUrl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (videoList, infohash);
|
||||
return (videoList, infohash);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -110,8 +116,3 @@ void _setBtServerPort(int newPort) {
|
|||
isar.writeTxnSync(() => isar.settings
|
||||
.putSync(isar.settings.getSync(227)!..btServerPort = newPort));
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<bool> mTorrentIsRunning(MTorrentIsRunningRef ref) async {
|
||||
return await MTorrentServer().check();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'torrent_server.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$mTorrentIsRunningHash() => r'04f7fd62d1b64299c040e24721a430a25d2d6b73';
|
||||
|
||||
/// See also [mTorrentIsRunning].
|
||||
@ProviderFor(mTorrentIsRunning)
|
||||
final mTorrentIsRunningProvider = AutoDisposeFutureProvider<bool>.internal(
|
||||
mTorrentIsRunning,
|
||||
name: r'mTorrentIsRunningProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$mTorrentIsRunningHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef MTorrentIsRunningRef = AutoDisposeFutureProviderRef<bool>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
Loading…
Reference in a new issue