2323#include <ctype.h>
2424#include <string.h>
2525
26- // (helpers moved below includes to ensure prototypes are visible)
2726
2827#include <libavutil/avutil.h>
2928
@@ -1023,6 +1022,85 @@ static double similarity_ratio(const char *a, const char *b)
10231022 return 1.0 - (double )d / (double )m ;
10241023}
10251024
1025+ // Returns true if a season/episode could be parsed.
1026+ // Recognizes common patterns like S01E02, s1e2, or 1x02 (case-insensitive).
1027+ static bool parse_season_episode (const char * path , int * out_season , int * out_episode )
1028+ {
1029+ if (out_season ) * out_season = -1 ;
1030+ if (out_episode ) * out_episode = -1 ;
1031+ if (!path )
1032+ return false;
1033+
1034+ // Work on a lowercase copy of the basename to simplify matching.
1035+ void * ta = talloc_new (NULL );
1036+ const char * base = mp_basename (path );
1037+ char * s = talloc_strdup (ta , base );
1038+ for (int i = 0 ; s [i ]; i ++ )
1039+ s [i ] = tolower ((unsigned char )s [i ]);
1040+
1041+ int season = -1 , episode = -1 ;
1042+
1043+ // Try SxxEyy pattern
1044+ for (int i = 0 ; s [i ]; i ++ ) {
1045+ if (s [i ] == 's' ) {
1046+ int j = i + 1 ;
1047+ int val_s = 0 , num_s = 0 ;
1048+ while (isdigit ((unsigned char )s [j ])) {
1049+ val_s = val_s * 10 + (s [j ] - '0' );
1050+ num_s ++ ; j ++ ;
1051+ }
1052+ if (num_s > 0 && s [j ] == 'e' ) {
1053+ j ++ ;
1054+ int val_e = 0 , num_e = 0 ;
1055+ while (isdigit ((unsigned char )s [j ])) {
1056+ val_e = val_e * 10 + (s [j ] - '0' );
1057+ num_e ++ ; j ++ ;
1058+ }
1059+ if (num_e > 0 ) {
1060+ season = val_s ;
1061+ episode = val_e ;
1062+ break ;
1063+ }
1064+ }
1065+ }
1066+ }
1067+
1068+ // Fallback: N x M pattern (e.g., 1x02)
1069+ if (season < 0 || episode < 0 ) {
1070+ for (int i = 0 ; s [i ]; i ++ ) {
1071+ if (isdigit ((unsigned char )s [i ])) {
1072+ int j = i ;
1073+ int val_s = 0 , num_s = 0 ;
1074+ while (isdigit ((unsigned char )s [j ])) {
1075+ val_s = val_s * 10 + (s [j ] - '0' );
1076+ num_s ++ ; j ++ ;
1077+ }
1078+ if (num_s > 0 && s [j ] == 'x' ) {
1079+ j ++ ;
1080+ int val_e = 0 , num_e = 0 ;
1081+ while (isdigit ((unsigned char )s [j ])) {
1082+ val_e = val_e * 10 + (s [j ] - '0' );
1083+ num_e ++ ; j ++ ;
1084+ }
1085+ if (num_e > 0 ) {
1086+ season = val_s ;
1087+ episode = val_e ;
1088+ break ;
1089+ }
1090+ }
1091+ }
1092+ }
1093+ }
1094+
1095+ talloc_free (ta );
1096+ if (season >= 0 && episode >= 0 ) {
1097+ if (out_season ) * out_season = season ;
1098+ if (out_episode ) * out_episode = episode ;
1099+ return true;
1100+ }
1101+ return false;
1102+ }
1103+
10261104// to be run on a worker thread, locked (temporarily unlocks core)
10271105static void open_external_files (struct MPContext * mpctx , char * * files ,
10281106 enum stream_type filter )
@@ -1064,7 +1142,10 @@ void autoload_external_files(struct MPContext *mpctx, struct mp_cancel *cancel)
10641142 if (opts -> sub_auto == 3 ) {
10651143 void * selctx = talloc_new (tmp );
10661144 char * movie_norm = normalize_base_name (selctx , mpctx -> filename );
1145+ int mv_season = -1 , mv_episode = -1 ;
1146+ bool mv_has_se = parse_season_episode (mpctx -> filename , & mv_season , & mv_episode );
10671147 double best_score = -1.0 ;
1148+ int best_match_se = 0 ; // 1 if S/E match; prefer this over plain similarity
10681149 for (int i = 0 ; list && list [i ].fname ; i ++ ) {
10691150 struct subfn * e = & list [i ];
10701151 if (e -> type != STREAM_SUB )
@@ -1083,7 +1164,15 @@ void autoload_external_files(struct MPContext *mpctx, struct mp_cancel *cancel)
10831164 continue ;
10841165 char * cand_norm = normalize_base_name (selctx , e -> fname );
10851166 double score = similarity_ratio (movie_norm , cand_norm );
1086- if (score > best_score ) {
1167+ int cand_season = -1 , cand_episode = -1 ;
1168+ int match_se = 0 ;
1169+ if (mv_has_se && parse_season_episode (e -> fname , & cand_season , & cand_episode )) {
1170+ if (mv_season == cand_season && mv_episode == cand_episode )
1171+ match_se = 1 ;
1172+ }
1173+ // Prefer exact S/E match first, then similarity score.
1174+ if (match_se > best_match_se || (match_se == best_match_se && score > best_score )) {
1175+ best_match_se = match_se ;
10871176 best_score = score ;
10881177 best_sub_index = i ;
10891178 }
0 commit comments