Re: [PATCH 2/2] local-metadata: add series support



Hi Lionel,

Quick "Friday evening" review:
 - As asked previously, could you rename video_uri_to_metadata()? (or
give a reason why not to do it)
 - I don't think that having several instances of a given source is a
case we really support yet, but to be future-proof, and overall cleaner,
it would be better to have the guess-video parameter stored in the
source instance rather than as a global variable. See for instance how
the API Key is handled in the youtube plugin.
 - Could you address the limitations of your may_resolve()
implementation I mentioned in my last review?

Thanks,

Guillaume

On 18/03/2011 14:57, lionel g landwerlin linux intel com wrote:
> From: Lionel Landwerlin <lionel g landwerlin linux intel com>
> 
> Based on a patch for Tracker by Iain Holmes :
> 
> http://build.meego.com/package/view_file?file=0002-Tracker-extract-Parse-the-video-filename-to-obtain-e.patch&package=tracker&project=devel%3Acontentfw&srcmd5=c91b724488ee9ae476a9ce65755d8152
> 
> Signed-off-by: Lionel Landwerlin <lionel g landwerlin linux intel com>
> ---
>  configure.ac                                     |    3 +
>  src/metadata/local-metadata/grl-local-metadata.c |  417 ++++++++++++++++++++--
>  2 files changed, 397 insertions(+), 23 deletions(-)
> 
> diff --git a/configure.ac b/configure.ac
> index 5d9d9a9..15304f8 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -709,6 +709,9 @@ AC_ARG_ENABLE(localmetadata,
>                          if test "x$HAVE_GIO" = "xno"; then
>                             AC_MSG_ERROR([gio-2.0 not found, install it or use --disable-localmetadata])
>                          fi
> +                        if test "x$HAVE_GLIB_2_26" = "xno"; then
> +                           AC_MSG_ERROR([glib-2.26.0 or above not found, install it or use --disable-localmetadata])
> +                        fi
>                          ;;
>                  esac
>          ],
> diff --git a/src/metadata/local-metadata/grl-local-metadata.c b/src/metadata/local-metadata/grl-local-metadata.c
> index 62737ba..b53f6f2 100644
> --- a/src/metadata/local-metadata/grl-local-metadata.c
> +++ b/src/metadata/local-metadata/grl-local-metadata.c
> @@ -24,6 +24,9 @@
>  #include "config.h"
>  #endif
>  
> +#include <string.h>
> +#include <stdlib.h>
> +
>  #include <grilo.h>
>  #include <gio/gio.h>
>  
> @@ -42,6 +45,43 @@ GRL_LOG_DOMAIN_STATIC(local_metadata_log_domain);
>  #define LICENSE     "LGPL"
>  #define SITE        "http://www.igalia.com";
>  
> +/**/
> +
> +#define TV_REGEX                                \
> +  "(?<showname>.*)\\."                          \
> +  "(?<season>(?:\\d{1,2})|(?:[sS]\\K\\d{1,2}))" \
> +  "(?<episode>(?:\\d{2})|(?:[eE]\\K\\d{1,2}))"  \
> +  "\\.?(?<name>.*)?"
> +#define MOVIE_REGEX                             \
> +  "(?<name>.*)"                                 \
> +  "\\.?[\\(\\[](?<year>[12][90]\\d{2})[\\)\\]]"
> +
> +/**/
> +
> +typedef enum {
> +  FLAG_VIDEO_TITLE    = 0x1,
> +  FLAG_VIDEO_SHOWNAME = 0x2,
> +  FLAG_VIDEO_DATE     = 0x4,
> +  FLAG_VIDEO_SEASON   = 0x8,
> +  FLAG_VIDEO_EPISODE  = 0x10,
> +  FLAG_THUMBNAIL      = 0x20,
> +} resolution_flags_t;
> +
> +const gchar *video_blacklisted_prefix[] = {
> +  "tpz-", NULL
> +};
> +
> +
> +const char *video_blacklisted_words[] = {
> +  "720p", "1080p",
> +  "ws", "WS", "proper", "PROPER",
> +  "repack", "real.repack",
> +  "hdtv", "HDTV", "pdtv", "PDTV", "notv", "NOTV",
> +  "dsr", "DSR", "DVDRip", "divx", "DIVX", "xvid", "Xvid",
> +  NULL
> +};
> +
> +/**/
>  
>  static GrlLocalMetadataSource *grl_local_metadata_source_new (void);
>  
> @@ -59,6 +99,9 @@ gboolean grl_local_metadata_source_plugin_init (GrlPluginRegistry *registry,
>                                                 const GrlPluginInfo *plugin,
>                                                 GList *configs);
>  
> +/**/
> +
> +static gboolean grl_local_metadata_guess_video = TRUE;
>  
>  /* =================== GrlLocalMetadata Plugin  =============== */
>  
> @@ -67,10 +110,27 @@ grl_local_metadata_source_plugin_init (GrlPluginRegistry *registry,
>                                        const GrlPluginInfo *plugin,
>                                        GList *configs)
>  {
> +  guint config_count;
> +  GrlConfig *config;
> +
>    GRL_LOG_DOMAIN_INIT (local_metadata_log_domain, "local-metadata");
>  
>    GRL_DEBUG ("grl_local_metadata_source_plugin_init");
>  
> +  if (!configs) {
> +    GRL_WARNING ("\tConfiguration not provided! Using default configuration.");
> +  } else {
> +    config_count = g_list_length (configs);
> +    if (config_count > 1) {
> +      GRL_WARNING ("\tProvided %i configs, but will only use one", config_count);
> +    }
> +
> +    config = GRL_CONFIG (configs->data);
> +
> +    grl_local_metadata_guess_video =
> +      grl_config_get_boolean (config, "guess-video");
> +  }
> +
>    GrlLocalMetadataSource *source = grl_local_metadata_source_new ();
>    grl_plugin_registry_register_source (registry,
>                                         plugin,
> @@ -115,6 +175,191 @@ G_DEFINE_TYPE (GrlLocalMetadataSource,
>                 GRL_TYPE_METADATA_SOURCE);
>  
>  /* ======================= Utilities ==================== */
> +
> +static gchar *
> +video_sanitise_string (const gchar *str)
> +{
> +  int    i;
> +  gchar *line;
> +
> +  line = (gchar *) str;
> +  for (i = 0; video_blacklisted_prefix[i]; i++) {
> +    if (g_str_has_prefix (str, video_blacklisted_prefix[i])) {
> +      int len = strlen (video_blacklisted_prefix[i]);
> +
> +      line = (gchar *) str + len;
> +    }
> +  }
> +
> +  for (i = 0; video_blacklisted_words[i]; i++) {
> +    gchar *end;
> +
> +    end = strstr (line, video_blacklisted_words[i]);
> +    if (end) {
> +      return g_strndup (line, end - line);
> +    }
> +  }
> +
> +  return g_strdup (line);
> +}
> +
> +/* tidies strings before we run them through the regexes */
> +static gchar *
> +video_uri_to_metadata (const gchar *uri)
> +{
> +  gchar *ext, *basename, *name, *whitelisted;
> +
> +  basename = g_path_get_basename (uri);
> +  ext = strrchr (basename, '.');
> +  if (ext) {
> +    name = g_strndup (basename, ext - basename);
> +    g_free (basename);
> +  } else {
> +    name = basename;
> +  }
> +
> +  /* Replace _ <space> with . */
> +  g_strdelimit (name, "_ ", '.');
> +  whitelisted = video_sanitise_string (name);
> +  g_free (name);
> +
> +  return whitelisted;
> +}
> +
> +static void
> +video_guess_values_from_uri (const gchar *uri,
> +                             gchar      **title,
> +                             gchar      **showname,
> +                             GDateTime  **date,
> +                             gint        *season,
> +                             gint        *episode)
> +{
> +  gchar      *metadata;
> +  GRegex     *regex;
> +  GMatchInfo *info;
> +
> +  metadata = video_uri_to_metadata (uri);
> +
> +  regex = g_regex_new (MOVIE_REGEX, 0, 0, NULL);
> +  g_regex_match (regex, metadata, 0, &info);
> +
> +  if (g_match_info_matches (info)) {
> +    if (title) {
> +      *title = g_match_info_fetch_named (info, "name");
> +      /* Replace "." with <space> */
> +      g_strdelimit (*title, ".", ' ');
> +    }
> +
> +    if (date) {
> +      gchar *year = g_match_info_fetch_named (info, "year");
> +
> +      *date = g_date_time_new_utc (atoi (year), 1, 1, 0, 0, 0.0);
> +      g_free (year);
> +    }
> +
> +    if (showname) {
> +      *showname = NULL;
> +    }
> +
> +    if (season) {
> +      *season = 0;
> +    }
> +
> +    if (episode) {
> +      *episode = 0;
> +    }
> +
> +    g_regex_unref (regex);
> +    g_match_info_free (info);
> +    g_free (metadata);
> +
> +    return;
> +  }
> +
> +  g_regex_unref (regex);
> +  g_match_info_free (info);
> +
> +  regex = g_regex_new (TV_REGEX, 0, 0, NULL);
> +  g_regex_match (regex, metadata, 0, &info);
> +
> +  if (g_match_info_matches (info)) {
> +    if (title) {
> +      *title = g_match_info_fetch_named (info, "name");
> +      g_strdelimit (*title, ".", ' ');
> +    }
> +
> +    if (showname) {
> +      *showname = g_match_info_fetch_named (info, "showname");
> +      g_strdelimit (*showname, ".", ' ');
> +    }
> +
> +    if (season) {
> +      gchar *s = g_match_info_fetch_named (info, "season");
> +      if (s) {
> +        if (*s == 's' || *s == 'S') {
> +          *season = atoi (s + 1);
> +        } else {
> +          *season = atoi (s);
> +        }
> +      } else {
> +        *season = 0;
> +      }
> +
> +      g_free (s);
> +    }
> +
> +    if (episode) {
> +      gchar *e = g_match_info_fetch_named (info, "episode");
> +      if (e) {
> +        if (*e == 'e' || *e == 'E') {
> +          *episode = atoi (e + 1);
> +        } else {
> +          *episode = atoi (e);
> +        }
> +      } else {
> +        *episode = 0;
> +      }
> +
> +      g_free (e);
> +    }
> +
> +    if (date) {
> +      *date = NULL;
> +    }
> +
> +    g_regex_unref (regex);
> +    g_match_info_free (info);
> +    g_free (metadata);
> +
> +    return;
> +  }
> +
> +  g_regex_unref (regex);
> +  g_match_info_free (info);
> +
> +  /* The filename doesn't look like a movie or a TV show, just use the
> +     filename without extension as the title */
> +  if (title) {
> +    *title = g_strdelimit (metadata, ".", ' ');
> +  }
> +
> +  if (showname) {
> +    *showname = NULL;
> +  }
> +
> +  if (date) {
> +    *date = NULL;
> +  }
> +
> +  if (season) {
> +    *season = 0;
> +  }
> +
> +  if (episode) {
> +    *episode = 0;
> +  }
> +}
> +
>  static void
>  got_file_info (GFile *file, GAsyncResult *result,
>                 GrlMetadataSourceResolveSpec *rs)
> @@ -168,21 +413,99 @@ exit:
>  }
>  
>  static void
> -resolve_image (GrlMetadataSourceResolveSpec *rs)
> +resolve_video (GrlMetadataSourceResolveSpec *rs, resolution_flags_t flags)
> +{
> +  gchar *title, *showname;
> +  GDateTime *date;
> +  gint season, episode;
> +  GrlData *data = GRL_DATA (rs->media);
> +  resolution_flags_t miss_flags = 0, fill_flags;
> +
> +  GRL_DEBUG ("%s",__FUNCTION__);
> +
> +  if (!(flags & (FLAG_VIDEO_TITLE |
> +                 FLAG_VIDEO_SHOWNAME |
> +                 FLAG_VIDEO_DATE |
> +                 FLAG_VIDEO_SEASON |
> +                 FLAG_VIDEO_EPISODE)))
> +    return;
> +
> +  miss_flags |= grl_data_key_is_known (data, GRL_METADATA_KEY_TITLE) ?
> +    0 : FLAG_VIDEO_TITLE;
> +  miss_flags |= grl_data_key_is_known (data, GRL_METADATA_KEY_SHOW) ?
> +    0 : FLAG_VIDEO_SHOWNAME;
> +  miss_flags |= grl_data_key_is_known (data, GRL_METADATA_KEY_DATE) ?
> +    0 : FLAG_VIDEO_DATE;
> +  miss_flags |= grl_data_key_is_known (data, GRL_METADATA_KEY_SEASON) ?
> +    0 : FLAG_VIDEO_SEASON;
> +  miss_flags |= grl_data_key_is_known (data, GRL_METADATA_KEY_EPISODE) ?
> +    0 : FLAG_VIDEO_EPISODE;
> +
> +  fill_flags = flags & miss_flags;
> +
> +  if (!fill_flags)
> +    return;
> +
> +  video_guess_values_from_uri (grl_media_get_url (rs->media),
> +                               &title, &showname, &date,
> +                               &season, &episode);
> +
> +  GRL_DEBUG ("\tfound title=%s/showname=%s/year=%i/season=%i/episode=%i",
> +             title, showname,
> +             date != NULL ? g_date_time_get_year (date) : 0,
> +             season, episode);
> +
> +  /* As this is just a guess, don't erase already provided values. */
> +  if (title) {
> +    if (fill_flags & FLAG_VIDEO_TITLE) {
> +      grl_data_set_string (data, GRL_METADATA_KEY_TITLE, title);
> +    }
> +    g_free (title);
> +  }
> +
> +  if (showname) {
> +    if (fill_flags & FLAG_VIDEO_SHOWNAME) {
> +      grl_data_set_string (data, GRL_METADATA_KEY_SHOW, showname);
> +    }
> +    g_free (showname);
> +  }
> +
> +  if (date) {
> +    if (fill_flags & FLAG_VIDEO_DATE) {
> +      gchar *str_date = g_date_time_format (date, "%F");
> +      grl_data_set_string (data, GRL_METADATA_KEY_DATE, str_date);
> +      g_free (str_date);
> +    }
> +    g_date_time_unref (date);
> +  }
> +
> +  if (season && (fill_flags & FLAG_VIDEO_SEASON)) {
> +    grl_data_set_int (data, GRL_METADATA_KEY_SEASON, season);
> +  }
> +
> +  if (episode && (fill_flags & FLAG_VIDEO_EPISODE)) {
> +    grl_data_set_int (data, GRL_METADATA_KEY_EPISODE, episode);
> +  }
> +}
> +
> +static void
> +resolve_image (GrlMetadataSourceResolveSpec *rs, resolution_flags_t flags)
>  {
>    GFile *file;
>  
>    GRL_DEBUG ("resolve_image");
>  
> -  file = g_file_new_for_uri (grl_media_get_url (rs->media));
> +  if (flags & FLAG_THUMBNAIL) {
> +    file = g_file_new_for_uri (grl_media_get_url (rs->media));
>  
> -  g_file_query_info_async (file, G_FILE_ATTRIBUTE_THUMBNAIL_PATH,
> -                           G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, NULL,
> -                           (GAsyncReadyCallback)got_file_info, rs);
> +    g_file_query_info_async (file, G_FILE_ATTRIBUTE_THUMBNAIL_PATH,
> +                             G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, NULL,
> +                             (GAsyncReadyCallback)got_file_info, rs);
> +  }
>  }
>  
>  static void
> -resolve_album_art (GrlMetadataSourceResolveSpec *rs)
> +resolve_album_art (GrlMetadataSourceResolveSpec *rs, resolution_flags_t flags)
>  {
>    /* FIXME: implement this, according to
>     * http://live.gnome.org/MediaArtStorageSpec
> @@ -214,6 +537,33 @@ has_compatible_media_url (GrlMedia *media)
>  
>    return ret;
>  }
> +
> +static resolution_flags_t
> +get_resolution_flags (GList *keys)
> +{
> +  GList *key = keys;
> +  resolution_flags_t flags = 0;
> +
> +  while (key != NULL) {
> +    if (key->data == GRL_METADATA_KEY_TITLE)
> +      flags |= FLAG_VIDEO_TITLE;
> +    else if (key->data == GRL_METADATA_KEY_SHOW)
> +      flags |= FLAG_VIDEO_SHOWNAME;
> +    else if (key->data == GRL_METADATA_KEY_DATE)
> +      flags |= FLAG_VIDEO_DATE;
> +    else if (key->data == GRL_METADATA_KEY_SEASON)
> +      flags |= FLAG_VIDEO_SEASON;
> +    else if (key->data == GRL_METADATA_KEY_EPISODE)
> +      flags |= FLAG_VIDEO_EPISODE;
> +    else if (key->data == GRL_METADATA_KEY_THUMBNAIL)
> +      flags |= FLAG_THUMBNAIL;
> +
> +    key = key->next;
> +  }
> +
> +  return flags;
> +}
> +
>  /* ================== API Implementation ================ */
>  
>  static const GList *
> @@ -222,6 +572,11 @@ grl_local_metadata_source_supported_keys (GrlMetadataSource *source)
>    static GList *keys = NULL;
>    if (!keys) {
>      keys = grl_metadata_key_list_new (GRL_METADATA_KEY_THUMBNAIL,
> +                                      GRL_METADATA_KEY_TITLE,
> +                                      GRL_METADATA_KEY_SHOW,
> +                                      GRL_METADATA_KEY_DATE,
> +                                      GRL_METADATA_KEY_SEASON,
> +                                      GRL_METADATA_KEY_EPISODE,
>                                        NULL);
>    }
>    return keys;
> @@ -233,13 +588,21 @@ grl_local_metadata_source_may_resolve (GrlMetadataSource *source,
>                                         GrlKeyID key_id,
>                                         GList **missing_keys)
>  {
> -  if (key_id != GRL_METADATA_KEY_THUMBNAIL
> -      || GRL_IS_MEDIA_AUDIO (media)
> -      || GRL_IS_MEDIA_BOX (media))
> -    return FALSE;
> -
> -  if (media && grl_data_key_is_known (GRL_DATA (media), GRL_METADATA_KEY_URL))
> -    return has_compatible_media_url (media);
> +  if (media) {
> +    if (grl_data_key_is_known (GRL_DATA (media), GRL_METADATA_KEY_URL) &&
> +        has_compatible_media_url (media)) {
> +      if (key_id == GRL_METADATA_KEY_THUMBNAIL)
> +        return TRUE;
> +
> +      if (grl_local_metadata_guess_video &&
> +          (key_id == GRL_METADATA_KEY_TITLE ||
> +           key_id == GRL_METADATA_KEY_SHOW ||
> +           key_id == GRL_METADATA_KEY_DATE ||
> +           key_id == GRL_METADATA_KEY_SEASON ||
> +           key_id == GRL_METADATA_KEY_EPISODE))
> +        return TRUE;
> +    }
> +  }
>  
>    if (missing_keys)
>      *missing_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_URL, NULL);
> @@ -252,15 +615,18 @@ grl_local_metadata_source_resolve (GrlMetadataSource *source,
>                                    GrlMetadataSourceResolveSpec *rs)
>  {
>    GError *error = NULL;
> +  resolution_flags_t flags;
>  
>    GRL_DEBUG ("grl_local_metadata_source_resolve");
>  
> -  if (!has_compatible_media_url (rs->media))
> -    error = g_error_new (GRL_CORE_ERROR, GRL_CORE_ERROR_RESOLVE_FAILED,
> -                         "local-metadata needs a url in the file:// scheme");
> -  else if (!g_list_find (rs->keys, GRL_METADATA_KEY_THUMBNAIL))
> -    error = g_error_new (GRL_CORE_ERROR, GRL_CORE_ERROR_RESOLVE_FAILED,
> -                         "local-metadata can only resolve the thumbnail key");
> +  flags = get_resolution_flags (rs->keys);
> +
> +   if (!flags)
> +     error = g_error_new (GRL_CORE_ERROR, GRL_CORE_ERROR_RESOLVE_FAILED,
> +                          "local-metadata cannot resolve any of the given keys");
> +   else if (!has_compatible_media_url (rs->media))
> +     error = g_error_new (GRL_CORE_ERROR, GRL_CORE_ERROR_RESOLVE_FAILED,
> +                          "local-metadata needs a url in the file:// scheme");
>  
>    if (error) {
>      /* No can do! */
> @@ -269,11 +635,16 @@ grl_local_metadata_source_resolve (GrlMetadataSource *source,
>      return;
>    }
>  
> -  if (GRL_IS_MEDIA_VIDEO (rs->media)
> -      || GRL_IS_MEDIA_IMAGE (rs->media)) {
> -    resolve_image (rs);
> +  GRL_DEBUG ("\ttrying to resolve for: %s", grl_media_get_url (rs->media));
> +
> +  if (GRL_IS_MEDIA_VIDEO (rs->media)) {
> +    if (grl_local_metadata_guess_video)
> +      resolve_video (rs, flags);
> +    resolve_image (rs, flags);
> +  } else if (GRL_IS_MEDIA_IMAGE (rs->media)) {
> +    resolve_image (rs, flags);
>    } else if (GRL_IS_MEDIA_AUDIO (rs->media)) {
> -    resolve_album_art (rs);
> +    resolve_album_art (rs, flags);
>    } else {
>      /* What's that media type? */
>      rs->callback (source, rs->media, rs->user_data, NULL);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]