2014-04-19 02:30:29 -04:00
package net.filebot.media ;
2011-11-14 06:43:22 -05:00
2013-09-11 13:22:00 -04:00
import static java.lang.Integer.* ;
2016-03-08 07:59:24 -05:00
import static java.nio.charset.StandardCharsets.* ;
2013-09-11 13:22:00 -04:00
import static java.util.Arrays.* ;
import static java.util.Collections.* ;
import static java.util.ResourceBundle.* ;
import static java.util.regex.Pattern.* ;
2014-04-19 02:30:29 -04:00
import static net.filebot.similarity.Normalization.* ;
import static net.filebot.util.FileUtilities.* ;
import static net.filebot.util.StringUtilities.* ;
2011-11-14 06:43:22 -05:00
2016-03-08 07:59:24 -05:00
import java.io.BufferedReader ;
import java.io.ByteArrayInputStream ;
2011-11-14 06:43:22 -05:00
import java.io.File ;
2012-02-10 11:43:09 -05:00
import java.io.FileFilter ;
2011-11-14 06:43:22 -05:00
import java.io.IOException ;
2016-03-08 07:59:24 -05:00
import java.io.InputStreamReader ;
import java.net.URL ;
2012-02-15 01:12:09 -05:00
import java.text.Collator ;
import java.text.Normalizer ;
import java.text.Normalizer.Form ;
2016-03-08 07:59:24 -05:00
import java.time.Duration ;
2011-11-14 06:43:22 -05:00
import java.util.ArrayList ;
2012-02-15 01:12:09 -05:00
import java.util.Collection ;
import java.util.Comparator ;
2012-07-26 04:45:15 -04:00
import java.util.HashMap ;
2012-02-15 01:12:09 -05:00
import java.util.HashSet ;
2012-10-09 11:04:14 -04:00
import java.util.LinkedHashMap ;
2011-11-14 06:43:22 -05:00
import java.util.List ;
2011-12-30 10:34:02 -05:00
import java.util.Locale ;
2012-01-02 11:59:37 -05:00
import java.util.Map ;
2016-03-08 07:59:24 -05:00
import java.util.Objects ;
2012-02-15 01:12:09 -05:00
import java.util.Set ;
2012-01-02 11:59:37 -05:00
import java.util.TreeMap ;
2016-03-08 07:59:24 -05:00
import java.util.function.Function ;
import java.util.function.IntFunction ;
2011-11-14 06:43:22 -05:00
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
2016-03-08 07:59:24 -05:00
import net.filebot.Cache ;
import net.filebot.CacheType ;
import net.filebot.Resource ;
2014-04-19 02:30:29 -04:00
import net.filebot.util.FileUtilities.RegexFileFilter ;
import net.filebot.web.AnidbSearchResult ;
import net.filebot.web.Movie ;
2015-05-11 07:42:59 -04:00
import net.filebot.web.SubtitleSearchResult ;
2014-04-19 02:30:29 -04:00
import net.filebot.web.TheTVDBSearchResult ;
2011-11-14 06:43:22 -05:00
2013-08-10 03:56:11 -04:00
import org.tukaani.xz.XZInputStream ;
2011-11-14 06:43:22 -05:00
public class ReleaseInfo {
2013-08-10 03:56:11 -04:00
2016-01-28 11:18:01 -05:00
private String [ ] videoSources ;
private Pattern videoSourcePattern ;
2014-06-29 07:04:04 -04:00
public String getVideoSource ( String . . . input ) {
2016-01-28 11:18:01 -05:00
if ( videoSources = = null | | videoSourcePattern = = null ) {
videoSources = PIPE . split ( getProperty ( " pattern.video.source " ) ) ;
videoSourcePattern = getVideoSourcePattern ( ) ;
}
2011-11-14 06:43:22 -05:00
// check parent and itself for group names
2016-01-28 11:18:01 -05:00
return matchLast ( videoSourcePattern , videoSources , input ) ;
2014-06-29 07:04:04 -04:00
}
2016-01-28 11:18:01 -05:00
private Pattern videoTagPattern ;
2014-06-29 07:04:04 -04:00
public List < String > getVideoTags ( String . . . input ) {
2016-01-28 11:18:01 -05:00
if ( videoTagPattern = = null ) {
videoTagPattern = getVideoTagPattern ( ) ;
}
2014-06-29 07:04:04 -04:00
List < String > tags = new ArrayList < String > ( ) ;
for ( String s : input ) {
if ( s = = null )
continue ;
2016-01-28 11:18:01 -05:00
Matcher m = videoTagPattern . matcher ( s ) ;
2014-06-29 07:04:04 -04:00
while ( m . find ( ) ) {
tags . add ( m . group ( ) ) ;
}
}
return tags ;
2011-11-14 06:43:22 -05:00
}
2013-08-10 03:56:11 -04:00
2015-11-30 03:51:42 -05:00
public String getStereoscopic3D ( String . . . input ) {
Pattern pattern = getStereoscopic3DPattern ( ) ;
for ( String s : input ) {
Matcher m = pattern . matcher ( s ) ;
if ( m . find ( ) ) {
return m . group ( ) ;
}
}
return null ;
}
2016-03-08 07:59:24 -05:00
public String getReleaseGroup ( String . . . strings ) throws Exception {
2012-06-27 22:36:32 -04:00
// check file and folder for release group names
2016-03-08 07:59:24 -05:00
String [ ] groups = releaseGroup . get ( ) ;
2013-08-10 03:56:11 -04:00
2012-06-27 22:36:32 -04:00
// try case-sensitive match
2012-10-24 07:57:36 -04:00
String match = matchLast ( getReleaseGroupPattern ( true ) , groups , strings ) ;
2013-08-10 03:56:11 -04:00
2012-06-27 22:36:32 -04:00
// try case-insensitive match as fallback
if ( match = = null ) {
2012-10-24 07:57:36 -04:00
match = matchLast ( getReleaseGroupPattern ( false ) , groups , strings ) ;
2012-06-27 22:36:32 -04:00
}
2013-08-10 03:56:11 -04:00
2012-06-27 22:36:32 -04:00
return match ;
2011-11-14 06:43:22 -05:00
}
2013-08-10 03:56:11 -04:00
2016-01-28 11:18:01 -05:00
private Map < String , Locale > languages ;
private Pattern languageSuffix ;
2012-01-02 11:59:37 -05:00
public Locale getLanguageSuffix ( String name ) {
// match locale identifier and lookup Locale object
2016-01-28 11:18:01 -05:00
if ( languages = = null | | languageSuffix = = null ) {
languages = getLanguageMap ( Locale . ENGLISH , Locale . getDefault ( ) ) ;
languageSuffix = getLanguageSuffixPattern ( languages . keySet ( ) , false ) ;
}
2013-08-10 03:56:11 -04:00
2016-01-28 11:18:01 -05:00
String lang = matchLast ( languageSuffix , null , name ) ;
2012-01-02 11:59:37 -05:00
if ( lang = = null )
return null ;
2013-08-10 03:56:11 -04:00
2012-02-15 01:12:09 -05:00
return languages . get ( lang ) ;
2012-01-02 11:59:37 -05:00
}
2013-08-10 03:56:11 -04:00
2012-01-01 22:48:24 -05:00
protected String matchLast ( Pattern pattern , String [ ] standardValues , CharSequence . . . sequence ) {
2011-11-14 06:43:22 -05:00
String lastMatch = null ;
2013-08-10 03:56:11 -04:00
2012-01-01 22:48:24 -05:00
// match last occurrence
2011-11-14 06:43:22 -05:00
for ( CharSequence name : sequence ) {
if ( name = = null )
continue ;
2013-08-10 03:56:11 -04:00
2011-11-14 06:43:22 -05:00
Matcher matcher = pattern . matcher ( name ) ;
while ( matcher . find ( ) ) {
lastMatch = matcher . group ( ) ;
}
}
2013-08-10 03:56:11 -04:00
2012-01-01 22:48:24 -05:00
// prefer standard value over matched value
2012-01-02 11:59:37 -05:00
if ( lastMatch ! = null & & standardValues ! = null ) {
2012-01-01 22:48:24 -05:00
for ( String standard : standardValues ) {
if ( standard . equalsIgnoreCase ( lastMatch ) ) {
return standard ;
}
}
}
2013-08-10 03:56:11 -04:00
2011-11-14 06:43:22 -05:00
return lastMatch ;
}
2013-08-10 03:56:11 -04:00
2012-07-24 16:01:48 -04:00
// cached patterns
2012-07-26 04:45:15 -04:00
private final Map < Boolean , Pattern [ ] > stopwords = new HashMap < Boolean , Pattern [ ] > ( 2 ) ;
private final Map < Boolean , Pattern [ ] > blacklist = new HashMap < Boolean , Pattern [ ] > ( 2 ) ;
2013-08-10 03:56:11 -04:00
2016-03-08 07:59:24 -05:00
public List < String > cleanRelease ( Collection < String > items , boolean strict ) throws Exception {
2012-07-24 16:01:48 -04:00
Pattern [ ] stopwords ;
Pattern [ ] blacklist ;
2013-08-10 03:56:11 -04:00
2012-07-24 16:01:48 -04:00
// initialize cached patterns
2012-07-26 04:45:15 -04:00
synchronized ( this . stopwords ) {
stopwords = this . stopwords . get ( strict ) ;
blacklist = this . blacklist . get ( strict ) ;
2013-08-10 03:56:11 -04:00
2012-07-24 16:01:48 -04:00
if ( stopwords = = null | | blacklist = = null ) {
Set < String > languages = getLanguageMap ( Locale . ENGLISH , Locale . getDefault ( ) ) . keySet ( ) ;
Pattern clutterBracket = getClutterBracketPattern ( strict ) ;
Pattern releaseGroup = getReleaseGroupPattern ( strict ) ;
2012-11-22 11:45:40 -05:00
Pattern languageSuffix = getLanguageSuffixPattern ( languages , strict ) ;
2012-07-24 16:01:48 -04:00
Pattern languageTag = getLanguageTagPattern ( languages ) ;
Pattern videoSource = getVideoSourcePattern ( ) ;
2014-07-14 07:19:41 -04:00
Pattern videoTags = getVideoTagPattern ( ) ;
2013-11-27 13:49:15 -05:00
Pattern videoFormat = getVideoFormatPattern ( strict ) ;
2016-01-28 11:18:01 -05:00
Pattern stereoscopic3d = getStereoscopic3DPattern ( ) ;
2012-07-24 16:01:48 -04:00
Pattern resolution = getResolutionPattern ( ) ;
Pattern queryBlacklist = getBlacklistPattern ( ) ;
2013-08-10 03:56:11 -04:00
2016-01-29 11:06:28 -05:00
stopwords = new Pattern [ ] { languageTag , videoSource , videoTags , videoFormat , resolution , stereoscopic3d , languageSuffix } ;
blacklist = new Pattern [ ] { queryBlacklist , languageTag , clutterBracket , releaseGroup , videoSource , videoTags , videoFormat , resolution , stereoscopic3d , languageSuffix } ;
2013-08-10 03:56:11 -04:00
2012-07-24 16:01:48 -04:00
// cache compiled patterns for common usage
2012-07-26 04:45:15 -04:00
this . stopwords . put ( strict , stopwords ) ;
this . blacklist . put ( strict , blacklist ) ;
2012-07-24 16:01:48 -04:00
}
}
2013-08-10 03:56:11 -04:00
2012-02-23 13:48:35 -05:00
List < String > output = new ArrayList < String > ( items . size ( ) ) ;
2011-11-26 04:50:31 -05:00
for ( String it : items ) {
2012-07-13 07:15:14 -04:00
it = strict ? clean ( it , stopwords ) : substringBefore ( it , stopwords ) ;
2013-04-01 06:17:20 -04:00
it = normalizePunctuation ( clean ( it , blacklist ) ) ;
2013-08-10 03:56:11 -04:00
2012-02-23 13:48:35 -05:00
// ignore empty values
if ( it . length ( ) > 0 ) {
output . add ( it ) ;
2012-01-01 22:48:24 -05:00
}
2011-11-26 04:50:31 -05:00
}
2013-08-10 03:56:11 -04:00
2012-02-23 13:48:35 -05:00
return output ;
2011-11-26 04:50:31 -05:00
}
2013-08-10 03:56:11 -04:00
2011-11-26 04:50:31 -05:00
public String clean ( String item , Pattern . . . blacklisted ) {
for ( Pattern it : blacklisted ) {
item = it . matcher ( item ) . replaceAll ( " " ) ;
2011-11-14 06:43:22 -05:00
}
2013-04-01 06:17:20 -04:00
return item ;
2011-11-14 06:43:22 -05:00
}
2013-08-10 03:56:11 -04:00
2012-02-23 13:48:35 -05:00
public String substringBefore ( String item , Pattern . . . stopwords ) {
for ( Pattern it : stopwords ) {
Matcher matcher = it . matcher ( item ) ;
if ( matcher . find ( ) ) {
2012-07-04 06:20:52 -04:00
String substring = item . substring ( 0 , matcher . start ( ) ) ; // use substring before the matched stopword
if ( normalizePunctuation ( substring ) . length ( ) > = 3 ) {
item = substring ; // make sure that the substring has enough data
}
2012-02-23 13:48:35 -05:00
}
}
return item ;
}
2013-08-10 03:56:11 -04:00
2013-11-16 00:37:41 -05:00
// cached patterns
2013-11-16 07:10:23 -05:00
private Set < File > volumeRoots ;
2013-11-16 00:37:41 -05:00
private Pattern structureRootFolderPattern ;
2013-11-16 07:10:23 -05:00
public Set < File > getVolumeRoots ( ) {
if ( volumeRoots = = null ) {
Set < File > volumes = new HashSet < File > ( ) ;
// user root folder
volumes . add ( new File ( System . getProperty ( " user.home " ) ) ) ;
// Windows / Linux / Mac system roots
2014-07-28 06:00:27 -04:00
volumes . addAll ( getFileSystemRoots ( ) ) ;
2013-11-16 07:10:23 -05:00
if ( File . separator . equals ( " / " ) ) {
2014-01-06 13:15:37 -05:00
// Linux and Mac system root folders
2014-07-28 06:00:27 -04:00
for ( File root : getFileSystemRoots ( ) ) {
volumes . addAll ( getChildren ( root , FOLDERS ) ) ;
2013-11-16 07:10:23 -05:00
}
2014-01-06 13:15:37 -05:00
// user-specific media roots
2014-08-10 10:47:47 -04:00
for ( File mediaRoot : getMediaRoots ( ) ) {
volumes . addAll ( getChildren ( mediaRoot , FOLDERS ) ) ;
volumes . add ( mediaRoot ) ;
// add additional user roots if user.home is not set properly or listFiles doesn't work
String username = System . getProperty ( " user.name " ) ;
if ( username ! = null & & username . length ( ) > 0 ) {
volumes . add ( new File ( mediaRoot , username ) ) ;
2013-11-16 07:10:23 -05:00
}
}
}
2014-01-06 13:15:37 -05:00
2013-12-13 23:11:44 -05:00
volumeRoots = unmodifiableSet ( volumes ) ;
2013-11-16 07:10:23 -05:00
}
return volumeRoots ;
}
2016-03-08 07:59:24 -05:00
public Pattern getStructureRootPattern ( ) throws Exception {
2013-11-16 00:37:41 -05:00
if ( structureRootFolderPattern = = null ) {
List < String > folders = new ArrayList < String > ( ) ;
2016-03-08 07:59:24 -05:00
for ( String it : queryBlacklist . get ( ) ) {
2014-01-11 04:04:49 -05:00
if ( it . startsWith ( " ^ " ) & & it . endsWith ( " $ " ) ) {
2013-11-16 00:37:41 -05:00
folders . add ( it ) ;
}
}
2015-05-20 03:35:45 -04:00
structureRootFolderPattern = compile ( or ( folders . toArray ( ) ) , CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS ) ;
2013-11-16 00:37:41 -05:00
}
return structureRootFolderPattern ;
}
2012-02-23 13:48:35 -05:00
public Pattern getLanguageTagPattern ( Collection < String > languages ) {
2012-02-15 01:12:09 -05:00
// [en]
2015-05-20 03:35:45 -04:00
return compile ( " (?<=[- \\ [{(]) " + or ( quoteAll ( languages ) ) + " (?= \\ p{Punct}) " , CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS ) ;
2012-01-02 11:59:37 -05:00
}
2013-08-10 03:56:11 -04:00
2012-11-22 11:45:40 -05:00
public Pattern getLanguageSuffixPattern ( Collection < String > languages , boolean strict ) {
2014-11-11 13:26:17 -05:00
// e.g. ".en.srt" or ".en.forced.srt"
2015-08-04 19:40:39 -04:00
return compile ( " (?<=[._-]) " + or ( quoteAll ( languages ) ) + " (?=([._-]( " + getProperty ( " pattern.subtitle.tags " ) + " ))?$) " , strict ? 0 : CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS ) ;
2011-12-30 10:34:02 -05:00
}
2013-08-10 03:56:11 -04:00
2011-12-30 10:34:02 -05:00
public Pattern getResolutionPattern ( ) {
// match screen resolutions 640x480, 1280x720, etc
return compile ( " (?<! \\ p{Alnum})( \\ d{4}|[6-9] \\ d{2})x( \\ d{4}|[4-9] \\ d{2})(?! \\ p{Alnum}) " ) ;
}
2013-08-10 03:56:11 -04:00
2013-11-27 13:49:15 -05:00
public Pattern getVideoFormatPattern ( boolean strict ) {
2011-11-14 06:43:22 -05:00
// pattern matching any video source name
2014-11-09 02:40:01 -05:00
String pattern = getProperty ( " pattern.video.format " ) ;
2013-11-27 13:49:15 -05:00
return strict ? compile ( " (?<! \\ p{Alnum})( " + pattern + " )(?! \\ p{Alnum}) " , CASE_INSENSITIVE ) : compile ( pattern , CASE_INSENSITIVE ) ;
2011-11-14 06:43:22 -05:00
}
2013-08-10 03:56:11 -04:00
2011-11-14 06:43:22 -05:00
public Pattern getVideoSourcePattern ( ) {
2014-06-29 07:04:04 -04:00
// pattern matching any video source name, like BluRay
2014-11-09 02:40:01 -05:00
String pattern = getProperty ( " pattern.video.source " ) ;
2011-11-14 06:43:22 -05:00
return compile ( " (?<! \\ p{Alnum})( " + pattern + " )(?! \\ p{Alnum}) " , CASE_INSENSITIVE ) ;
2014-06-29 07:04:04 -04:00
}
public Pattern getVideoTagPattern ( ) {
// pattern matching any video tag, like Directors Cut
2014-11-09 02:40:01 -05:00
String pattern = getProperty ( " pattern.video.tags " ) ;
2014-06-29 07:04:04 -04:00
return compile ( " (?<! \\ p{Alnum})( " + pattern + " )(?! \\ p{Alnum}) " , CASE_INSENSITIVE ) ;
2011-11-14 06:43:22 -05:00
}
2013-08-10 03:56:11 -04:00
2015-11-30 03:51:42 -05:00
public Pattern getStereoscopic3DPattern ( ) {
// pattern matching any 3D flags like 3D.HSBS
String pattern = getProperty ( " pattern.video.s3d " ) ;
return compile ( " (?<! \\ p{Alnum})( " + pattern + " )(?! \\ p{Alnum}) " , CASE_INSENSITIVE ) ;
}
2012-06-22 03:47:26 -04:00
public Pattern getClutterBracketPattern ( boolean strict ) {
// match patterns like [Action, Drama] or {ENG-XViD-MP3-DVDRiP} etc
String contentFilter = strict ? " [ \\ p{Space} \\ p{Punct}&&[^ \\ [ \\ ]]] " : " \\ p{Alpha} " ;
return compile ( " (?: \\ [([^ \\ [ \\ ]]+? " + contentFilter + " [^ \\ [ \\ ]]+?) \\ ])|(?: \\ {([^ \\ { \\ }]+? " + contentFilter + " [^ \\ { \\ }]+?) \\ })|(?: \\ (([^ \\ ( \\ )]+? " + contentFilter + " [^ \\ ( \\ )]+?) \\ )) " ) ;
}
2013-08-10 03:56:11 -04:00
2016-03-08 07:59:24 -05:00
public Pattern getReleaseGroupPattern ( boolean strict ) throws Exception {
2011-11-14 06:43:22 -05:00
// pattern matching any release group name enclosed in separators
2016-03-08 07:59:24 -05:00
return compile ( " (?<! \\ p{Alnum}) " + or ( releaseGroup . get ( ) ) + " (?! \\ p{Alnum}|[^ \\ p{Alnum}](19|20) \\ d{2}) " , strict ? 0 : CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS ) ;
2011-11-14 06:43:22 -05:00
}
2013-08-10 03:56:11 -04:00
2016-03-08 07:59:24 -05:00
public Pattern getBlacklistPattern ( ) throws Exception {
2011-12-30 16:42:25 -05:00
// pattern matching any release group name enclosed in separators
2016-03-08 07:59:24 -05:00
return compile ( " (?<! \\ p{Alnum}) " + or ( queryBlacklist . get ( ) ) + " (?! \\ p{Alnum}) " , CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS ) ;
2012-07-26 04:45:15 -04:00
}
2013-08-10 03:56:11 -04:00
2016-03-08 07:59:24 -05:00
public Pattern getExcludePattern ( ) throws Exception {
2012-07-26 04:45:15 -04:00
// pattern matching any release group name enclosed in separators
2016-03-08 07:59:24 -05:00
return compile ( or ( excludeBlacklist . get ( ) ) , CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS ) ;
2012-01-01 22:48:24 -05:00
}
2013-08-10 03:56:11 -04:00
2014-02-19 15:28:00 -05:00
public Pattern getCustomRemovePattern ( Collection < String > terms ) throws IOException {
2015-05-20 03:35:45 -04:00
return compile ( " (?<! \\ p{Alnum}) " + or ( quoteAll ( terms ) ) + " (?! \\ p{Alnum}) " , CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS ) ;
2014-02-19 15:28:00 -05:00
}
2016-03-08 07:59:24 -05:00
public Movie [ ] getMovieList ( ) throws Exception {
return movieIndex . get ( ) ;
2011-12-30 16:42:25 -05:00
}
2013-08-10 03:56:11 -04:00
2016-03-08 07:59:24 -05:00
public TheTVDBSearchResult [ ] getTheTVDBIndex ( ) throws Exception {
return tvdbIndex . get ( ) ;
2013-03-17 10:19:11 -04:00
}
2013-08-10 03:56:11 -04:00
2016-03-08 07:59:24 -05:00
public AnidbSearchResult [ ] getAnidbIndex ( ) throws Exception {
return anidbIndex . get ( ) ;
2012-10-14 07:57:25 -04:00
}
2013-08-10 03:56:11 -04:00
2016-03-08 07:59:24 -05:00
public SubtitleSearchResult [ ] getOpenSubtitlesIndex ( ) throws Exception {
return osdbIndex . get ( ) ;
2015-05-11 05:13:35 -04:00
}
2013-12-13 23:11:44 -05:00
private Map < Pattern , String > seriesDirectMappings ;
2016-03-08 07:59:24 -05:00
public Map < Pattern , String > getSeriesDirectMappings ( ) throws Exception {
2013-12-13 23:11:44 -05:00
if ( seriesDirectMappings = = null ) {
Map < Pattern , String > mappings = new LinkedHashMap < Pattern , String > ( ) ;
for ( String line : seriesDirectMappingsResource . get ( ) ) {
String [ ] tsv = line . split ( " \ t " , 2 ) ;
if ( tsv . length = = 2 ) {
2014-04-27 23:57:35 -04:00
mappings . put ( compile ( " (?<! \\ p{Alnum})( " + tsv [ 0 ] + " )(?! \\ p{Alnum}) " , CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS ) , tsv [ 1 ] ) ;
2013-12-13 23:11:44 -05:00
}
2012-10-09 11:04:14 -04:00
}
2013-12-13 23:11:44 -05:00
seriesDirectMappings = unmodifiableMap ( mappings ) ;
2012-10-09 11:04:14 -04:00
}
2013-12-13 23:11:44 -05:00
return seriesDirectMappings ;
2012-10-09 11:04:14 -04:00
}
2013-08-10 03:56:11 -04:00
2016-01-28 11:18:01 -05:00
private static FolderEntryFilter diskFolderFilter ;
2012-02-10 11:43:09 -05:00
public FileFilter getDiskFolderFilter ( ) {
2016-01-28 11:18:01 -05:00
if ( diskFolderFilter = = null ) {
diskFolderFilter = new FolderEntryFilter ( compile ( getProperty ( " pattern.diskfolder.entry " ) ) ) ;
}
return diskFolderFilter ;
2012-02-10 11:43:09 -05:00
}
2013-08-10 03:56:11 -04:00
2016-01-28 11:18:01 -05:00
private static RegexFileFilter diskFolderEntryFilter ;
2013-09-17 13:32:38 -04:00
public FileFilter getDiskFolderEntryFilter ( ) {
2016-01-28 11:18:01 -05:00
if ( diskFolderEntryFilter = = null ) {
diskFolderEntryFilter = new RegexFileFilter ( compile ( getProperty ( " pattern.diskfolder.entry " ) ) ) ;
}
return diskFolderEntryFilter ;
2013-09-17 13:32:38 -04:00
}
2016-01-28 11:18:01 -05:00
private static ClutterFileFilter clutterFileFilter ;
2016-03-08 07:59:24 -05:00
public FileFilter getClutterFileFilter ( ) throws Exception {
2016-01-28 11:18:01 -05:00
if ( clutterFileFilter = = null ) {
clutterFileFilter = new ClutterFileFilter ( getExcludePattern ( ) , Long . parseLong ( getProperty ( " number.clutter.maxfilesize " ) ) ) ; // only files smaller than 250 MB may be considered clutter
}
return clutterFileFilter ;
2012-06-15 06:45:35 -04:00
}
2013-08-10 03:56:11 -04:00
2014-01-06 13:15:37 -05:00
public List < File > getMediaRoots ( ) {
List < File > roots = new ArrayList < File > ( ) ;
2014-11-09 02:40:01 -05:00
for ( String it : getProperty ( " folder.media.roots " ) . split ( " : " ) ) {
2014-01-06 13:15:37 -05:00
roots . add ( new File ( it ) ) ;
}
return roots ;
}
2011-11-26 10:41:58 -05:00
// fetch release group names online and try to update the data every other day
2016-03-08 07:59:24 -05:00
protected final Resource < String [ ] > releaseGroup = patternResource ( " url.release-groups " ) ;
protected final Resource < String [ ] > queryBlacklist = patternResource ( " url.query-blacklist " ) ;
protected final Resource < String [ ] > excludeBlacklist = patternResource ( " url.exclude-blacklist " ) ;
protected final Resource < String [ ] > seriesDirectMappingsResource = patternResource ( " url.series-mappings " ) ;
protected final Resource < Movie [ ] > movieIndex = tsvResource ( " url.movie-list " , this : : parseMovie , Movie [ ] : : new ) ;
protected final Resource < TheTVDBSearchResult [ ] > tvdbIndex = tsvResource ( " url.thetvdb-index " , this : : parseSeries , TheTVDBSearchResult [ ] : : new ) ;
protected final Resource < AnidbSearchResult [ ] > anidbIndex = tsvResource ( " url.anidb-index " , this : : parseAnime , AnidbSearchResult [ ] : : new ) ;
protected final Resource < SubtitleSearchResult [ ] > osdbIndex = tsvResource ( " url.osdb-index " , this : : parseSubtitle , SubtitleSearchResult [ ] : : new ) ;
private Movie parseMovie ( String [ ] v ) {
int imdbid = parseInt ( v [ 0 ] ) ;
int tmdbid = parseInt ( v [ 1 ] ) ;
int year = parseInt ( v [ 2 ] ) ;
String name = v [ 3 ] ;
String [ ] aliasNames = copyOfRange ( v , 4 , v . length ) ;
return new Movie ( name , aliasNames , year , imdbid > 0 ? imdbid : - 1 , tmdbid > 0 ? tmdbid : - 1 , null ) ;
}
private TheTVDBSearchResult parseSeries ( String [ ] v ) {
int id = parseInt ( v [ 0 ] ) ;
String name = v [ 1 ] ;
String [ ] aliasNames = copyOfRange ( v , 2 , v . length ) ;
return new TheTVDBSearchResult ( name , aliasNames , id ) ;
}
private AnidbSearchResult parseAnime ( String [ ] v ) {
int aid = parseInt ( v [ 0 ] ) ;
String primaryTitle = v [ 1 ] ;
String [ ] aliasNames = copyOfRange ( v , 2 , v . length ) ;
return new AnidbSearchResult ( aid , primaryTitle , aliasNames ) ;
}
private SubtitleSearchResult parseSubtitle ( String [ ] v ) {
String kind = v [ 0 ] ;
int score = parseInt ( v [ 1 ] ) ;
int imdbId = parseInt ( v [ 2 ] ) ;
int year = parseInt ( v [ 3 ] ) ;
String name = v [ 4 ] ;
String [ ] aliasNames = copyOfRange ( v , 5 , v . length ) ;
return new SubtitleSearchResult ( name , aliasNames , year , imdbId , - 1 , Locale . ENGLISH , SubtitleSearchResult . Kind . forName ( kind ) , score ) ;
}
protected Resource < String [ ] > patternResource ( String name ) {
return resource ( name , Cache . ONE_WEEK , s - > {
return s . length ( ) > 0 ? s : null ;
} , String [ ] : : new ) ;
}
protected < T > Resource < T [ ] > tsvResource ( String name , Function < String [ ] , T > parse , IntFunction < T [ ] > generator ) {
return resource ( name , Cache . ONE_WEEK , s - > {
String [ ] v = s . split ( " \ t " ) ;
return v . length > 0 ? parse . apply ( v ) : null ;
} , generator ) ;
}
protected < T > Resource < T [ ] > resource ( String name , Duration expirationTime , Function < String , T > parse , IntFunction < T [ ] > generator ) {
return ( ) - > {
Cache cache = Cache . getCache ( " data " , CacheType . Persistent ) ;
byte [ ] bytes = cache . bytes ( name , n - > new URL ( getProperty ( n ) ) ) . expire ( expirationTime ) . get ( ) ;
// all data file are xz compressed
try ( BufferedReader text = new BufferedReader ( new InputStreamReader ( new XZInputStream ( new ByteArrayInputStream ( bytes ) ) , UTF_8 ) ) ) {
return text . lines ( ) . map ( parse ) . filter ( Objects : : nonNull ) . toArray ( generator ) ;
}
} ;
}
protected String getProperty ( String name ) {
// override resource locations via Java System properties
return System . getProperty ( name , getBundle ( ReleaseInfo . class . getName ( ) ) . getString ( name ) ) ;
2014-11-09 02:40:01 -05:00
}
2013-08-10 03:56:11 -04:00
2016-03-08 08:06:07 -05:00
public static class FolderEntryFilter implements FileFilter {
2013-08-10 03:56:11 -04:00
2012-02-10 11:43:09 -05:00
private final Pattern entryPattern ;
2013-08-10 03:56:11 -04:00
2012-02-10 11:43:09 -05:00
public FolderEntryFilter ( Pattern entryPattern ) {
this . entryPattern = entryPattern ;
}
2013-08-10 03:56:11 -04:00
2012-02-10 11:43:09 -05:00
@Override
public boolean accept ( File dir ) {
if ( dir . isDirectory ( ) ) {
2014-10-21 06:26:42 -04:00
for ( File f : getChildren ( dir ) ) {
if ( entryPattern . matcher ( f . getName ( ) ) . matches ( ) ) {
2012-02-10 11:43:09 -05:00
return true ;
}
}
}
return false ;
}
}
2013-08-10 03:56:11 -04:00
2012-06-15 06:45:35 -04:00
public static class FileFolderNameFilter implements FileFilter {
2013-08-10 03:56:11 -04:00
2012-06-15 06:45:35 -04:00
private final Pattern namePattern ;
2013-08-10 03:56:11 -04:00
2012-06-15 06:45:35 -04:00
public FileFolderNameFilter ( Pattern namePattern ) {
this . namePattern = namePattern ;
}
2013-08-10 03:56:11 -04:00
2012-06-15 06:45:35 -04:00
@Override
public boolean accept ( File file ) {
return ( namePattern . matcher ( file . getName ( ) ) . find ( ) | | ( file . isFile ( ) & & namePattern . matcher ( file . getParentFile ( ) . getName ( ) ) . find ( ) ) ) ;
}
}
2013-08-10 03:56:11 -04:00
2013-03-28 05:04:35 -04:00
public static class ClutterFileFilter extends FileFolderNameFilter {
2013-08-10 03:56:11 -04:00
2013-03-28 05:04:35 -04:00
private long maxFileSize ;
2013-08-10 03:56:11 -04:00
2013-03-28 05:04:35 -04:00
public ClutterFileFilter ( Pattern namePattern , long maxFileSize ) {
super ( namePattern ) ;
this . maxFileSize = maxFileSize ;
}
2013-08-10 03:56:11 -04:00
2013-03-28 05:04:35 -04:00
@Override
public boolean accept ( File file ) {
return super . accept ( file ) & & file . isFile ( ) & & file . length ( ) < maxFileSize ;
}
}
2013-08-10 03:56:11 -04:00
2015-05-20 03:35:45 -04:00
private String or ( Object [ ] terms ) {
return joinSorted ( terms , " | " , reverseOrder ( ) , " ( " , " ) " ) ; // non-capturing group that matches the longest occurrence
}
private String [ ] quoteAll ( Collection < String > values ) {
return values . stream ( ) . map ( ( s ) - > Pattern . quote ( s ) ) . toArray ( String [ ] : : new ) ;
2012-02-15 01:12:09 -05:00
}
2013-08-10 03:56:11 -04:00
2014-12-03 03:45:33 -05:00
public Map < String , Locale > getLanguageMap ( Locale . . . supportedDisplayLocale ) {
2012-02-15 01:12:09 -05:00
// use maximum strength collator by default
2016-02-10 09:31:53 -05:00
Collator collator = Collator . getInstance ( Locale . ENGLISH ) ;
2012-02-15 01:12:09 -05:00
collator . setDecomposition ( Collator . FULL_DECOMPOSITION ) ;
collator . setStrength ( Collator . PRIMARY ) ;
2013-08-10 03:56:11 -04:00
2016-02-10 09:31:53 -05:00
Comparator < ? super String > order = collator ;
2013-12-13 21:22:31 -05:00
Map < String , Locale > languageMap = new TreeMap < String , Locale > ( order ) ;
2013-08-10 03:56:11 -04:00
2012-02-15 01:12:09 -05:00
for ( String code : Locale . getISOLanguages ( ) ) {
2013-12-13 21:22:31 -05:00
Locale locale = new Locale ( code ) ; // force ISO3 language as default toString() value
Locale iso3locale = new Locale ( locale . getISO3Language ( ) ) ;
languageMap . put ( locale . getLanguage ( ) , iso3locale ) ;
languageMap . put ( locale . getISO3Language ( ) , iso3locale ) ;
2013-08-10 03:56:11 -04:00
2012-02-15 01:12:09 -05:00
// map display language names for given locales
2012-07-24 16:01:48 -04:00
for ( Locale language : new HashSet < Locale > ( asList ( supportedDisplayLocale ) ) ) {
2012-02-15 01:12:09 -05:00
// make sure language name is properly normalized so accents and whatever don't break the regex pattern syntax
String languageName = Normalizer . normalize ( locale . getDisplayLanguage ( language ) , Form . NFKD ) ;
2013-12-13 21:22:31 -05:00
languageMap . put ( languageName . toLowerCase ( ) , iso3locale ) ;
2012-02-15 01:12:09 -05:00
}
}
2013-08-10 03:56:11 -04:00
2013-12-13 21:22:31 -05:00
// unofficial language for pb/pob for Portuguese (Brazil)
Locale brazil = new Locale ( " pob " ) ;
2014-12-03 03:45:33 -05:00
languageMap . put ( " brazilian " , brazil ) ;
2013-12-13 21:22:31 -05:00
languageMap . put ( " pb " , brazil ) ;
languageMap . put ( " pob " , brazil ) ;
2014-06-11 11:03:58 -04:00
// missing ISO 639-2 (B/T) locales (see https://github.com/TakahikoKawasaki/nv-i18n/blob/master/src/main/java/com/neovisionaries/i18n/LanguageAlpha3Code.java)
languageMap . put ( " tib " , new Locale ( " bod " ) ) ;
languageMap . put ( " cze " , new Locale ( " ces " ) ) ;
languageMap . put ( " wel " , new Locale ( " cym " ) ) ;
languageMap . put ( " ger " , new Locale ( " deu " ) ) ;
languageMap . put ( " gre " , new Locale ( " ell " ) ) ;
languageMap . put ( " baq " , new Locale ( " eus " ) ) ;
languageMap . put ( " per " , new Locale ( " fas " ) ) ;
languageMap . put ( " fre " , new Locale ( " fra " ) ) ;
languageMap . put ( " arm " , new Locale ( " hye " ) ) ;
languageMap . put ( " ice " , new Locale ( " isl " ) ) ;
languageMap . put ( " geo " , new Locale ( " kat " ) ) ;
languageMap . put ( " mac " , new Locale ( " mkd " ) ) ;
languageMap . put ( " mao " , new Locale ( " mri " ) ) ;
languageMap . put ( " may " , new Locale ( " msa " ) ) ;
languageMap . put ( " bur " , new Locale ( " mya " ) ) ;
languageMap . put ( " dut " , new Locale ( " nld " ) ) ;
languageMap . put ( " rum " , new Locale ( " ron " ) ) ;
languageMap . put ( " slo " , new Locale ( " slk " ) ) ;
languageMap . put ( " alb " , new Locale ( " sqi " ) ) ;
languageMap . put ( " chi " , new Locale ( " zho " ) ) ;
2014-05-09 16:27:18 -04:00
2012-02-15 01:12:09 -05:00
// remove illegal tokens
languageMap . remove ( " " ) ;
2012-06-22 03:47:26 -04:00
languageMap . remove ( " II " ) ;
languageMap . remove ( " III " ) ;
2016-02-25 07:16:18 -05:00
languageMap . remove ( " hi " ) ; // hi => typically used for hearing-impaired subtitles, NOT hindi language
2013-08-10 03:56:11 -04:00
2016-02-25 07:16:18 -05:00
return unmodifiableMap ( languageMap ) ;
2012-02-15 01:12:09 -05:00
}
2014-11-09 02:40:01 -05:00
2011-11-14 06:43:22 -05:00
}