local folders now also scans for subtitles

- added help button to show an example of a local folder
This commit is contained in:
Schnitzel5 2025-09-05 21:33:22 +02:00
parent 05d0ddf0d6
commit 5a6552e6f6
20 changed files with 205 additions and 8 deletions

View file

@ -547,6 +547,7 @@
"sequels": "Sequels",
"recommendations": "Recommendations",
"recommendations_similarity": "Similarity:",
"local_folder_structure": "Structure of a local folder",
"local_folder": "Local folders",
"add_local_folder": "Add local folder",
"rescan_local_folder": "Rescan all local folders now",

View file

@ -3351,6 +3351,12 @@ abstract class AppLocalizations {
/// **'Similarity:'**
String get recommendations_similarity;
/// No description provided for @local_folder_structure.
///
/// In en, this message translates to:
/// **'Structure of a local folder'**
String get local_folder_structure;
/// No description provided for @local_folder.
///
/// In en, this message translates to:

View file

@ -1734,6 +1734,9 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1736,6 +1736,9 @@ class AppLocalizationsAs extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1747,6 +1747,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1735,6 +1735,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1752,6 +1752,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1753,6 +1753,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1737,6 +1737,9 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1741,6 +1741,9 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1750,6 +1750,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1749,6 +1749,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1751,6 +1751,9 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1735,6 +1735,9 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1741,6 +1741,9 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -1706,6 +1706,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get recommendations_similarity => 'Similarity:';
@override
String get local_folder_structure => 'Structure of a local folder';
@override
String get local_folder => 'Local folders';

View file

@ -332,8 +332,14 @@ class _SubtitlesWidgetSearchState extends ConsumerState<SubtitlesWidgetSearch> {
try {
final subtitle = subtitles![index];
final storageProvider = StorageProvider();
final animeDir =
widget.chapter.archivePath != null &&
widget.chapter.manga.value?.source == "local"
? Directory(path.dirname(widget.chapter.archivePath!))
: null;
final chapterDirectory = (await storageProvider.getMangaChapterDirectory(
widget.chapter,
mangaMainDirectory: animeDir,
))!;
final subtitleFile = File(
path.join(

View file

@ -131,7 +131,9 @@ Future<void> _scanDirectory(Ref ref, Directory? dir) async {
final files = children.whereType<File>().toList();
// Determine itemtype
final hasImagesFolders = subDirs.isNotEmpty;
final hasImagesFolders = subDirs
.where((e) => !e.path.endsWith("_subtitles"))
.isNotEmpty;
final hasArchives = files.any((f) => _isArchive(f.path));
final hasVideos = files.any((f) => _isVideo(f.path));
final hasEpubs = files.any((f) => _isEpub(f.path));

View file

@ -132,12 +132,21 @@ class _DownloadsScreenState extends ConsumerState<DownloadsScreen> {
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(
context.l10n.local_folder,
style: TextStyle(
fontSize: 13,
color: context.primaryColor,
),
child: Row(
children: [
Text(
context.l10n.local_folder,
style: TextStyle(
fontSize: 13,
color: context.primaryColor,
),
),
const SizedBox(width: 20),
OutlinedButton.icon(
onPressed: () => _showHelpDialog(context),
label: const Icon(Icons.question_mark),
),
],
),
),
FutureBuilder(
@ -163,6 +172,133 @@ class _DownloadsScreenState extends ConsumerState<DownloadsScreen> {
);
}
void _showHelpDialog(BuildContext context) {
final data = (
"LocalFolder",
[
(
"MangaName",
[
("cover.jpg", Icons.image_outlined),
(
"Chapter1",
[
("Page1.jpg", Icons.image_outlined),
("Page2.jpeg", Icons.image_outlined),
("Page3.png", Icons.image_outlined),
("Page4.webp", Icons.image_outlined),
],
),
("Chapter2.cbz", Icons.folder_zip_outlined),
("Chapter3.zip", Icons.folder_zip_outlined),
("Chapter4.cbt", Icons.folder_zip_outlined),
("Chapter5.tar", Icons.folder_zip_outlined),
],
),
(
"AnimeName",
[
("cover.jpg", Icons.image_outlined),
("Episode1.mp4", Icons.video_file_outlined),
(
"Episode1_subtitles",
[
("en.srt", Icons.subtitles_outlined),
("de.srt", Icons.subtitles_outlined),
],
),
("Episode2.mov", Icons.video_file_outlined),
("Episode3.avi", Icons.video_file_outlined),
("Episode4.flv", Icons.video_file_outlined),
("Episode5.wmv", Icons.video_file_outlined),
("Episode6.mpeg", Icons.video_file_outlined),
("Episode7.mkv", Icons.video_file_outlined),
],
),
(
"NovelName",
[
("cover.jpg", Icons.image_outlined),
("NovelName.epub", Icons.book_outlined),
],
),
],
);
Widget buildSubFolder((String, dynamic) data, int level) {
if (data.$2 is List) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text.rich(
TextSpan(
children: [
for (int i = 1; i < level; i++)
const WidgetSpan(child: SizedBox(width: 20)),
if (level > 0)
WidgetSpan(child: Icon(Icons.subdirectory_arrow_right)),
WidgetSpan(child: Icon(Icons.folder)),
const WidgetSpan(child: SizedBox(width: 5)),
TextSpan(text: data.$1),
],
),
),
...(data.$2 as List<(String, dynamic)>).map(
(e) => buildSubFolder(e, level + 1),
),
],
);
}
return Text.rich(
TextSpan(
children: [
for (int i = 1; i < level; i++)
const WidgetSpan(child: SizedBox(width: 20)),
if (level > 0)
WidgetSpan(child: Icon(Icons.subdirectory_arrow_right)),
WidgetSpan(child: Icon(data.$2 as IconData)),
const WidgetSpan(child: SizedBox(width: 5)),
TextSpan(text: data.$1),
],
),
);
}
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text(context.l10n.local_folder_structure),
content: SizedBox(
width: context.width(0.6),
height: context.height(0.8),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(child: buildSubFolder(data, 0)),
),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(context.l10n.cancel),
),
],
),
],
);
},
);
},
);
}
Widget _buildLocalFolder(
AppLocalizations l10n,
List<String> localFolders,

View file

@ -32,9 +32,13 @@ Future<(List<Video>, bool, List<String>, Directory?)> getVideoList(
);
List<String> infoHashes = [];
if (await File(mp4animePath).exists() || isLocalArchive) {
final animeDir =
episode.archivePath != null && episode.manga.value?.source == "local"
? Directory(p.dirname(episode.archivePath!))
: null;
final chapterDirectory = (await storageProvider.getMangaChapterDirectory(
episode,
mangaMainDirectory: mangaDirectory,
mangaMainDirectory: animeDir ?? mangaDirectory,
))!;
final path = isLocalArchive ? episode.archivePath : mp4animePath;
final subtitlesDir = Directory(