diff --git a/violet/lib/component/hitomi/displayed_tag.dart b/violet/lib/component/hitomi/displayed_tag.dart index a159950e4..5cc1a9df0 100644 --- a/violet/lib/component/hitomi/displayed_tag.dart +++ b/violet/lib/component/hitomi/displayed_tag.dart @@ -4,9 +4,9 @@ import 'package:violet/component/hitomi/tag_translate.dart'; class DisplayedTag { - // artist, group, tag, series, character, uploader, type, class, language, prefix, page + // artist, group, tag, series, character, uploader, type, class, language, prefix, page, female, male String? group; - // , female:, male: + // String? name; // String? translated; @@ -14,33 +14,8 @@ class DisplayedTag { // tag := : DisplayedTag({String? tag, this.group, this.name, this.translated}) { if (tag != null) { - final maybeGroup = tag.split(':').first; - if ([ - 'artist', - 'group', - 'tag', - 'series', - 'character', - 'uploader', - 'type', - 'class', - 'language', - 'prefix', - 'page', - ].contains(maybeGroup)) { - group = maybeGroup; - name = tag.substring(tag.indexOf(':') + 1); - } else if (['female', 'male'].contains(maybeGroup)) { - group = 'tag'; - name = tag; - } - } - - if (group != null) { - if (['female', 'male'].contains(group)) { - if (!name!.startsWith('$group:')) name = '$group:$name'; - group = 'tag'; - } + group = tag.split(':').first; + name = tag.substring(tag.indexOf(':') + 1); } } @@ -61,12 +36,4 @@ class DisplayedTag { String toString() { return getTag(); } - - // TODO: https://github.com/project-violet/violet/issues/440 로 삭제 - bool groupEqualTo(String otherGroup) { - if (otherGroup == 'female' || otherGroup == 'male') { - return group == 'tag' && name!.startsWith(otherGroup); - } - return group == otherGroup; - } } diff --git a/violet/lib/component/hitomi/hitomi.dart b/violet/lib/component/hitomi/hitomi.dart index bc1058fca..7fda5dd51 100644 --- a/violet/lib/component/hitomi/hitomi.dart +++ b/violet/lib/component/hitomi/hitomi.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'dart:io'; +import 'package:get/get.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:tuple/tuple.dart'; @@ -46,8 +47,6 @@ class HitomiManager { static String normalizeTagPrefix(String pp) { switch (pp) { - case 'female': - case 'male': case 'tags': return 'tag'; @@ -86,6 +85,24 @@ class HitomiManager { final text = path.readAsStringSync(); tagmap = jsonDecode(text); } + + // split `tag:female:` and `tag:male:` to `female:` and `male:` + if (tagmap!.containsKey('tag')) { + final tags = tagmap!['tag'] as Map; + final femaleTags = tags.entries + .where((e) => e.key.startsWith('female:')) + .map((e) => MapEntry(e.key.split(':')[1], e.value)) + .toList(); + final maleTags = tags.entries + .where((e) => e.key.startsWith('male:')) + .map((e) => MapEntry(e.key.split(':')[1], e.value)) + .toList(); + tagmap!['female'] = Map.fromEntries(femaleTags); + tagmap!['male'] = Map.fromEntries(maleTags); + + tags.removeWhere( + (tag, _) => tag.startsWith('female:') || tag.startsWith('male:')); + } } } @@ -108,44 +125,20 @@ class HitomiManager { String prefix, bool useTranslated) { final groupOrig = prefix.split(':')[0]; final group = normalizeTagPrefix(groupOrig); + final name = prefix.split(':').last; final results = >[]; if (!tagmap!.containsKey(group)) return results; final nameCountsMap = tagmap![group] as Map; if (!useTranslated) { - final tagContains = () { - switch (groupOrig) { - case 'female': - case 'male': - return (key) => - key.toLowerCase().startsWith('$groupOrig:') && - key.toLowerCase().contains(prefix); - - case 'tag': - final po = prefix.split(':')[1]; - return (key) => - !key.toLowerCase().startsWith('female:') && - !key.toLowerCase().startsWith('male:') && - key.toLowerCase().contains(po); - - default: - final po = prefix.split(':')[1]; - return (key) => key.toLowerCase().contains(po) as bool; - } - }(); - - nameCountsMap.entries - .where((element) => tagContains(element.key)) - .forEach((element) { - results.add(Tuple2( - DisplayedTag(group: group, name: element.key), element.value)); - }); + results.addAll(nameCountsMap.entries + .where((e) => e.key.toString().toLowerCase().contains(name)) + .map((e) => Tuple2( + DisplayedTag(group: group, name: e.key), e.value))); } else { - final name = prefix.split(':').last; results.addAll(TagTranslate.containsTotal(name) - .where((e) => - e.groupEqualTo(groupOrig) && nameCountsMap.containsKey(e.name)) + .where((e) => e.group! == group && nameCountsMap.containsKey(e.name)) .map((e) => Tuple2(e, nameCountsMap[e.name]))); } results.sort((a, b) => b.item2.compareTo(a.item2)); @@ -203,53 +196,28 @@ class HitomiManager { if (prefix.contains(':')) { final groupOrig = prefix.split(':')[0]; final group = normalizeTagPrefix(groupOrig); + final name = prefix.split(':').last; + // final results = >[]; if (!tagmap!.containsKey(group)) return >[]; final nameCountsMap = tagmap![group]; if (!useTranslated) { - if (groupOrig == 'female' || groupOrig == 'male') { - nameCountsMap.forEach((key, value) { - if (key.toLowerCase().startsWith('$groupOrig:')) { - results.add(Tuple3( - DisplayedTag(group: groupOrig, name: key), - Distance.levenshteinDistance( - prefix.runes.toList(), key.runes.toList()), - value)); - } - }); - } else if (groupOrig == 'tag') { - // CHECK: tag:female:~, tag:artist:~도 허용해야하는지? - final name = prefix.split(':').last; - nameCountsMap.forEach((key, value) { - if (!key.toLowerCase().startsWith('female:') && - !key.toLowerCase().startsWith('male:')) { - results.add(Tuple3( - DisplayedTag(group: groupOrig, name: key), - Distance.levenshteinDistance( - name.runes.toList(), key.runes.toList()), - value)); - } - }); - } else { - final name = prefix.split(':').last; - nameCountsMap.forEach((key, value) { - results.add(Tuple3( - DisplayedTag(group: group, name: key), - Distance.levenshteinDistance( - name.runes.toList(), key.runes.toList()), - value)); - }); - } + nameCountsMap.forEach((key, value) { + results.add(Tuple3( + DisplayedTag(group: group, name: key), + Distance.levenshteinDistance( + name.runes.toList(), key.runes.toList()), + value)); + }); } else { - final name = prefix.split(':').last; results.addAll(TagTranslate.containsFuzzingTotal(name) .where((e) => - e.item1.groupEqualTo(groupOrig) && + e.item1.group! == group && nameCountsMap.containsKey(e.item1.name)) .map((e) => Tuple3( - e.item1, nameCountsMap[e.item1.name], e.item2))); + e.item1, e.item2, nameCountsMap[e.item1.name]))); } results.sort((a, b) => a.item2.compareTo(b.item2)); return results @@ -259,32 +227,13 @@ class HitomiManager { if (!useTranslated) { final results = >[]; tagmap!.forEach((group, value) { - if (group == 'tag') { - value.forEach((name, count) { - if (name.contains(':')) { - final split = name.split(':'); - results.add(Tuple3( - DisplayedTag(group: split[0], name: name), - Distance.levenshteinDistance( - prefix.runes.toList(), split[1].runes.toList()), - count)); - } else { - results.add(Tuple3( - DisplayedTag(group: 'tag', name: name), - Distance.levenshteinDistance( - prefix.runes.toList(), name.runes.toList()), - count)); - } - }); - } else { - value.forEach((name, count) { - results.add(Tuple3( - DisplayedTag(group: group, name: name), - Distance.levenshteinDistance( - prefix.runes.toList(), name.runes.toList()), - count)); - }); - } + value.forEach((name, count) { + results.add(Tuple3( + DisplayedTag(group: group, name: name), + Distance.levenshteinDistance( + prefix.runes.toList(), name.runes.toList()), + count)); + }); }); results.sort((a, b) => a.item2.compareTo(b.item2)); return results diff --git a/violet/lib/pages/search/search_bar_page.dart b/violet/lib/pages/search/search_bar_page.dart index e81b190b9..46b0eb4de 100644 --- a/violet/lib/pages/search/search_bar_page.dart +++ b/violet/lib/pages/search/search_bar_page.dart @@ -675,7 +675,9 @@ class _SearchBarPageState extends State ? token.split(':').last.replaceAll('_', ' ') : token.replaceAll('_', ' ')) .map((e) => Tuple2( - DisplayedTag(group: 'tag', name: e.item1), + DisplayedTag( + group: e.item1.split(':').first, + name: e.item1.split(':').last), (e.item2 * 100).toInt())) .toList(); } else if (token.startsWith('series:')) { @@ -785,14 +787,12 @@ class _SearchBarPageState extends State } if (info.item2 > 0 && Settings.searchShowCount) { - count = - ' (${info.item2.toString() + (related && info.item1.group == 'tag' ? '%' : '')})'; + count = ' (${info.item2.toString() + (related ? '%' : '')})'; } - if (info.item1.group == 'tag' && info.item1.name!.startsWith('female:')) { + if (info.item1.group == 'female') { color = Colors.pink; - } else if (info.item1.group == 'tag' && - info.item1.name!.startsWith('male:')) { + } else if (info.item1.group == 'male') { color = Colors.blue; } else if (info.item1.group == 'prefix') { color = Colors.orange; @@ -884,11 +884,7 @@ class _SearchBarPageState extends State labelPadding: const EdgeInsets.all(0.0), avatar: CircleAvatar( backgroundColor: Colors.grey.shade600, - child: Text(info.item1.group == 'tag' && - (info.item1.name!.startsWith('female:') || - info.item1.name!.startsWith('male:')) - ? info.item1.name![0].toUpperCase() - : info.item1.group![0].toUpperCase()), + child: Text(info.item1.group![0].toUpperCase()), ), label: RichText( text: TextSpan( @@ -907,12 +903,7 @@ class _SearchBarPageState extends State onPressed: () async { // Insert text to cursor. if (info.item1.group != 'prefix') { - var insert = (info.item1.group == 'tag' && - (info.item1.name!.startsWith('female') || - info.item1.name!.startsWith('male')) - ? info.item1.name - : info.item1.getTag())! - .replaceAll(' ', '_'); + final insert = info.item1.getTag().replaceAll(' ', '_'); _searchController.text = _searchText!.substring(0, _insertPos) + insert + @@ -923,11 +914,18 @@ class _SearchBarPageState extends State extentOffset: _insertPos! + insert.length, ); - if (info.item1.group == 'tag') { - _relatedLists = HitomiIndexs.getRelatedTag( - info.item1.name!.replaceAll('_', ' ')) + if (info.item1.group == 'tag' || + info.item1.group == 'female' || + info.item1.group == 'male') { + _relatedLists = HitomiIndexs.getRelatedTag(info.item1.group == 'tag' + ? info.item1.name!.replaceAll('_', ' ') + : info.item1.getTag().replaceAll('_', ' ')) .map((e) => Tuple2( - DisplayedTag(group: 'tag', name: e.item1), + DisplayedTag( + group: e.item1.contains(':') + ? e.item1.split(':').first + : 'tag', + name: e.item1.split(':').last), (e.item2 * 100).toInt())) .toList(); setState(() {}); diff --git a/violet/lib/pages/settings/tag_selector.dart b/violet/lib/pages/settings/tag_selector.dart index efa828d30..82385ef6a 100644 --- a/violet/lib/pages/settings/tag_selector.dart +++ b/violet/lib/pages/settings/tag_selector.dart @@ -227,10 +227,11 @@ class _TagSelectorDialogState extends State { if (info.item2 > 0 && _showCount) count = ' (${info.item2})'; - if (info.item1.group == 'tag' && info.item1.name!.startsWith('female:')) { + print(info.item1); + + if (info.item1.group == 'female') { color = Colors.pink; - } else if (info.item1.group == 'tag' && - info.item1.name!.startsWith('male:')) { + } else if (info.item1.group == 'male') { color = Colors.blue; } else if (info.item1.group == 'prefix') { color = Colors.orange; @@ -244,15 +245,11 @@ class _TagSelectorDialogState extends State { color = Colors.orange; } - var fc = RawChip( + final fc = RawChip( labelPadding: const EdgeInsets.all(0.0), avatar: CircleAvatar( backgroundColor: Colors.grey.shade600, - child: Text(info.item1.group == 'tag' && - (info.item1.name!.startsWith('female:') || - info.item1.name!.startsWith('male:')) - ? info.item1.name![0].toUpperCase() - : info.item1.group![0].toUpperCase()), + child: Text(info.item1.group![0].toUpperCase()), ), label: Text( ' $tagDisplayed$count', @@ -267,12 +264,7 @@ class _TagSelectorDialogState extends State { onPressed: () async { // Insert text to cursor. if (info.item1.group != 'prefix') { - var insert = (info.item1.group == 'tag' && - (info.item1.name!.startsWith('female') || - info.item1.name!.startsWith('male')) - ? info.item1.name - : info.item1.getTag())! - .replaceAll(' ', '_'); + final insert = info.item1.getTag().replaceAll(' ', '_'); _searchController.text = _searchText!.substring(0, _insertPos) + insert + diff --git a/violet/test/query_test.dart b/violet/test/query_test.dart index 712c9aa66..209501e19 100644 --- a/violet/test/query_test.dart +++ b/violet/test/query_test.dart @@ -17,7 +17,7 @@ void main() { final result0 = await HitomiManager.queryAutoComplete('fema'); final result1 = await HitomiManager.queryAutoComplete('random:'); - expect(result0[0].item1.toString(), 'tag:female:sole female'); + expect(result0[0].item1.toString(), 'female:sole female'); expect(result1.length, 0); }); @@ -25,8 +25,8 @@ void main() { final result0 = await HitomiManager.queryAutoComplete('단독여', true); final result1 = await HitomiManager.queryAutoComplete('male:단독', true); - expect(result0[0].item1.toString(), 'tag:female:sole female'); - expect(result1[0].item1.toString(), 'tag:male:sole male'); + expect(result0[0].item1.toString(), 'female:sole female'); + expect(result1[0].item1.toString(), 'male:sole male'); }); test('Hitomi Query Auto Complete Fuzzy', () async { @@ -38,7 +38,7 @@ void main() { expect(result0[0].item1.toString(), 'artist:michiking'); expect(result1[0].item1.toString(), 'artist:michiking'); - expect(result2[0].item1.toString(), 'tag:female:big breasts'); + expect(result2[0].item1.toString(), 'female:big breasts'); }); test('Hitomi Query To Sql', () {