subtitles.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:13k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * subtitles.c
  3.  *****************************************************************************
  4.  * Copyright (C) 2003-2006 the VideoLAN team
  5.  * $Id: c721a7958ef3cdbd0f695d973620c3702638fcb3 $
  6.  *
  7.  * Authors: Derk-Jan Hartman <hartman at videolan.org>
  8.  * This is adapted code from the GPL'ed MPlayer (http://mplayerhq.hu)
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  *****************************************************************************/
  24. /**
  25.  *  file
  26.  *  This file contains functions to dectect subtitle files.
  27.  */
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #include <vlc_input.h>
  33. #include <vlc_charset.h>
  34. #ifdef HAVE_DIRENT_H
  35. #   include <dirent.h>
  36. #endif
  37. #include <limits.h>
  38. #ifdef HAVE_UNISTD_H
  39. #   include <unistd.h>
  40. #endif
  41. #include <sys/stat.h>
  42. #include <ctype.h>
  43. #include "input_internal.h"
  44. /**
  45.  * We are not going to autodetect more subtitle files than this.
  46.  */
  47. #define MAX_SUBTITLE_FILES 128
  48. /**
  49.  * The possible extensions for subtitle files we support
  50.  */
  51. static const char const sub_exts[][6] = {
  52.     "idx", "sub",  "srt",
  53.     "ssa", "ass",  "smi",
  54.     "utf", "utf8", "utf-8",
  55.     "txt", "rt",   "aqt",
  56.     "usf", "jss",  "cdg",
  57.     "psb", "mpsub","mpl2",
  58.     "pjs", "dks",
  59.     ""
  60. };
  61. static void strcpy_trim( char *d, const char *s )
  62. {
  63.     /* skip leading whitespace */
  64.     while( *s && !isalnum(*s) )
  65.     {
  66.         s++;
  67.     }
  68.     for(;;)
  69.     {
  70.         /* copy word */
  71.         while( *s && isalnum(*s) )
  72.         {
  73.             *d = tolower(*s);
  74.             s++; d++;
  75.         }
  76.         if( *s == 0 ) break;
  77.         /* trim excess whitespace */
  78.         while( *s && !isalnum(*s) )
  79.         {
  80.             s++;
  81.         }
  82.         if( *s == 0 ) break;
  83.         *d++ = ' ';
  84.     }
  85.     *d = 0;
  86. }
  87. static void strcpy_strip_ext( char *d, const char *s )
  88. {
  89.     const char *tmp = strrchr(s, '.');
  90.     if( !tmp )
  91.     {
  92.         strcpy(d, s);
  93.         return;
  94.     }
  95.     else
  96.         strlcpy(d, s, tmp - s + 1 );
  97.     while( *d )
  98.     {
  99.         *d = tolower(*d);
  100.         d++;
  101.     }
  102. }
  103. static void strcpy_get_ext( char *d, const char *s )
  104. {
  105.     const char *tmp = strrchr(s, '.');
  106.     if( !tmp )
  107.         strcpy(d, "");
  108.     else
  109.         strcpy( d, tmp + 1 );
  110. }
  111. static int whiteonly( const char *s )
  112. {
  113.     while( *s )
  114.     {
  115.         if( isalnum( *s ) )
  116.             return 0;
  117.         s++;
  118.     }
  119.     return 1;
  120. }
  121. enum
  122. {
  123.     SUB_PRIORITY_NONE = 0,
  124.     SUB_PRIORITY_MATCH_NONE = 1,
  125.     SUB_PRIORITY_MATCH_RIGHT = 2,
  126.     SUB_PRIORITY_MATCH_LEFT = 3,
  127.     SUB_PRIORITY_MATCH_ALL = 4,
  128. };
  129. typedef struct
  130. {
  131.     int priority;
  132.     char *psz_fname;
  133.     char *psz_ext;
  134. } vlc_subfn_t;
  135. static int compare_sub_priority( const void *a, const void *b )
  136. {
  137.     const vlc_subfn_t *p0 = a;
  138.     const vlc_subfn_t *p1 = b;
  139.     if( p0->priority > p1->priority )
  140.         return -1;
  141.     if( p0->priority < p1->priority )
  142.         return 1;
  143. #ifndef UNDER_CE
  144.     return strcoll( p0->psz_fname, p1->psz_fname);
  145. #else
  146.     return strcmp( p0->psz_fname, p1->psz_fname);
  147. #endif
  148. }
  149. /*
  150.  * Check if a file ends with a subtitle extension
  151.  */
  152. int subtitles_Filter( const char *psz_dir_content )
  153. {
  154.     const char *tmp = strrchr( psz_dir_content, '.');
  155.     int i;
  156.     if( !tmp )
  157.         return 0;
  158.     tmp++;
  159.     for( i = 0; sub_exts[i][0]; i++ )
  160.         if( strcasecmp( sub_exts[i], tmp ) == 0 )
  161.             return 1;
  162.     return 0;
  163. }
  164. /**
  165.  * Convert a list of paths separated by ',' to a char**
  166.  */
  167. static char **paths_to_list( const char *psz_dir, char *psz_path )
  168. {
  169.     unsigned int i, k, i_nb_subdirs;
  170.     char **subdirs; /* list of subdirectories to look in */
  171.     char *psz_parser = psz_path;
  172.     if( !psz_dir || !psz_path )
  173.         return NULL;
  174.     for( k = 0, i_nb_subdirs = 1; psz_path[k] != ''; k++ )
  175.     {
  176.         if( psz_path[k] == ',' )
  177.             i_nb_subdirs++;
  178.     }
  179.     subdirs = calloc( i_nb_subdirs + 1, sizeof(char*) );
  180.     if( !subdirs )
  181.         return NULL;
  182.     for( i = 0; psz_parser && *psz_parser != '' ; )
  183.     {
  184.         char *psz_subdir = psz_parser;
  185.         psz_parser = strchr( psz_subdir, ',' );
  186.         if( psz_parser )
  187.         {
  188.             *psz_parser++ = '';
  189.             while( *psz_parser == ' ' )
  190.                 psz_parser++;
  191.         }
  192.         if( *psz_subdir == '' )
  193.             continue;
  194.         if( asprintf( &subdirs[i++], "%s%s%c",
  195.                   psz_subdir[0] == '.' ? psz_dir : "",
  196.                   psz_subdir,
  197.                   psz_subdir[strlen(psz_subdir) - 1] == DIR_SEP_CHAR ?
  198.                                            '' : DIR_SEP_CHAR ) == -1 )
  199.             break;
  200.     }
  201.     subdirs[i] = NULL;
  202.     return subdirs;
  203. }
  204. /**
  205.  * Detect subtitle files.
  206.  *
  207.  * When called this function will split up the psz_name string into a
  208.  * directory, filename and extension. It then opens the directory
  209.  * in which the file resides and tries to find possible matches of
  210.  * subtitles files.
  211.  *
  212.  * ingroup Demux
  213.  * param p_this the calling ref input_thread_t
  214.  * param psz_path a list of subdirectories (separated by a ',') to look in.
  215.  * param psz_name the complete filename to base the search on.
  216.  * return a NULL terminated array of filenames with detected possible subtitles.
  217.  * The array contains max MAX_SUBTITLE_FILES items and you need to free it after use.
  218.  */
  219. char **subtitles_Detect( input_thread_t *p_this, char *psz_path,
  220.                          const char *psz_name_org )
  221. {
  222.     int i_fuzzy;
  223.     int j, i_result2, i_sub_count, i_fname_len;
  224.     char *f_dir = NULL, *f_fname = NULL, *f_fname_noext = NULL, *f_fname_trim = NULL;
  225.     char *tmp = NULL;
  226.     char **subdirs; /* list of subdirectories to look in */
  227.     vlc_subfn_t *result = NULL; /* unsorted results */
  228.     char **result2; /* sorted results */
  229.     const char *psz_fname = psz_name_org;
  230.     if( !psz_fname )
  231.         return NULL;
  232.     if( !strncmp( psz_fname, "file://", 7 ) )
  233.         psz_fname += 7;
  234.     /* extract filename & dirname from psz_fname */
  235.     tmp = strrchr( psz_fname, DIR_SEP_CHAR );
  236.     if( tmp )
  237.     {
  238.         const int i_dirlen = strlen(psz_fname)-strlen(tmp)+1; /* include the separator */
  239.         f_fname = strdup( &tmp[1] );    /* skip the separator */
  240.         f_dir = strndup( psz_fname, i_dirlen );
  241.     }
  242.     else
  243.     {
  244. #if defined (HAVE_UNISTD_H) && !defined (UNDER_CE)
  245.         /* Get the current working directory */
  246.         char *psz_cwd = getcwd( NULL, 0 );
  247. #else
  248.         char *psz_cwd = NULL;
  249. #endif
  250.         if( !psz_cwd )
  251.             return NULL;
  252.         f_fname = strdup( psz_fname );
  253.         if( asprintf( &f_dir, "%s%c", psz_cwd, DIR_SEP_CHAR ) == -1 )
  254.             f_dir = NULL; /* Assure that function will return in next test */
  255.         free( psz_cwd );
  256.     }
  257.     if( !f_fname || !f_dir )
  258.     {
  259.         free( f_fname );
  260.         free( f_dir );
  261.         return NULL;
  262.     }
  263.     i_fname_len = strlen( f_fname );
  264.     f_fname_noext = malloc(i_fname_len + 1);
  265.     f_fname_trim = malloc(i_fname_len + 1 );
  266.     if( !f_fname_noext || !f_fname_trim )
  267.     {
  268.         free( f_fname );
  269.         free( f_dir );
  270.         free( f_fname_noext );
  271.         free( f_fname_trim );
  272.         return NULL;
  273.     }
  274.     strcpy_strip_ext( f_fname_noext, f_fname );
  275.     strcpy_trim( f_fname_trim, f_fname_noext );
  276.     i_fuzzy = var_GetInteger( p_this, "sub-autodetect-fuzzy" );
  277.     result = calloc( MAX_SUBTITLE_FILES+1, sizeof(vlc_subfn_t) ); /* We check it later (simplify code) */
  278.     subdirs = paths_to_list( f_dir, psz_path );
  279.     for( j = -1, i_sub_count = 0; (j == -1) || ( j >= 0 && subdirs != NULL && subdirs[j] != NULL ); j++ )
  280.     {
  281.         const char *psz_dir = j < 0 ? f_dir : subdirs[j];
  282.         char **ppsz_dir_content;
  283.         int i_dir_content;
  284.         int a;
  285.         if( psz_dir == NULL || ( j >= 0 && !strcmp( psz_dir, f_dir ) ) )
  286.             continue;
  287.         /* parse psz_src dir */
  288.         i_dir_content = utf8_scandir( psz_dir, &ppsz_dir_content,
  289.                                       subtitles_Filter, NULL );
  290.         if( i_dir_content < 0 )
  291.             continue;
  292.         msg_Dbg( p_this, "looking for a subtitle file in %s", psz_dir );
  293.         for( a = 0; a < i_dir_content && i_sub_count < MAX_SUBTITLE_FILES ; a++ )
  294.         {
  295.             char *psz_name = ppsz_dir_content[a];
  296.             char tmp_fname_noext[strlen( psz_name ) + 1];
  297.             char tmp_fname_trim[strlen( psz_name ) + 1];
  298.             char tmp_fname_ext[strlen( psz_name ) + 1];
  299.             int i_prio;
  300.             if( psz_name == NULL || psz_name[0] == '.' )
  301.                 continue;
  302.             /* retrieve various parts of the filename */
  303.             strcpy_strip_ext( tmp_fname_noext, psz_name );
  304.             strcpy_get_ext( tmp_fname_ext, psz_name );
  305.             strcpy_trim( tmp_fname_trim, tmp_fname_noext );
  306.             i_prio = SUB_PRIORITY_NONE;
  307.             if( i_prio == SUB_PRIORITY_NONE && !strcmp( tmp_fname_trim, f_fname_trim ) )
  308.             {
  309.                 /* matches the movie name exactly */
  310.                 i_prio = SUB_PRIORITY_MATCH_ALL;
  311.             }
  312.             if( i_prio == SUB_PRIORITY_NONE &&
  313.                 ( tmp = strstr( tmp_fname_trim, f_fname_trim ) ) )
  314.             {
  315.                 /* contains the movie name */
  316.                 tmp += strlen( f_fname_trim );
  317.                 if( whiteonly( tmp ) )
  318.                 {
  319.                     /* chars in front of the movie name */
  320.                     i_prio = SUB_PRIORITY_MATCH_RIGHT;
  321.                 }
  322.                 else
  323.                 {
  324.                     /* chars after (and possibly in front of)
  325.                      * the movie name */
  326.                     i_prio = SUB_PRIORITY_MATCH_LEFT;
  327.                 }
  328.             }
  329.             if( i_prio == SUB_PRIORITY_NONE &&
  330.                 j == 0 )
  331.             {
  332.                 /* doesn't contain the movie name, prefer files in f_dir over subdirs */
  333.                 i_prio = SUB_PRIORITY_MATCH_NONE;
  334.             }
  335.             if( i_prio >= i_fuzzy )
  336.             {
  337.                 char psz_path[strlen( psz_dir ) + strlen( psz_name ) + 1];
  338.                 struct stat st;
  339.                 sprintf( psz_path, "%s%s", psz_dir, psz_name );
  340.                 if( !strcmp( psz_path, psz_fname ) )
  341.                     continue;
  342.                 if( !utf8_stat( psz_path, &st ) && S_ISREG( st.st_mode ) && result )
  343.                 {
  344.                     msg_Dbg( p_this,
  345.                             "autodetected subtitle: %s with priority %d",
  346.                             psz_path, i_prio );
  347.                     result[i_sub_count].priority = i_prio;
  348.                     result[i_sub_count].psz_fname = strdup( psz_path );
  349.                     result[i_sub_count].psz_ext = strdup(tmp_fname_ext);
  350.                     i_sub_count++;
  351.                 }
  352.                 else
  353.                 {
  354.                     msg_Dbg( p_this, "stat failed (autodetecting subtitle: %s with priority %d)",
  355.                              psz_path, i_prio );
  356.                 }
  357.             }
  358.         }
  359.         if( ppsz_dir_content )
  360.         {
  361.             for( a = 0; a < i_dir_content; a++ )
  362.                 free( ppsz_dir_content[a] );
  363.             free( ppsz_dir_content );
  364.         }
  365.     }
  366.     if( subdirs )
  367.     {
  368.         for( j = 0; subdirs[j]; j++ )
  369.             free( subdirs[j] );
  370.         free( subdirs );
  371.     }
  372.     free( f_fname );
  373.     free( f_dir );
  374.     free( f_fname_trim );
  375.     free( f_fname_noext );
  376.     if( !result )
  377.         return NULL;
  378.     qsort( result, i_sub_count, sizeof(vlc_subfn_t), compare_sub_priority );
  379.     result2 = calloc( i_sub_count + 1, sizeof(char*) );
  380.     for( j = 0, i_result2 = 0; j < i_sub_count && result2 != NULL; j++ )
  381.     {
  382.         bool b_reject = false;
  383.         if( !result[j].psz_fname || !result[j].psz_ext ) /* memory out */
  384.             break;
  385.         if( !strcasecmp( result[j].psz_ext, "sub" ) )
  386.         {
  387.             int i;
  388.             for( i = 0; i < i_sub_count; i++ )
  389.             {
  390.                 if( result[i].psz_fname && result[i].psz_ext &&
  391.                     !strncasecmp( result[j].psz_fname, result[i].psz_fname,
  392.                                   strlen( result[j].psz_fname) - 3 ) &&
  393.                     !strcasecmp( result[i].psz_ext, "idx" ) )
  394.                     break;
  395.             }
  396.             if( i < i_sub_count )
  397.                 b_reject = true;
  398.         }
  399.         else if( !strcasecmp( result[j].psz_ext, "cdg" ) )
  400.         {
  401.             if( result[j].priority < SUB_PRIORITY_MATCH_ALL )
  402.                 b_reject = true;
  403.         }
  404.         /* */
  405.         if( !b_reject )
  406.             result2[i_result2++] = strdup( result[j].psz_fname );
  407.     }
  408.     for( j = 0; j < i_sub_count; j++ )
  409.     {
  410.         free( result[j].psz_fname );
  411.         free( result[j].psz_ext );
  412.     }
  413.     free( result );
  414.     return result2;
  415. }