-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsorter.groovy
4561 lines (4530 loc) · 259 KB
/
sorter.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package lib
import groovy.transform.Field
import net.filebot.Logging
import net.filebot.WebServices
import net.filebot.web.SearchResult
//--- VERSION 1.10.0
//import net.filebot.web.TheTVDBSeriesInfo
import net.filebot.web.SeriesInfo
import org.apache.commons.text.similarity.JaroWinklerDistance
import com.cedarsoftware.util.io.JsonObject
import net.filebot.web.Episode
import java.util.regex.Matcher
//import org.apache.commons.lang3.ObjectUtils
// VOID - /(?i)((^[a-z\s]+)\(?((19\d\d|20\d\d)\)?\s))/
// VOID - /(?i)((^[a-z\s-]+)\(?((19\d\d|20\d\d)\)?\b))/
// VOID - /(?i)((^[^\d\(]+)\(?((19\d\d|20\d\d)\)?\b))/ // Thank you Intellij, but the escape is needed
// VOID - /(?i)((^[^\(]+)\(?((19\d\d|20\d\d)\)?\b))/
@Field String stripYearDateRegex = /(?i)[\.\(|\s|\[]((19|20)\d\d)(?!\w)[\)|\s|\]|\r|\n|\t|\W]?/
// alt matcher - (?i)((^[^\(]+)\(?((19\d\d|20\d\d)\)?\b))
// alt matcher 2 - (?i)((^[a-z\s-]+)\(?((19\d\d|20\d\d)\)?\s))
// OVA, ONA or Special and all spaces, dashes etc around them. It also requires at least one around the word.
// Match 1 - The "Type" aka ova, ONA, OAD, SPECIAL, bsp, bspe, bonus etc. with spaces
// Match 2 - The "Type" aka ova, ONA, OAD, SPECIAL, bsp, bspe, bonus etc. without spaces :)
// VOID - /(?i)([-\s(]+(\bOAV|\bOVA|\bONA|\bOAD|\bSPECIAL\b|\bsp\d{1,2}|\bspe\d{1,2}|\bbonus)(\b\d)?)[-\s\)]?/
@Field String ovaOnaOadSpecialBonusSyntaxMatcher = /(?i)([-\s(]+(\bOAV|\bOVA|\bONA|\bOAD|\bSPECIAL\b|\bsp\d{1,2}|\bspe\d{1,2}|\bbonus)([\s]\d)?)[-\s\)]?/
@Field String matchEndOfLineVersion = /v(\d{1,2})\s?$/
@Field String stripTrailingSpacesDashRegex = /([\s-])*$/
/**
* Generate the possible "series" anime names
* Interger Series Syntax (series 1), Roman Ordinal Series Syntax (series i), or Ordinal Series Syntax (series 2nd)
*
* @param String animeName
* @return HashSet of [seriesGeneratedNames]
*/
HashSet seriesNameGenerator ( String baseAnimeName, Integer mySeasonalityNumber, Boolean hasRomanSeries ) {
HashSet tempBaseGeneratedAnimeNames = []
String generatedAnimeName
if ( mySeasonalityNumber > 1 ) {
// ---------- Add Series Name Varients as options ---------- //
generatedAnimeName = baseAnimeName + ' ' + getOrdinalNumber(mySeasonalityNumber) // anime 2nd
Logging.log.info "----- Adding Ordinal Seasonality Anime Name to Anime Name List - ${generatedAnimeName}"
tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
if ( mySeasonalityNumber < 10 ) {
generatedAnimeName = baseAnimeName + ' ' + getRomanOrdinal(mySeasonalityNumber) // anime II
Logging.log.info "----- Adding Seasonality Anime Name to Anime Name List - ${generatedAnimeName}"
tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
}
generatedAnimeName = baseAnimeName + ' ' + mySeasonalityNumber // anime 2
Logging.log.info "----- Adding Series # Anime Name to Anime Name List - ${generatedAnimeName}"
tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
} else if ( hasRomanSeries ) {
generatedAnimeName = baseAnimeName + ' ' + getRomanOrdinal(mySeasonalityNumber) // anime II
Logging.log.info "----- Adding Series Anime Name to Anime Name List - ${generatedAnimeName}"
tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
}
return tempBaseGeneratedAnimeNames
}
/**
* Generate the base anime names we will be using to build possible Anime Series names to search for
* Basenames are the base part of the series name, later we add/modify based on things like the various seasonality syntax (Partial, Ordinal etc)
*
* @param group The LinkedHashMap for this group of files
* @param useBaseAnimeNameWithSeriesSyntax Should we add the Base Anime Name when the Series has "Series" Syntax
* @param seriesBasenameGeneratorOverrideJsonFile The Series Basename Override file
* @return ArrayList of [[group], baseGeneratedAnimeNames ]
*/
ArrayList seriesBasenameGenerator(LinkedHashMap group, Boolean useBaseAnimeNameWithSeriesSyntax, ArrayList seriesBasenameGeneratorOverrideJsonFile) {
tempBaseGeneratedAnimeNames = [] as HashSet
baseGeneratedAnimeNames = [] as HashSet
String baseAnimeName
Logging.log.info '// START---------- Basename Generation ---------- //'
Logging.log.finest "${groupInfoGenerator(group)}"
Logging.log.finest "group.class:${group.getClass()}"
Logging.log.info "--- group.anime - ${group.anime}"
Logging.log.info "--- group.altTitle - ${group.altTitle}"
switch(group.anime) {
// A little bit off
case ~/phi brain/:
baseAnimeName = 'Phi Brain: Kami no Puzzle'
group.anime = baseAnimeName
break
case ~/drawing jianghu of bu liang ren/:
baseAnimeName = 'Hua Jianghu: Buliang Ren'
group.anime = baseAnimeName
break
case ~/blocker army machine blaster/:
baseAnimeName = 'blocker army iv machine blaster'
group.anime = baseAnimeName
break
case ~/saikyo robo daiouja/:
baseAnimeName = 'Saikyou Robo Daiouja'
group.anime = baseAnimeName
break
// While the short name is good, when you add the year .. less so
case ~/genjitsu/:
baseAnimeName = 'Genjitsu Shugi Yuusha no Oukoku Saikenki'
group.anime = baseAnimeName
break
case ~/genjitsu shugi yuusha/:
baseAnimeName = 'Genjitsu Shugi Yuusha no Oukoku Saikenki'
group.anime = baseAnimeName
break
// Close .. but not close enough
case ~/battle through the heaven 3 years agreement/:
baseAnimeName = 'Battle Through The Heaven 3 Year Agreement'
group.anime = baseAnimeName
break
// How is this supposed to actually find something?
case ~/fanrenxiuxianzhuan mdzf/:
baseAnimeName = 'Fanren Xiuxian Chuan Mo Dao Zheng Feng'
group.anime = baseAnimeName
break
// Huh. It actually DOES have TV in the official name
case ~/yarou nanana kaibutsu kraken o oe!/:
baseAnimeName = 'TV Yarou Nanana: Kaibutsu Kraken o Oe!'
group.anime = baseAnimeName
break
// the duke and his maid
case ~/the duke and his maid/:
baseAnimeName = 'The Duke of Death and His Maid'
group.anime = baseAnimeName
break
// tdg
case ~/tdg/:
baseAnimeName = 'Tales of Demons and Gods'
group.anime = baseAnimeName
break
// Taishou Otome Otogibanashi
case ~/taishou otome/:
baseAnimeName = 'Taishou Otome Otogibanashi'
group.anime = baseAnimeName
break
// Lion Force
case ~/lion force/:
baseAnimeName = 'Hyakujuu Ou Golion'
group.anime = baseAnimeName
break
// Iruma-kun is not an accepted short
case ~/iruma-kun/:
baseAnimeName = 'Mairimashita! Iruma-kun'
group.anime = baseAnimeName
break
// Mini Dragon (Miss Kobayashi's Dragon Maid S Short Animation Series) is considered special episodes
// case ~/mini dragon/:
// baseAnimeName = 'Kobayashi-san Chi no Maidragon S'
// group.anime = baseAnimeName
// group.isSpecialEpisode = true
// break
case ~/stellar transformations/:
baseAnimeName = 'stellar transformation'
group.anime = baseAnimeName
break
// Monster Farm in TVDB is not anime
case ~/monster farm/:
baseAnimeName = 'monster rancher'
group.anime = baseAnimeName
break
// Filebot detected name is *almost* there ..
case ~/kmplx m3 the dark metal/:
baseAnimeName = 'M3 the dark metal'
group.anime = baseAnimeName
break
// Close ..
case ~/granblue/:
baseAnimeName = 'Granblue Fantasy The Animation'
group.anime = baseAnimeName
break
// Close ..
case ~/hige wo soru soshite joshikousei wo hirou/:
baseAnimeName = 'Hige o Soru. Soshite Joshikousei o Hirou.'
group.anime = baseAnimeName
break
// Considered a Special, not a "Real" series
// case ~/mobile suit gundam zz frag/:
// baseAnimeName = 'Kidou Senshi Gundam ZZ'
// group.anime = baseAnimeName
// group.isSpecialEpisode = true
// break
// Not a common short name for it..
case ~/kono sekai the animation/:
baseAnimeName = 'The World Ends with You the Animation'
group.anime = baseAnimeName
break
// Close, and yet so far ..
case ~/jiranaide, nagatoro-san/:
baseAnimeName = 'Ijiranaide, Nagatoro-san'
group.anime = baseAnimeName
break
// Special Name != Anime name
// case ~/sk crazy rock jam/:
// baseAnimeName = 'SK8 the Infinity'
// group.anime = baseAnimeName
// group.isSpecialEpisode = true
// break
// Special Name != Anime name
case ~/himouto! umaru-chans/:
baseAnimeName = 'Himouto! Umaru-chan'
group.anime = baseAnimeName
break
// Spelling is important
case ~/nejimaki seirei senki tenkyo no aruderamin/:
baseAnimeName = 'Nejimaki Seirei Senki: Tenkyou no Alderamin'
group.anime = baseAnimeName
break
// Because the actual romanization title has ... at the end in AniDB
case ~/otome game no hametsu flag shika nai akuyaku reijou ni tensei shiteshimatta/:
baseAnimeName = 'My Next Life as a Villainess: All Routes Lead to Doom!'
group.anime = baseAnimeName
break
case ~/Pokemon XY/:
baseAnimeName = 'Pocket Monsters XY'
group.anime = baseAnimeName
break
case ~/motto! ojamajo doremi kaeru seki no himitsu/:
baseAnimeName = 'Eiga Motto! Ojamajo Doremi Kaeru Ishi no Himitsu'
group.anime = baseAnimeName
break
case ~/turning mecard w secret of vandine/:
baseAnimeName = 'Turning Mecard W: Vandyne-ui Bimil'
group.anime = baseAnimeName
break
// Spelling is important
case ~/nantsu no taizai/:
baseAnimeName = 'Nanatsu no Taizai'
group.anime = baseAnimeName
break
// Spelling is important
case ~/otona no bogaya san/:
baseAnimeName = 'Otona no Bouguya-san'
group.anime = baseAnimeName
break
case ~/girl gaku ~hijiri girls square gakuin~/:
baseAnimeName = 'Girl Gaku. Sei Girls Square Gakuin'
group.anime = baseAnimeName
break
case ~/girl gaku/:
baseAnimeName = 'Girl Gaku. Sei Girls Square Gakuin'
group.anime = baseAnimeName
break
case ~/hanyou no yashahime/:
baseAnimeName = 'Han`you no Yashahime: Sengoku Otogizoushi'
group.anime = baseAnimeName
break
case ~/sk/:
// while yes, sk is a short for Shaman King, however more recently it's a widely used short for SK8 The Infinity, so let's go with that for now.
// Tho why SK vs SK8 as the short name to use ... only the release groups using SK know ..
baseAnimeName = 'SK8 the Infinity'
group.anime = baseAnimeName
break
case ~/rezero kara hajimeru break time|re zero break time/:
// AniDB has this as specials under Re:Zero kara Hajimeru Isekai Seikatsu
baseAnimeName = 'Re:Zero kara Hajimeru Isekai Seikatsu'
group.anime = baseAnimeName
break
case ~/digimon savers kyuukyoku power burst mode hatsudou!!/:
baseAnimeName = 'Digimon Savers The Movie: Kyuukyoku Power! Burst Mode Hatsudou!!'
group.anime = baseAnimeName
break
case ~/mushoku tensei/:
baseAnimeName = 'Mushoku Tensei: Isekai Ittara Honki Dasu'
group.anime = baseAnimeName
break
case ~/munou no nana/:
baseAnimeName = 'Munou na Nana'
group.anime = baseAnimeName
break
case ~/hanaukyo maid tai/:
baseAnimeName = 'hanaukyou maid tai'
group.anime = baseAnimeName
break
case ~/wixoss divalive/:
baseAnimeName = 'Wixoss Diva(A)Live'
group.anime = baseAnimeName
break
case ~/2 43 seiin volley bu/:
baseAnimeName = '2.43 Seiin Koukou Danshi Volley Bu'
group.anime = baseAnimeName
break
case ~/the wonderful adventures of nils/:
baseAnimeName = 'Nils no Fushigi na Tabi'
group.anime = baseAnimeName
break
case ~/tatoeba last dungeon/:
baseAnimeName = 'Suppose a Kid from the Last Dungeon Boonies Moved to a Starter Town'
group.anime = baseAnimeName
break
case ~/evangelion 1 0 you are alone/:
baseAnimeName = 'evangelion 1.0 you are not alone'
group.anime = baseAnimeName
break
case ~/evangelion 1 11 - you are alone/:
baseAnimeName = 'evangelion 1.0 you are not alone'
group.anime = baseAnimeName
break
case ~/evangelion 2 0 you can advance/:
baseAnimeName = 'evangelion 2.0 you can not advance'
group.anime = baseAnimeName
break
case ~/evangelion 3 0 you can redo,/:
baseAnimeName = 'evangelion 3.0 you can not redo,'
group.anime = baseAnimeName
break
case ~/otome game/:
baseAnimeName = 'My Next Life as a Villainess: All Routes Lead to Doom!'
group.anime = baseAnimeName
break
case ~/otome game no hametsu/:
baseAnimeName = 'My Next Life as a Villainess: All Routes Lead to Doom!'
group.anime = baseAnimeName
break
case ~/gate - jieitai kanochi nite, kaku tatakaeri/:
baseAnimeName = 'Gate: Thus the JSDF Fought There'
group.anime = baseAnimeName
break
case ~/let's & go!! wgp/:
baseAnimeName = 'Bakusou Kyoudai Lets Go!! WGP'
group.anime = baseAnimeName
break
case ~/let s go wgp/:
baseAnimeName = 'Bakusou Kyoudai Lets Go!! WGP'
group.anime = baseAnimeName
break
case ~/recorder to randoseru re/:
baseAnimeName = 'Recorder to Ransel Re'
group.anime = baseAnimeName
break
case ~/recorder to randoseru do/:
baseAnimeName = 'Recorder to Ransel Do'
group.anime = baseAnimeName
break
case ~/recorder to randoseru mi/:
baseAnimeName = 'Recorder to Ransel Mi'
group.anime = baseAnimeName
break
case ~/pokmon journeys/:
baseAnimeName = 'Pokemon Journeys: The Series'
group.anime = baseAnimeName
break
case ~/carpe reborn/:
baseAnimeName = 'Yuan Long'
group.anime = baseAnimeName
break
case ~/yuan long - carp reborn/:
baseAnimeName = 'Yuan Long'
group.anime = baseAnimeName
break
case ~/maou-sama, petit retry!/:
baseAnimeName = 'Maou-sama, Retry!'
group.anime = baseAnimeName
break
case ~/ling long - ling cage - incarnation/:
baseAnimeName = 'Ling Long: Incarnation Xia Ban Ji'
group.anime = baseAnimeName
break
case ~/infinite stratos/:
baseAnimeName = 'IS: Infinite Stratos'
group.anime = baseAnimeName
break
case ~/pocket monsters twilight wings/:
baseAnimeName = 'Pokemon: Twilight Wings'
group.anime = baseAnimeName
break
case ~/anime kapibara san/:
baseAnimeName = 'Anime Kabibarasan'
group.anime = baseAnimeName
break
case ~/desolate era/:
baseAnimeName = 'Mang Huang Ji'
group.anime = baseAnimeName
break
case ~/kyou kara ore wa/:
baseAnimeName = 'Kyou kara Ore wa!!'
group.anime = baseAnimeName
break
case ~/higurashi no naku koro ni 2020/:
baseAnimeName = 'Higurashi: When They Cry - Gou'
group.anime = baseAnimeName
break
case ~/ace of diamond act/:
baseAnimeName = 'Ace of the Diamond: Act'
group.anime = baseAnimeName
break
case ~/isekai maou to shoukan/:
baseAnimeName = 'Isekai Maou to Shoukan Shoujo no Dorei Majutsu'
group.anime = baseAnimeName
break
case ~/Arifureta/:
baseAnimeName = 'Arifureta Shokugyou de Sekai Saikyou'
group.anime = baseAnimeName
break
case ~/honzuki no gekokujou - shisho ni naru tame ni wa shudan wo erandeiraremasen/:
baseAnimeName = 'Ascendance of a Bookworm'
group.anime = baseAnimeName
break
case ~/honzuki no gekokujou shisho ni naru tame ni wa shudan o erandeiraremasen/:
baseAnimeName = 'Ascendance of a Bookworm'
group.anime = baseAnimeName
break
case ~/milf isekai/:
baseAnimeName = 'Do You Love Your Mom and Her Two-Hit Multi-Target Attacks'
group.anime = baseAnimeName
break
case ~/my teenage romcom snafu climax/:
baseAnimeName = 'My Teen Romantic Comedy SNAFU Climax'
group.anime = baseAnimeName
break
case ~/redo of a healer/:
baseAnimeName = 'Kaifuku Jutsushi no Yarinaoshi'
group.anime = baseAnimeName
break
// As of 02/24/2021 AniDB has the TV episodes as type "other" in AniDB, which the Entry being a Movie. Filebot can't do anything with that.
// Switch to the TVDB name (which probably will not match anything in AniDB)
case ~/wave!! surfing yappe!!/:
baseAnimeName = 'WAVE!! -Let\'s go surfing!!- (TV)'
group.anime = baseAnimeName
break
default:
// Use group.anime for consistency here, so we can do further processing on the baseAnimeName should we want to
// As we would use the "Raw" name on overrides ..
baseAnimeName = group.anime
// baseAnimeName = jwdStringBlender(group.anime)
break
}
// ---------- Checking for Name Variations --- //
// Anime names that don't really match well, so they need help
// Using groupOverrides() to check for a match and return an updated group to us (if needed)
// group.groupOverride will either be null (no override applied)
group = groupOverrides(group, seriesBasenameGeneratorOverrideJsonFile)
if ( group.groupOverride != null ) {
Logging.log.info "--- Group Override Applied: [${group.groupOverride}]"
Logging.log.info "--- Group now: [${group}]"
baseAnimeName = group.anime
}
Logging.log.info "----- baseAnimeName - ${baseAnimeName}"
// if there is an Alternative Title, iterate over that and the baseAnimeName when compiling our list of
// possible base Anime names to add to tempBaseGeneratedAnimeNames
def checkAnimeNameList = group.altTitle != null ? [ baseAnimeName, group.altTitle as String ] : [ baseAnimeName ]
checkAnimeNameList.each { anime ->
Logging.log.info "----- Checking Anime Name - ${anime}"
// If it ends with Special or Bonus, remove that and add that as a basename.
// VOID - myOVARegexMatcher = group.anime =~ /(?i)([-\s\(]+(\bOAV|\bOVA|\bONA|\bOAD|\bSPECIAL|\bsp\d{1,2}|\bbonus)(\b\d)?)[-\s\)]?$/
// VOID - myOVARegexMatcher = group.anime =~ /(?i)([-\s(]+(\bOAV|\bOVA|\bONA|\bOAD|\bSPECIAL|\bsp\d{1,2}|\bbonus)(\b\d)?)[-\s)]?$/
myOVARegexMatcher = anime =~ /${ovaOnaOadSpecialBonusSyntaxMatcher}/
if ( myOVARegexMatcher.find() ) {
generatedAnimeName = anime.replaceAll(/${ovaOnaOadSpecialBonusSyntaxMatcher}/, '')
tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
Logging.log.info "----- TEMP: OVAMatcher - Adding to base Name List - [${generatedAnimeName}] "
}
// Unfortunately there is at *least* one anime that officially ends with v3, so I can't just ignore the "version" and the end.
// - https://anidb.net/anime/5078
myRegexMatcher = anime =~ /${matchEndOfLineVersion}/
if ( myRegexMatcher.find() ) {
generatedAnimeName = anime.replaceAll(/${matchEndOfLineVersion}/, '')
tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
Logging.log.info "----- TEMP: End of Line Version - Adding to base Name List - [${generatedAnimeName}] "
}
if (!group.hasSeriesSyntax || (group.hasSeriesSyntax && useBaseAnimeNameWithSeriesSyntax) || (group.hasSeriesSyntax && group.order == 'airdate')) {
Logging.log.info "----- TEMP: Adding to base Name List - [${anime}] "
tempBaseGeneratedAnimeNames += ["${anime}"]
}
if ( anime =~ /(?i)^the\s/ ) {
Logging.log.info "----- TEMP: 'the' prefix - Adding to base Name List - [${anime.replaceFirst(/(?i)^the\\s/, '')}] "
tempBaseGeneratedAnimeNames += ["${anime.replaceFirst(/(?i)^the\s/, '')}"]
}
// If it has "series" syntax that can mean series 1 (Interger Series Syntax), series i (Roman Ordinal Series Syntax), or series 2nd (Ordinal Series Syntax)
// Note: Ordinal Series Syntax is NOT the same as Ordinal Seasonality Syntax (series 2nd Season). While it's more common to see Ordinal Seasonality Syntax
// There are some anime that actually use Ordinal Series Syntax.
if ( group.hasSeriesSyntax ) {
Logging.log.info "----- hasSeriesSyntax detected"
hasSeasonality = true
switch (group.seriesNumber) {
case ~/[0-9]+/:
mySeasonalityNumber = group.seriesNumber.toInteger()
Logging.log.info "----- Numerical Series - mySeasonalityNumber: ${mySeasonalityNumber}"
hasRomanSeries = false
break
default:
// mySeasonalityNumber = group.seriesNumber
mySeasonalityNumber = getNumberFromRomanOrdinal(group.seriesNumber)
Logging.log.info "----- Roman Series - Ordinal: ${group.seriesNumber}"
Logging.log.info "----- Roman Series - mySeasonalityNumber: ${mySeasonalityNumber}"
hasRomanSeries = true
break
}
tempBaseGeneratedAnimeNames += seriesNameGenerator(anime, mySeasonalityNumber, hasRomanSeries)
// if ( group.altTitle != null ) {
// Logging.log.info "----- Alternative Title Detected:[${group.altTitle}]"
//// Logging.log.info "----- Adding Alternative Title to Anime Name List - ${group.altTitle}"
// tempBaseGeneratedAnimeNames += seriesNameGenerator(group.altTitle as String, mySeasonalityNumber, hasRomanSeries)
//// tempBaseGeneratedAnimeNames += ["${group.altTitle}"]
// // Unfortunately there is at *least* one anime that officially ends with v3, so I can't just ignore the "version" and the end.
// // - https://anidb.net/anime/5078
// myRegexMatcher = group.altTitle =~ /${matchEndOfLineVersion}/
// if ( myRegexMatcher.find() ) {
// Logging.log.info "----- TEMP: End of Line Version - Adding to base Name List"
// generatedAnimeName = group.altTitle.replaceAll(/${matchEndOfLineVersion}/, '')
// tempBaseGeneratedAnimeNames += seriesNameGenerator(generatedAnimeName as String, mySeasonalityNumber, hasRomanSeries)
// }
// }
if ( anime =~ /(?i)^the\s/ ) {
Logging.log.info "----- 'the' prefix detected':[${anime}]"
tempBaseGeneratedAnimeNames += seriesNameGenerator(anime.replaceFirst(/(?i)^the\s/, ''), mySeasonalityNumber, hasRomanSeries)
// Unfortunately there is at *least* one anime that officially ends with v3, so I can't just ignore the "version" and the end.
// - https://anidb.net/anime/5078
myRegexMatcher = anime =~ /${matchEndOfLineVersion}/
if ( myRegexMatcher.find() ) {
Logging.log.info "----- TEMP: End of Line Version - Adding to base Name List"
generatedAnimeName = anime.replaceAll(/${matchEndOfLineVersion}/, '')
tempBaseGeneratedAnimeNames += seriesNameGenerator(generatedAnimeName as String, mySeasonalityNumber, hasRomanSeries)
}
}
}
}
// // If it ends with Special or Bonus, remove that and add that as a basename.
// // VOID - myOVARegexMatcher = group.anime =~ /(?i)([-\s\(]+(\bOAV|\bOVA|\bONA|\bOAD|\bSPECIAL|\bsp\d{1,2}|\bbonus)(\b\d)?)[-\s\)]?$/
// // VOID - myOVARegexMatcher = group.anime =~ /(?i)([-\s(]+(\bOAV|\bOVA|\bONA|\bOAD|\bSPECIAL|\bsp\d{1,2}|\bbonus)(\b\d)?)[-\s)]?$/
// myOVARegexMatcher = anime =~ /${ovaOnaOadSpecialBonusSyntaxMatcher}/
// if ( myOVARegexMatcher.find() ) {
// generatedAnimeName = anime.replaceAll(/${ovaOnaOadSpecialBonusSyntaxMatcher}/, '')
// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
// Logging.log.info "----- TEMP: OVAMatcher - Adding to base Name List - [${generatedAnimeName}] "
// }
// // Unfortunately there is at *least* one anime that officially ends with v3, so I can't just ignore the "version" and the end.
// // - https://anidb.net/anime/5078
// myRegexMatcher = group.anime =~ /${matchEndOfLineVersion}/
// if ( myRegexMatcher.find() ) {
// generatedAnimeName = group.anime.replaceAll(/${matchEndOfLineVersion}/, '')
// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
// Logging.log.info "----- TEMP: End of Line Version - Adding to base Name List - [${generatedAnimeName}] "
// }
// if (!group.hasSeriesSyntax || (group.hasSeriesSyntax && useBaseAnimeNameWithSeriesSyntax) || (group.hasSeriesSyntax && group.order == 'airdate')) {
// Logging.log.info "----- TEMP: Adding to base Name List - [${baseAnimeName}] "
// tempBaseGeneratedAnimeNames += ["${baseAnimeName}"]
// }
// if ( baseAnimeName =~ /(?i)^the\s/ ) {
// Logging.log.info "----- TEMP: 'the' prefix - Adding to base Name List - [${baseAnimeName}] "
// tempBaseGeneratedAnimeNames += ["${baseAnimeName.replaceFirst(/(?i)^the\s/, '')}"]
// }
// // If it has "series" syntax that can mean series 1 (Interger Series Syntax), series i (Roman Ordinal Series Syntax), or series 2nd (Ordinal Series Syntax)
// // Note: Ordinal Series Syntax is NOT the same as Ordinal Seasonality Syntax (series 2nd Season). While it's more common to see Ordinal Seasonality Syntax
// // There are some anime that actually use Ordinal Series Syntax.
// if ( group.hasSeriesSyntax ) {
// Logging.log.info "----- hasSeriesSyntax detected"
// hasSeasonality = true
// switch (group.seriesNumber) {
// case ~/[0-9]+/:
// mySeasonalityNumber = group.seriesNumber.toInteger()
// Logging.log.info "----- Numerical Series - mySeasonalityNumber: ${mySeasonalityNumber}"
// hasRomanSeries = false
// break
// default:
//// mySeasonalityNumber = group.seriesNumber
// mySeasonalityNumber = getNumberFromRomanOrdinal(group.seriesNumber)
// Logging.log.info "----- Roman Series - Ordinal: ${group.seriesNumber}"
// Logging.log.info "----- Roman Series - mySeasonalityNumber: ${mySeasonalityNumber}"
// hasRomanSeries = true
// break
// }
// tempBaseGeneratedAnimeNames += seriesNameGenerator(baseAnimeName, mySeasonalityNumber, hasRomanSeries)
// if ( group.altTitle != null ) {
// Logging.log.info "----- Alternative Title Detected:[${group.altTitle}]"
//// Logging.log.info "----- Adding Alternative Title to Anime Name List - ${group.altTitle}"
// tempBaseGeneratedAnimeNames += seriesNameGenerator(group.altTitle as String, mySeasonalityNumber, hasRomanSeries)
//// tempBaseGeneratedAnimeNames += ["${group.altTitle}"]
// // Unfortunately there is at *least* one anime that officially ends with v3, so I can't just ignore the "version" and the end.
// // - https://anidb.net/anime/5078
// myRegexMatcher = group.altTitle =~ /${matchEndOfLineVersion}/
// if ( myRegexMatcher.find() ) {
// Logging.log.info "----- TEMP: End of Line Version - Adding to base Name List"
// generatedAnimeName = group.altTitle.replaceAll(/${matchEndOfLineVersion}/, '')
// tempBaseGeneratedAnimeNames += seriesNameGenerator(generatedAnimeName as String, mySeasonalityNumber, hasRomanSeries)
// }
// }
// if ( baseAnimeName =~ /(?i)^the\s/ ) {
// Logging.log.info "----- 'the' prefix detected':[${baseAnimeName}]"
// tempBaseGeneratedAnimeNames += seriesNameGenerator(baseAnimeName.replaceFirst(/(?i)^the\s/, ''), mySeasonalityNumber, hasRomanSeries)
// // Unfortunately there is at *least* one anime that officially ends with v3, so I can't just ignore the "version" and the end.
// // - https://anidb.net/anime/5078
// myRegexMatcher = baseAnimeName =~ /${matchEndOfLineVersion}/
// if ( myRegexMatcher.find() ) {
// Logging.log.info "----- TEMP: End of Line Version - Adding to base Name List"
// generatedAnimeName = baseAnimeName.replaceAll(/${matchEndOfLineVersion}/, '')
// tempBaseGeneratedAnimeNames += seriesNameGenerator(generatedAnimeName as String, mySeasonalityNumber, hasRomanSeries)
//
// }
// }
// // baseAnimeName = "${jwdStringBlender(group.anime)}" // Always add the group.anime name
// // Logging.log.info "----- Adding Base Anime Name to base Name List - ${baseAnimeName} - Season 1/0"
// // tempBaseGeneratedAnimeNames += ["${baseAnimeName}"]
//// if ( mySeasonalityNumber > 1 ) {
//// // ---------- Add Series Name Varients as options ---------- //
//// generatedAnimeName = baseAnimeName + ' ' + getOrdinalNumber(mySeasonalityNumber) // anime 2nd
//// Logging.log.info "----- Adding Ordinal Seasonality Anime Name to Anime Name List - ${generatedAnimeName}"
//// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
//// if ( mySeasonalityNumber < 10 ) {
//// generatedAnimeName = baseAnimeName + ' ' + getRomanOrdinal(mySeasonalityNumber) // anime II
//// Logging.log.info "----- Adding Seasonality Anime Name to Anime Name List - ${generatedAnimeName}"
//// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
//// }
//// generatedAnimeName = baseAnimeName + ' ' + mySeasonalityNumber // anime 2
//// Logging.log.info "----- Adding Series # Anime Name to Anime Name List - ${generatedAnimeName}"
//// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
//// } else if ( hasRomanSeries ) {
//// generatedAnimeName = baseAnimeName + ' ' + group.seriesNumber // anime I/II/III/IV/V
//// Logging.log.info "----- Adding Series Anime Name to Anime Name List - ${generatedAnimeName}"
//// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
//// }
//// if ( !hasRomanSeries ) {
//// if ( mySeasonalityNumber > 1 ) {
//// // ---------- Add Series Name Varients as options ---------- //
//// generatedAnimeName = baseAnimeName + ' ' + getOrdinalNumber(mySeasonalityNumber) // anime 2nd
//// Logging.log.info "----- Adding Ordinal Seasonality Anime Name to Anime Name List - ${generatedAnimeName}"
//// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
//// if ( mySeasonalityNumber < 10 ) {
//// generatedAnimeName = baseAnimeName + ' ' + getRomanOrdinal(mySeasonalityNumber) // anime II
//// Logging.log.info "----- Adding Seasonality Anime Name to Anime Name List - ${generatedAnimeName}"
//// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
//// }
//// generatedAnimeName = baseAnimeName + ' ' + mySeasonalityNumber // anime 2
//// Logging.log.info "----- Adding Series # Anime Name to Anime Name List - ${generatedAnimeName}"
//// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
//// }
//// } else {
//// generatedAnimeName = baseAnimeName + ' ' + mySeasonalityNumber // anime I/II/III/IV/V
//// Logging.log.info "----- Adding Series Anime Name to Anime Name List - ${generatedAnimeName}"
//// tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
//// switch (mySeasonalityNumber) {
//// case ~/i/:
//// mySeasonalityNumber = 1
//// break
//// case ~/ii/:
//// mySeasonalityNumber = 2
//// break
//// case ~/iii/:
//// mySeasonalityNumber = 3
//// break
//// case ~/iv/:
//// mySeasonalityNumber = 4
//// break
//// case ~/v/:
//// mySeasonalityNumber = 5
//// break
//// case ~/vi/:
//// mySeasonalityNumber = 6
//// break
//// case ~/vii/:
//// mySeasonalityNumber = 7
//// break
//// case ~/viii/:
//// mySeasonalityNumber = 8
//// break
//// case ~/ix/:
//// mySeasonalityNumber = 9
//// break
//// case ~/x/:
//// mySeasonalityNumber = 10
//// break
//// default:
//// mySeasonalityNumber = group.seriesNumber
//// break
//// }
//// Logging.log.info "----- Roman Series - mySeasonalityNumber is now: ${mySeasonalityNumber}"
//// }
// }
//// if ( group.altTitle != null ) {
//// Logging.log.info "----- Alternative Title Detected:[${group.altTitle}]"
//// Logging.log.info "----- Adding Alternative Title to Anime Name List - ${group.altTitle}"
//// tempBaseGeneratedAnimeNames += ["${group.altTitle}"]
//// }
tempBaseGeneratedAnimeNames.each { tempname ->
Logging.log.info "----- BASE: Adding [${tempname}]"
baseGeneratedAnimeNames += tempname
baseGeneratedAnimeNames += ["${returnAniDBRomanization(tempname)}"]
}
// baseGeneratedAnimeNames = baseAnimeNameGenerator()
Logging.log.finest "${groupInfoGenerator(group)}"
Logging.log.info '// END---------- Basename Generation ---------- //'
return [[group], baseGeneratedAnimeNames ]
}
/**
* Generate the base anime names we will be using to build possible Anime Movie names to search for
* Basenames are the base part of the movie name, later we add/modify based on things like movie/the etc.
*
* @param group The LinkedHashMap for this group of files
* @param moviesBasenameGeneratorOverrideJsonFile The Movie Basename Override file
* @return ArrayList of [[group], baseGeneratedAnimeNames ]
*/
ArrayList moviesBasenameGenerator(LinkedHashMap group, ArrayList moviesBasenameGeneratorOverrideJsonFile) {
tempBaseGeneratedAnimeNames = [] as HashSet
baseGeneratedAnimeNames = [] as HashSet
String baseAnimeName
Logging.log.info '// START---------- Basename Generation ---------- //'
Logging.log.finest "${groupInfoGenerator(group)}"
Logging.log.finest "group.class:${group.getClass()}"
Logging.log.info "--- group.anime - ${group.anime}"
Logging.log.info "--- group.altTitle - ${group.altTitle}"
switch(group.anime) {
// Close...
case ~/eiyuu banku koushi-den/:
// tempAnimeName = jwdStringBlender('Eiyuu Banka Koushi-den')
baseAnimeName = group.anime
group.anime = baseAnimeName
break
default:
// Use group.anime for consistency here, so we can do further processing on the baseAnimeName should we want to
// As we would use the "Raw" name on overrides ..
baseAnimeName = group.anime
// tempAnimeName = jwdStringBlender(group.anime)
}
myExceptionMatcher = group.anime =~ /(?i)(fate[ ]?stay night)/
if ( myExceptionMatcher.find() ) {
baseAnimeName = jwdStringBlender("Fate/Stay Night: Heaven`s Feel")
Logging.log.info "//----- AniDB Entry is a multi-part movie - ${baseAnimeName}"
}
// ---------- Checking for Name Variations --- //
// Anime names that don't really match well, so they need help
// Using groupOverrides() to check for a match and return an updated group to us (if needed)
// group.groupOverride will either be null (no override applied)
group = groupOverrides(group, moviesBasenameGeneratorOverrideJsonFile)
if ( group.groupOverride != null ) {
Logging.log.info "--- Group Override Applied: [${group.groupOverride}]"
Logging.log.info "--- Group now: [${group}]"
baseAnimeName = group.anime
}
Logging.log.info "----- baseAnimeName - ${baseAnimeName}"
// group.filebotMovieTitle is a net.filebot.web.Movie object!
tempFileBotName = group.filebotMovieTitle != null ? jwdStringBlender(group.filebotMovieTitle.toString()) : ""
// --- Start of tempBaseGeneratedAnimeNames generation --- //
// if there is an Alternative Title, iterate over that and the baseAnimeName when compiling our list of
// possible base Anime names to add to tempBaseGeneratedAnimeNames
def checkAnimeNameList = group.altTitle != null ? [ baseAnimeName, group.altTitle ] : [ baseAnimeName]
checkAnimeNameList.each { anime ->
Logging.log.info "----- TEMP: Adding anime - [${anime}]"
tempBaseGeneratedAnimeNames += ["${anime}"]
// Unfortunately there is at *least* one anime that officially ends with v3, so I can't just ignore the "version" and the end.
// - https://anidb.net/anime/5078
myRegexMatcher = anime =~ /${matchEndOfLineVersion}/
if ( myRegexMatcher.find() ) {
generatedAnimeName = anime.replaceAll(/${matchEndOfLineVersion}/, '')
tempBaseGeneratedAnimeNames += ["${jwdStringBlender(generatedAnimeName)}"]
Logging.log.info "----- TEMP: End of Line Version - Adding to base Name List - [${generatedAnimeName}] "
}
// myMovieRegexMatcher = group.anime =~ /(?i)(\bmovie\s[\d]{1,3}|\bmovie)\b/
Logging.log.info '--- Checking if we should add variations based on movie keyword'
// We want to check if the anime is (the) movie followed by one to three digits
// We would want to match
// pocket monsters the movie 1
// pocket monsters movie 1
// VOID - /(?i)(\s?(the)?(\smovie\s[\d]{1,3}|\smovie))\b/
Matcher myMovieRegexMatcher = anime =~ /(?i)(\s?(the)?(\smovie\s[\d]{1,3}))\b/
if ( myMovieRegexMatcher.find() ) {
animeTemp = anime.replaceAll(/(?i)(\s?(the)?(\smovie\s[\d]{1,3}))\b/, '').replaceAll(/${stripTrailingSpacesDashRegex}/, '')
Logging.log.info "----- TEMP: Adding 'movie' keyword variation #1 - [${animeTemp}]"
// Removing The, Movie and any 3 digits after movie
// Leaving
// pocket monsters
tempBaseGeneratedAnimeNames += ["${animeTemp}"]
}
// We want to check if the anime has (the) movie amd remove (the) movie from it.
// Leaving everything else alone
// We would want to match
// pocket monsters the movie 1
// pocket monsters movie 1
// gundam g no reconguista movie
// Leaving
// pocket monsters 1
// gundam g no reconguista
// VOID - /(?i)(\s?(the)?(\smovie)\s(?!\d))/
// VOID - /(?i)(\s?(the)?(\smovie(?!\s\d)))/
// VOID - /(?i)(\s?(the)?(\smovie\s[\d]{1,3}))\b/
myMovieRegexMatcher = anime =~ /(?i)(\s?(the)?(\smovie))\b/
if ( myMovieRegexMatcher.find() ) {
animeTemp = anime.replaceAll(/(?i)(\s?(the)?(\smovie))/, '').replaceAll(/${stripTrailingSpacesDashRegex}/, '')
Logging.log.info "----- TEMP: Adding 'movie' keyword variation #2 - [${animeTemp}]"
// Removing The, Movie but leaving the digits
// Leaving
// pocket monsters 1
tempBaseGeneratedAnimeNames += ["${animeTemp}"]
}
myMovieRegexMatcher = anime =~ /(?i)(\s?(the)?(\smovie\s[\d]{1,3}))\b/
if ( myMovieRegexMatcher.find() ) {
animeTemp = anime.replaceAll(/(?i)(?<=movie\s)([\d]{1,3})/, '')
Logging.log.info "----- TEMP: Adding 'movie' keyword variation #3 - [${animeTemp}]" // Removing the digits after movie
tempBaseGeneratedAnimeNames += ["${animeTemp}"]
}
// We want to add a the to any anime that already has the word movie in it and does not already have the in front of it.
// so we would match
// pocket monsters movie
// VOID - (?i)(\s?(?<!the)(\smovie(?!\s\d)))
myMovieRegexMatcher = anime =~ /(?i)(\s?(?<!the)(\smovie\s))/
if ( myMovieRegexMatcher.find() ) {
animeTemp = anime.replaceAll(/(?i)(movie)/, 'the movie').replaceAll(/${stripTrailingSpacesDashRegex}/, '')
Logging.log.info "----- TEMP: Adding 'movie' keyword variation #4 - [${animeTemp}]" // Adding 'the' before 'movie' (but only if it has movie)
tempBaseGeneratedAnimeNames += ["${animeTemp}"]
}
tempBaseGeneratedAnimeNames.each { tempname ->
Logging.log.info "--- BASE: Adding [${tempname}]"
baseGeneratedAnimeNames += tempname
baseGeneratedAnimeNames += ["${returnAniDBRomanization(tempname)}"]
// Taken from groupGeneration
// VOID - (?i)(~\s(.*))$
mySanityRegexMatcher = tempname =~ /(?i)(~(.*))$/
if (mySanityRegexMatcher.find() ) {
//noinspection GroovyAssignabilityCheck
mySanityAltTxt = mySanityRegexMatcher[0][2]
Logging.log.info "--- BASE: Adding possible Alternative Title: [${mySanityAltTxt}] using ~"
baseGeneratedAnimeNames += ["${mySanityAltTxt}"]
baseGeneratedAnimeNames += ["${returnAniDBRomanization(mySanityAltTxt)}"]
animeTemp = tempname.replaceAll(/(?i)(~(.*))$/, '').replaceAll(/(\s){2,20}/, ' ').replaceAll(/([\s-])*$/, '')
Logging.log.info "--- BASE: Adding possible Alternative Title: [${animeTemp}] using ~"
baseGeneratedAnimeNames += ["${animeTemp}"]
baseGeneratedAnimeNames += ["${returnAniDBRomanization(animeTemp)}"]
}
// (?i)(-\s(.*))$
mySanityRegexMatcher = tempname =~ /(?i)(-\s(.*))$/
if (mySanityRegexMatcher.find() ) {
// mySanityAltTxt = mySanityRegexMatcher[0][2]
animeTemp = tempname.replaceAll(/(?i)(-\s(.*))$/, '').replaceAll(/(\s){2,20}/, ' ').replaceAll(/([\s-])*$/, '')
Logging.log.info "--- BASE: Adding Title Text Variation [${animeTemp}] using -"
baseGeneratedAnimeNames += ["${animeTemp}"]
baseGeneratedAnimeNames += ["${returnAniDBRomanization(animeTemp)}"]
}
}
}
Logging.log.info '// END---------- Basename Generation ---------- //'
return [[group], baseGeneratedAnimeNames ]
}
/**
* Generate a printable string that contains only the group values that are set.
*
* @param group The LinkedHashMap for this group of files
* @return String containing the group values that are set (not null)
*/
@SuppressWarnings('GrMethodMayBeStatic')
String groupInfoGenerator ( def group ) {
def groupInfo = "Group: $group.anime, order: $group.order"
if ( group.altTitle != null ) { groupInfo = groupInfo + ", altTitle: $group.altTitle" }
if ( group.animeDetectedName != null ) { groupInfo = groupInfo + ", animeDetectedName: $group.animeDetectedName" }
if ( group.filebotMovieTitle != null ) { groupInfo = groupInfo + ", filebotMovieTitle: $group.filebotMovieTitle" }
if ( group.order == 'airdate') { groupInfo = groupInfo + ", airdateSeasonNumber: $group.airdateSeasonNumber" }
if ( group.isMovieType ) { groupInfo = groupInfo + ", mov: $group.isMovieType" }
if ( group.isFileBotDetectedName ) { groupInfo = groupInfo + ", isFileBotDetectedName: $group.isFileBotDetectedName" }
if ( group.hasSeriesSyntax ) { groupInfo = groupInfo + ", hasSeriesSyntax: $group.hasSeriesSyntax, seriesNumber: $group.seriesNumber" }
if ( group.hasSeasonality ) { groupInfo = groupInfo + ", hasSeasonality: $group.hasSeasonality, seasonNumber: $group.seasonNumber" }
if ( group.hasOrdinalSeasonality ) { groupInfo = groupInfo + ", hasOrdinalSeasonality: $group.hasOrdinalSeasonality, ordinalSeasonNumber: $group.ordinalSeasonNumber" }
if ( group.hasPartialSeasonality ) { groupInfo = groupInfo + ", hasPartialSeasonality: $group.hasPartialSeasonality, partialSeasonNumber: $group.partialSeasonNumber" }
if ( group.isSpecialType ) { groupInfo = groupInfo + ", isSpecialType: $group.isSpecialType" }
if ( group.isSpecialEpisode ) { groupInfo = groupInfo + ", isSpecialEpisode: $group.isSpecialEpisode" }
if ( group.specialType ) { groupInfo = groupInfo + ", specialType: $group.specialType" }
if ( group.yearDateInName != null ) { groupInfo = groupInfo + ", yearDateInName: $group.yearDateInName" }
if ( group.releaseGroup != null ) { groupInfo = groupInfo + ", releaseGroup: $group.releaseGroup" }
return groupInfo
}
/**
* Generate the filebot mapper(s) to use when we perform a rename. The Idea is not use mappers that do not have the necessary data behind them to work (AnimeList.AniDB etc)
*
* @param order The Episode ordering used, absolute or airdate
* @param db The rename database, anidb or tvdb
* @param dbHasAbsoluteNumbering Does it have Absolute Numbering (important really only for tvdb)
* @param hasAnimeListMapping Does it have an AnimeList mapping entry?
* @return renameMapper String [ mapper1, mapper2 ]
*/
@SuppressWarnings('GrMethodMayBeStatic')
String renameMapperGenerator( String order, String db , Boolean dbHasAbsoluteNumbering, Boolean hasAnimeListMapping) {
String renameMapper = ""
switch (db){
case 'tvdb':
if ( order.toLowerCase() == 'airdate' ) { renameMapper = renameMapper + ' episode '}
if ( hasAnimeListMapping ) { renameMapper = renameMapper + ' AnimeList.AniDB '}
if ( order.toLowerCase() == 'absolute' && dbHasAbsoluteNumbering ) { renameMapper = renameMapper + ' order.absolute.episode ' }
if ( order.toLowerCase() == 'absolute' && !dbHasAbsoluteNumbering && !hasAnimeListMapping) { renameMapper = renameMapper + ' order.absolute.episode.derive(e) ' }
break
case 'anidb':
if ( order.toLowerCase() == 'absolute' ) { renameMapper = renameMapper + ' episode '}
if ( hasAnimeListMapping ) { renameMapper = renameMapper + ' AnimeList.TheTVDB '}
break
}
renameMapper = "[" + renameMapper.replaceAll(/^([\s-])*/, '').replaceAll(/([\s-])*$/, '').replaceAll(/(\s){2,20}/, ' ').replaceAll(/\s/, ',') + "]"
return renameMapper
}
/**
* Using a HashSet of Anime Series Names [Generated by seriesBasenameGenerator(), then seriesnameGenerator()] Search for
* "matching" Anime in AniDB using JaroWinklerDistance to measure how close the Anime name is in AniDB to our search term.
*
* @param animeSeriesNames A Hashset of Anime Series Names [Generated by seriesBasenameGenerator(), then seriesnameGenerator()]
* @param aniDBJWDResults A LinkedHashMap of the AniDB Results format we use
* @param animeFoundInAniDB Boolean representing if the Anime was found in AniDB
* @param locale The Locale (aka Locale.English)
* @param aniDBTitleXMLFilename The local filename of the AniDB Offline XML file
* @param aniDBSynonymXMLFilename The local filename of the AniDB Synonym XML File
* @param useFilebotAniDBAliases Boolean: Should we use filebot Aliases for AniDB Series or use Synonyms/Aliases from AniDB XML files (I recommend not using Filebot Aliases)
* @param animeOffLineDatabaseJsonObject Json Object of the Anime Offline Database
* @return LinkedHashMap of [jwdresults: jwdResults, animeFoundInAniDB:animeFoundInAniDB]
*/
@SuppressWarnings('GrReassignedInClosureLocalVar')
LinkedHashMap filebotAnidbJWDSearch(HashSet animeSeriesNames, LinkedHashMap aniDBJWDResults, Boolean animeFoundInAniDB, Locale locale, String aniDBTitleXMLFilename, String aniDBSynonymXMLFilename, Boolean useFilebotAniDBAliases, JsonObject animeOffLineDatabaseJsonObject, LinkedHashMap aniDBCompleteXMLList) {
aniDBTitleXML = new groovy.xml.XmlParser(false, false).parse(aniDBTitleXMLFilename) // XMLParser
aniDBSynonymXML = new groovy.xml.XmlParser(false, false).parse(aniDBSynonymXMLFilename) // XMLParser
LinkedHashMap jwdResults = aniDBJWDResults
Boolean animeANIDBSearchFound = false
String myQueryAniDB = ''
HashSet myOptionsAniDB
BigDecimal jwdcompare = 0
BigDecimal jwdcompare2 = 0
// def myTVDBseriesInfo
// def myTBDBSeriesInfoAliasNames
// def gotAniDBID
ArrayList myAniDBOMTitles = []
JaroWinklerDistance jaroWinklerDistance = new JaroWinklerDistance()
animeSeriesNames.each { series ->
// ---------------------------------- //
// ---------- Search AniDB ---------- //
// ---------------------------------- //
Logging.log.finest "...AniDB"
animeANIDBSearchFound = false
myQueryAniDB = series
myOptionsAniDB = WebServices.AniDB.search(myQueryAniDB, locale) as HashSet
Logging.log.finest "myOptionsAniDB Class : ${myOptionsAniDB.getClass()}"
if (myOptionsAniDB.isEmpty()) {
Logging.log.finest "TV Series not found in AniDB by FileBot: $myQueryAniDB"
// --- We are searching AniDB XML Titles because occationally filebot will not find something, but the Title search will.
// --- And while it's slow as hell, I'm not going to do this all the time ..
myOptionsAniDB += anidbHashTitleSearch(aniDBCompleteXMLList, ["${myQueryAniDB}"] as Set, locale, false, false, false, 3)
if (myOptionsAniDB.isEmpty()) {
Logging.log.finest "TV Series not found in AniDB by AniDB XML Title Search: $myQueryAniDB"
} else {
Logging.log.finest "Our Query Returned from AniDB: ${myOptionsAniDB}"
animeANIDBSearchFound = true
}
} else {
// TODO
// Filebot returns ' while AniDB returns `, so sometimes *effective* duplicates will occur in myOptionsAniDB
// which waste processing time.
Logging.log.finest "Our Query Returned from AniDB: ${myOptionsAniDB}"
animeANIDBSearchFound = true
Logging.log.finest "Filebot Returned ${myOptionsAniDB.size()} Titles:${myOptionsAniDB} :::FOR::: ${myQueryAniDB}"
// --- Return AID as there are a few edge cases where there are in fact multiple titles with the EXACT same words, but
// --- one might be the Official title, while the Other the Main Title, or set as different languages etc.
// --- So returning only the title will mean we *might* not get the actual AID from the query, so return the AID
// --- This also means we don't have to lookup the AID in the next stage as well :)
myOptionsAniDB += anidbHashTitleSearch(aniDBCompleteXMLList, ["${myQueryAniDB}"] as Set, locale, true, false, false, 3)
Logging.log.finest "After XMLTitleSearch ${myOptionsAniDB.size()} Titles:${myOptionsAniDB} :::FOR::: ${myQueryAniDB}"
}
if ( animeANIDBSearchFound ) {
animeFoundInAniDB = true
// ---------- Parse Series Results ---------- //
myOptionsAniDB.each { results ->
Logging.log.finer "Comparing Search Result - ${results}"
// ---------- START - Compile Aliases for Current Result ---------- //
try {
// Logging.log.info "Get Series Information for Aliases"
// Between some wierdness with the Aliases returned and wanting to reduce the API call's to AniDB
// Switch to getting the Series info from AniDB Title XML
// Filebot doesn't return aliases for AniDB when using query by ID
if ( useFilebotAniDBAliases ) {
try {
myTVDBseriesInfo = WebServices.AniDB.getSeriesInfo(results as SearchResult, locale)
} catch (e) {
Logging.log.severe "filebotAnidbJWDSearch() - getSeriesInfo() - Caught error:[${e}]"
myTVDBseriesInfo = []
}
}
if ( useFilebotAniDBAliases && myTVDBseriesInfo != [] ) {
myTBDBSeriesInfoAliasNames = myTVDBseriesInfo.aliasNames // Unfortunately this does NOT always include Synonyms! (but sometimes it does)
} else {
// For some reason beyond my comprehension, Filebot includes aliases for multiple seasons of My Teen Romantic Comedy: SNAFU aka Yahari Ore no Seishun LoveCome wa Machigatte Iru.
// So just zero them out and let the XML take over..
myTBDBSeriesInfoAliasNames = []
}
// Literal Match will not work as Filebot Search returns are *slightly* different (encoding differences?) aka filebot uses ', while AniDB XML is `
// So we need to change it to suit (hopefully it will not end up being a ongoing and expanding issue"
if ( results.toString().isInteger() ) {
gotAniDBID = results as Integer
Logging.log.finest "Got AID: ${gotAniDBID}"
} else {
//noinspection GroovyAssignabilityCheck
gotAniDBID = anidbHashTitleSearch(aniDBCompleteXMLList, ["${results.toString().replaceAll(/'/, '`')}"] as Set, locale, true, false, true, 3)[0] // It returns a linkedHashSet
if ( gotAniDBID <= 0 ) {
//noinspection GroovyAssignabilityCheck
gotAniDBID = anidbHashTitleSearch(aniDBCompleteXMLList, ["${results.toString().replaceAll(/'/, '`')}"] as Set, locale, true, false, true, 3)[0] // It returns a linkedHashSet
Logging.log.finest "Searching Synonyms.xml returned: ${gotAniDBID}"
}
Logging.log.finest "Got AID: ${gotAniDBID} for ${results}"
}
// Logging.log.info "myTVDBseriesInfo properties for ${results}: ${myTVDBseriesInfo.properties}" // No info on # of Episodes
// myTVDBseriesInfo properties for Sword Art Online II: [runtime:null, startDate:2014-07-05, genres:[], certification:null, rating:5.26, id:10376, name:Sword Art Online II, network:null,
// ratingCount:null, type:Anime, class:class net.filebot.web.SeriesInfo, spokenLanguages:[], order:Absolute, status:null, language:en,
// aliasNames:[????????????II, ???????????II, Gun Art Online, Sword Art Online 2, Sword Art Online II: Calibur, Sword Art Online II: Mother's Rosario, Sword Art Online II: Phantom Bullet, GGO, SAO 2, SAO2, SAOII], database:AniDB]
// Logging.log.info "${myTVDBseriesInfo.properties}"
// I am not sure exactly why, but the alias info returned on AniDB series FREQUENTLY does not match what's in the anime-titles.xml