2009-11-21 14:21:46 -05:00
2011-09-22 01:03:46 -04:00
package net.sourceforge.filebot.ui.rename ;
2009-11-21 14:21:46 -05:00
2011-11-07 23:35:56 -05:00
import static java.util.Arrays.* ;
import static java.util.Collections.* ;
2009-11-21 14:21:46 -05:00
import static net.sourceforge.filebot.MediaTypes.* ;
2011-12-26 13:10:53 -05:00
import static net.sourceforge.filebot.media.MediaDetection.* ;
2009-11-22 07:51:23 -05:00
import static net.sourceforge.tuned.FileUtilities.* ;
2009-11-21 20:27:05 -05:00
import static net.sourceforge.tuned.ui.TunedUtilities.* ;
2009-11-21 14:21:46 -05:00
2011-11-28 05:24:46 -05:00
import java.awt.Component ;
2009-11-21 14:21:46 -05:00
import java.io.File ;
import java.util.ArrayList ;
2011-11-14 06:43:22 -05:00
import java.util.Collection ;
2009-11-22 07:51:23 -05:00
import java.util.Collections ;
import java.util.Comparator ;
2010-01-26 14:08:09 -05:00
import java.util.HashMap ;
2011-12-30 10:34:02 -05:00
import java.util.LinkedHashSet ;
2009-11-21 14:21:46 -05:00
import java.util.List ;
2011-08-08 13:37:45 -04:00
import java.util.Locale ;
2010-01-26 14:08:09 -05:00
import java.util.Map ;
2011-12-05 10:38:41 -05:00
import java.util.Map.Entry ;
2011-12-30 10:34:02 -05:00
import java.util.Set ;
2010-01-26 14:08:09 -05:00
import java.util.SortedSet ;
import java.util.TreeSet ;
2009-11-21 20:27:05 -05:00
import java.util.concurrent.Callable ;
import java.util.concurrent.FutureTask ;
import java.util.concurrent.RunnableFuture ;
import javax.swing.Action ;
import javax.swing.SwingUtilities ;
2009-11-21 14:21:46 -05:00
2011-09-21 09:29:21 -04:00
import net.sourceforge.filebot.Analytics ;
2009-11-21 14:21:46 -05:00
import net.sourceforge.filebot.similarity.Match ;
2009-11-21 20:27:05 -05:00
import net.sourceforge.filebot.ui.SelectDialog ;
2011-09-22 08:55:04 -04:00
import net.sourceforge.filebot.web.Movie ;
2009-11-21 14:21:46 -05:00
import net.sourceforge.filebot.web.MovieIdentificationService ;
2011-09-18 15:08:03 -04:00
import net.sourceforge.filebot.web.MoviePart ;
2009-11-21 14:21:46 -05:00
class MovieHashMatcher implements AutoCompleteMatcher {
private final MovieIdentificationService service ;
2011-12-05 10:38:41 -05:00
2009-11-21 14:21:46 -05:00
public MovieHashMatcher ( MovieIdentificationService service ) {
this . service = service ;
}
2011-12-05 10:38:41 -05:00
2009-11-21 14:21:46 -05:00
@Override
2011-11-28 05:24:46 -05:00
public List < Match < File , ? > > match ( final List < File > files , Locale locale , boolean autodetect , Component parent ) throws Exception {
2009-11-22 07:51:23 -05:00
// handle movie files
File [ ] movieFiles = filter ( files , VIDEO_FILES ) . toArray ( new File [ 0 ] ) ;
2011-12-30 10:34:02 -05:00
File [ ] subtitleFiles = filter ( files , SUBTITLE_FILES ) . toArray ( new File [ 0 ] ) ;
Movie [ ] movieByFileHash = null ;
if ( movieFiles . length > 0 ) {
// match movie hashes online
movieByFileHash = service . getMovieDescriptors ( movieFiles , locale ) ;
Analytics . trackEvent ( service . getName ( ) , " HashLookup " , " Movie " , movieByFileHash . length - frequency ( asList ( movieByFileHash ) , null ) ) ; // number of positive hash lookups
} else if ( subtitleFiles . length > 0 ) {
// special handling if there is only subtitle files
movieByFileHash = new Movie [ subtitleFiles . length ] ;
movieFiles = subtitleFiles ;
subtitleFiles = new File [ 0 ] ;
}
2009-11-21 14:21:46 -05:00
2010-01-26 14:08:09 -05:00
// map movies to (possibly multiple) files (in natural order)
2011-09-22 08:55:04 -04:00
Map < Movie , SortedSet < File > > filesByMovie = new HashMap < Movie , SortedSet < File > > ( ) ;
2009-11-21 14:21:46 -05:00
2010-01-26 14:08:09 -05:00
// map all files by movie
for ( int i = 0 ; i < movieFiles . length ; i + + ) {
2011-09-22 08:55:04 -04:00
Movie movie = movieByFileHash [ i ] ;
2010-01-26 14:08:09 -05:00
// unknown hash, try via imdb id from nfo file
2011-08-26 05:46:02 -04:00
if ( movie = = null | | ! autodetect ) {
2011-11-28 05:24:46 -05:00
movie = grabMovieName ( movieFiles [ i ] , locale , autodetect , parent , movie ) ;
2011-09-21 09:29:21 -04:00
if ( movie ! = null ) {
2011-11-10 22:35:50 -05:00
Analytics . trackEvent ( service . getName ( ) , " SearchMovie " , movie . toString ( ) , 1 ) ;
2011-09-21 09:29:21 -04:00
}
2010-01-26 14:08:09 -05:00
}
// check if we managed to lookup the movie descriptor
if ( movie ! = null ) {
// get file list for movie
SortedSet < File > movieParts = filesByMovie . get ( movie ) ;
2009-11-21 20:27:05 -05:00
2010-01-26 14:08:09 -05:00
if ( movieParts = = null ) {
movieParts = new TreeSet < File > ( ) ;
filesByMovie . put ( movie , movieParts ) ;
2009-11-21 20:27:05 -05:00
}
2010-01-26 14:08:09 -05:00
movieParts . add ( movieFiles [ i ] ) ;
}
}
// collect all File/MoviePart matches
List < Match < File , ? > > matches = new ArrayList < Match < File , ? > > ( ) ;
2011-09-22 08:55:04 -04:00
for ( Entry < Movie , SortedSet < File > > entry : filesByMovie . entrySet ( ) ) {
Movie movie = entry . getKey ( ) ;
2010-01-26 14:08:09 -05:00
int partIndex = 0 ;
int partCount = entry . getValue ( ) . size ( ) ;
// add all movie parts
for ( File file : entry . getValue ( ) ) {
2011-09-22 08:55:04 -04:00
Movie part = movie ;
2011-09-18 15:08:03 -04:00
if ( partCount > 1 ) {
part = new MoviePart ( movie , + + partIndex , partCount ) ;
}
2011-09-22 08:55:04 -04:00
matches . add ( new Match < File , Movie > ( file , part ) ) ;
2009-11-21 14:21:46 -05:00
}
}
2009-11-22 07:51:23 -05:00
// handle subtitle files
2011-12-30 10:34:02 -05:00
for ( File subtitle : subtitleFiles ) {
2009-11-22 07:51:23 -05:00
// check if subtitle corresponds to a movie file (same name, different extension)
for ( Match < File , ? > movieMatch : matches ) {
2011-12-30 10:34:02 -05:00
if ( isDerived ( subtitle , movieMatch . getValue ( ) ) ) {
2009-11-22 07:51:23 -05:00
matches . add ( new Match < File , Object > ( subtitle , movieMatch . getCandidate ( ) ) ) ;
// movie match found, we're done
break ;
}
}
}
// restore original order
Collections . sort ( matches , new Comparator < Match < File , ? > > ( ) {
@Override
public int compare ( Match < File , ? > o1 , Match < File , ? > o2 ) {
return files . indexOf ( o1 . getValue ( ) ) - files . indexOf ( o2 . getValue ( ) ) ;
}
} ) ;
2009-11-21 14:21:46 -05:00
return matches ;
}
2009-11-21 20:27:05 -05:00
2011-11-28 05:24:46 -05:00
protected Movie grabMovieName ( File movieFile , Locale locale , boolean autodetect , Component parent , Movie . . . suggestions ) throws Exception {
2011-12-30 10:34:02 -05:00
Set < Movie > options = new LinkedHashSet < Movie > ( ) ;
2009-11-21 20:27:05 -05:00
2011-08-26 05:46:02 -04:00
// add default value if any
2011-09-22 08:55:04 -04:00
for ( Movie it : suggestions ) {
2011-08-26 05:46:02 -04:00
if ( it ! = null ) {
options . add ( it ) ;
}
}
2011-12-30 10:34:02 -05:00
// auto-detect movie from nfo or folder / file name
2011-12-31 05:21:58 -05:00
options . addAll ( detectMovie ( movieFile , null , service , locale , false ) ) ;
2011-08-21 23:43:22 -04:00
// allow manual user input
if ( options . isEmpty ( ) | | ! autodetect ) {
2011-12-30 10:34:02 -05:00
String suggestion = options . isEmpty ( ) ? stripReleaseInfo ( getName ( movieFile ) ) : options . iterator ( ) . next ( ) . getName ( ) ;
2011-11-26 04:50:31 -05:00
String input = null ;
synchronized ( this ) {
2011-12-30 10:34:02 -05:00
input = showInputDialog ( " Enter movie name: " , suggestion , movieFile . getPath ( ) , parent ) ;
2011-11-26 04:50:31 -05:00
}
2011-07-06 22:08:50 -04:00
2011-12-30 10:34:02 -05:00
// we only care about results from manual input from here on out
options . clear ( ) ;
2011-08-21 23:43:22 -04:00
if ( input ! = null ) {
2011-12-30 10:34:02 -05:00
options . addAll ( service . searchMovie ( input , locale ) ) ;
2011-07-06 22:08:50 -04:00
}
}
2011-12-30 10:34:02 -05:00
return options . isEmpty ( ) ? null : selectMovie ( movieFile , options , parent ) ;
2009-11-21 20:27:05 -05:00
}
2011-12-05 10:38:41 -05:00
2011-12-30 10:34:02 -05:00
protected Movie selectMovie ( final File movieFile , final Collection < Movie > options , final Component parent ) throws Exception {
2009-11-21 20:27:05 -05:00
if ( options . size ( ) = = 1 ) {
2011-12-30 10:34:02 -05:00
return options . iterator ( ) . next ( ) ;
2009-11-21 20:27:05 -05:00
}
// show selection dialog on EDT
2011-09-22 08:55:04 -04:00
final RunnableFuture < Movie > showSelectDialog = new FutureTask < Movie > ( new Callable < Movie > ( ) {
2009-11-21 20:27:05 -05:00
@Override
2011-09-22 08:55:04 -04:00
public Movie call ( ) throws Exception {
2009-11-21 20:27:05 -05:00
// multiple results have been found, user must select one
2011-11-28 05:24:46 -05:00
SelectDialog < Movie > selectDialog = new SelectDialog < Movie > ( parent , options ) ;
2009-11-21 20:27:05 -05:00
2011-12-30 10:34:02 -05:00
selectDialog . setTitle ( movieFile . getPath ( ) ) ;
selectDialog . getHeaderLabel ( ) . setText ( String . format ( " Movies matching '%s': " , stripReleaseInfo ( getName ( movieFile ) ) ) ) ;
2009-11-21 20:27:05 -05:00
selectDialog . getCancelAction ( ) . putValue ( Action . NAME , " Ignore " ) ;
// show dialog
selectDialog . setLocation ( getOffsetLocation ( selectDialog . getOwner ( ) ) ) ;
selectDialog . setVisible ( true ) ;
// selected value or null if the dialog was canceled by the user
return selectDialog . getSelectedValue ( ) ;
}
} ) ;
// allow only one select dialog at a time
synchronized ( this ) {
SwingUtilities . invokeAndWait ( showSelectDialog ) ;
}
// selected value or null
return showSelectDialog . get ( ) ;
}
2009-11-21 14:21:46 -05:00
}