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        @Override
038        public int compare(File f1, File f2) {
039                // use primary video file size for media characteristics (i.e. make companion files like subtitles follow the corresponding primary video file)
040                File vf1 = findPrimaryFile(f1, VIDEO_FILES).orElse(f1);
041                File vf2 = findPrimaryFile(f2, VIDEO_FILES).orElse(f2);
042
043                MediaCharacteristics mi1 = getMediaCharacteristics(vf1).orElse(null);
044                MediaCharacteristics mi2 = getMediaCharacteristics(vf2).orElse(null);
045
046                // compare media characteristics if possible
047                if (mi1 != null && mi2 != null) {
048                        return quality.compare(mi1, mi2);
049                }
050
051                // compare file size if necessary
052                return FILE_SIZE_ORDER.compare(vf1, vf2);
053        }
054
055        private int getRepackScore(MediaCharacteristics mi) {
056                return find(mi.getFileName(), repack) ? 1 : 0;
057        }
058
059        private int getFormatScore(MediaCharacteristics mi) {
060                Integer w = mi.getWidth();
061                Integer h = mi.getHeight();
062
063                if (w != null && h != null) {
064                        try {
065                                return format.guessFormat(w, h);
066                        } catch (Exception e) {
067                                debug.warning(cause(e));
068                        }
069                }
070
071                // invalid media file
072                return 0;
073        }
074
075        private double getBitrateScore(MediaCharacteristics mi) {
076                // use primary video file when checking video resolution of subtitle files or disk folders
077                Integer w = mi.getWidth();
078                Integer h = mi.getHeight();
079                Double br = mi.getBitRate();
080
081                if (w != null && h != null && br != null) {
082                        return w * h * br;
083                }
084
085                // invalid media file
086                return 0;
087        }
088
089        public enum VideoCodec {
090
091                MPEG, AVC, HEVC, AV1;
092
093                public static VideoCodec get(MediaCharacteristics mi) {
094                        String vc = mi.getVideoCodec();
095
096                        if (vc != null) {
097                                if (vc.contains("AV1"))
098                                        return AV1;
099                                if (vc.contains("HEVC"))
100                                        return HEVC;
101                                if (vc.contains("AVC"))
102                                        return AVC;
103                                if (vc.contains("MPEG-4"))
104                                        return MPEG;
105                        }
106
107                        // print missing codec name
108                        throw new IllegalArgumentException("Unknown Video Codec: " + vc);
109                }
110
111                public static int compare(MediaCharacteristics mi1, MediaCharacteristics mi2) {
112                        try {
113                                VideoCodec vc1 = VideoCodec.get(mi1);
114                                VideoCodec vc2 = VideoCodec.get(mi2);
115                                return vc1.compareTo(vc2);
116                        } catch (Exception e) {
117                                debug.warning(cause(e));
118                        }
119
120                        // invalid media file
121                        return 0;
122                }
123
124        }
125
126}