001package net.filebot.media;
002
003import static java.util.Comparator.*;
004import static net.filebot.Logging.*;
005import static net.filebot.MediaTypes.*;
006import static net.filebot.media.CachedMediaCharacteristics.*;
007import static net.filebot.media.MediaDetection.*;
008import static net.filebot.media.MediaFileUtilities.*;
009import static net.filebot.util.StringUtilities.*;
010
011import java.io.File;
012import java.util.Comparator;
013import java.util.regex.Pattern;
014
015public class VideoQuality implements Comparator<File> {
016
017        public static final Comparator<File> DESCENDING_ORDER = new VideoQuality(VideoFormat.DEFAULT_GROUPS, releaseInfo.getRepackPattern()).reversed();
018
019        public static boolean isBetter(File f1, File f2) {
020                return DESCENDING_ORDER.compare(f1, f2) < 0;
021        }
022
023        private final VideoFormat format;
024        private final Pattern repack;
025
026        public VideoQuality(VideoFormat format, Pattern repack) {
027                this.format = format;
028                this.repack = repack;
029        }
030
031        private final Comparator<MediaCharacteristics> quality = comparingInt(this::getFormatScore)
032                        .thenComparing(VideoCodec::compare)
033                        .thenComparingInt(this::getRepackScore)
034                        .thenComparingDouble(this::getBitrateScore)
035                        .thenComparingLong(MediaCharacteristics::getFileSize);
036
037        private final Comparator<File> size = comparingLong(File::length);
038
039        @Override
040        public int compare(File f1, File f2) {
041                // use primary video file size for media characteristics (i.e. make companion files like subtitles follow the corresponding primary video file)
042                File vf1 = findPrimaryFile(f1, VIDEO_FILES).orElse(f1);
043                File vf2 = findPrimaryFile(f2, VIDEO_FILES).orElse(f2);
044
045                MediaCharacteristics mi1 = getMediaCharacteristics(vf1).orElse(null);
046                MediaCharacteristics mi2 = getMediaCharacteristics(vf2).orElse(null);
047
048                // compare media characteristics if possible
049                if (mi1 != null && mi2 != null) {
050                        return quality.compare(mi1, mi2);
051                }
052
053                // compare file size if necessary
054                return size.compare(vf1, vf2);
055        }
056
057        private int getRepackScore(MediaCharacteristics mi) {
058                return find(mi.getFileName(), repack) ? 1 : 0;
059        }
060
061        private int getFormatScore(MediaCharacteristics mi) {
062                Integer w = mi.getWidth();
063                Integer h = mi.getHeight();
064
065                if (w != null && h != null) {
066                        try {
067                                return format.guessFormat(w, h);
068                        } catch (Exception e) {
069                                debug.warning(cause(e));
070                        }
071                }
072
073                // invalid media file
074                return 0;
075        }
076
077        private double getBitrateScore(MediaCharacteristics mi) {
078                // use primary video file when checking video resolution of subtitle files or disk folders
079                Integer w = mi.getWidth();
080                Integer h = mi.getHeight();
081                Double br = mi.getBitRate();
082
083                if (w != null && h != null && br != null) {
084                        return w * h * br;
085                }
086
087                // invalid media file
088                return 0;
089        }
090
091        public enum VideoCodec {
092
093                MPEG, AVC, HEVC;
094
095                public static VideoCodec get(MediaCharacteristics mi) {
096                        String vc = mi.getVideoCodec();
097
098                        if (vc != null) {
099                                switch (vc) {
100                                        case "HEVC":
101                                                return HEVC;
102                                        case "AVC":
103                                                return AVC;
104                                        case "MPEG-4 Visual":
105                                                return MPEG;
106                                }
107                        }
108
109                        // print missing codec name
110                        throw new IllegalArgumentException("Unknown Video Codec: " + vc);
111                }
112
113                public static int compare(MediaCharacteristics mi1, MediaCharacteristics mi2) {
114                        try {
115                                VideoCodec vc1 = VideoCodec.get(mi1);
116                                VideoCodec vc2 = VideoCodec.get(mi2);
117                                return vc1.compareTo(vc2);
118                        } catch (Exception e) {
119                                debug.warning(cause(e));
120                        }
121
122                        // invalid media file
123                        return 0;
124                }
125
126        }
127
128}