return ch;
}
+
+/// Copy name without space and HD suffix, lowercase in to a new
+/// buffer
+static char *
+channel_make_fuzzy_name(const char *name)
+{
+ if (!name) return NULL;
+ const size_t len = strlen(name);
+ char *fuzzy_name = malloc(len + 1);
+ char *ch_fuzzy = fuzzy_name;
+ const char *ch = name;
+
+ for (; *ch ; ++ch) {
+ /* Strip trailing 'HD'. */
+ if (*ch == 'H' && *(ch+1) == 'D' && *(ch+2) == 0)
+ break;
+
+ if (!isspace(*ch)) {
+ *ch_fuzzy++ = tolower(*ch);
+ }
+ }
+ /* Terminate the string */
+ *ch_fuzzy = 0;
+ return fuzzy_name;
+}
+
+channel_t *
+channel_find_by_name_fuzzy ( const char *name )
+{
+ channel_t *ch;
+ const char *s;
+
+ if (name == NULL)
+ return NULL;
+
+ char *fuzzy_name = channel_make_fuzzy_name(name);
+
+ CHANNEL_FOREACH(ch) {
+ if (!ch->ch_enabled) continue;
+ s = channel_get_name(ch, NULL);
+ if (s == NULL) continue;
+ /* We need case insensitive since historical constraints means we
+ * often have channels with slightly different case on DVB-T vs
+ * DVB-S such as 'One' and 'ONE'.
+ */
+ if (strcasecmp(s, name) == 0) break;
+ if (strcasecmp(s, fuzzy_name) == 0) break;
+
+ /* If here, we don't have an obvious match, so we need to fixup
+ * the ch name to see if it then matches. We can use strcmp
+ * since both names are already lowercased.
+ */
+ char *s_fuzzy_name = channel_make_fuzzy_name(s);
+ const int is_match = !strcmp(s_fuzzy_name, fuzzy_name);
+ free(s_fuzzy_name);
+ if (is_match) break;
+ }
+
+ free(fuzzy_name);
+ return ch;
+}
+
channel_t *
channel_find_by_id ( uint32_t i )
{
void channel_delete(channel_t *ch, int delconf);
channel_t *channel_find_by_name(const char *name);
+/// Apply fuzzy matching when finding a channel such as ignoring
+/// whitespace, case, and stripping HD suffix. This means that
+/// 'Channel 5+1', 'Channel 5 +1', 'Channel 5+1HD' and
+/// 'Channel 5 +1HD' can all be merged together.
+/// Since channel names aren't unique, this returns the
+/// first match (similar to channel_find_by_name).
+channel_t *channel_find_by_name_fuzzy(const char *name);
#define channel_find_by_uuid(u)\
(channel_t*)idnode_find(u, &channel_class, NULL)
/* Find existing channel */
name = service_get_channel_name(s);
- if (!bq && conf->merge_same_name && name && *name)
+ if (!bq && conf->merge_same_name && name && *name) {
+ /* Try exact match first */
chn = channel_find_by_name(name);
+ if (!chn && conf->merge_same_name_fuzzy) {
+ chn = channel_find_by_name_fuzzy(name);
+ }
+ }
if (!chn) {
chn = channel_create(NULL, NULL, NULL);
chn->ch_bouquet = bq;
.desc = N_("Merge services with the same name into one channel."),
.off = offsetof(service_mapper_t, d.merge_same_name),
},
+ {
+ .type = PT_BOOL,
+ .id = "merge_same_name_fuzzy",
+ .name = N_("Use fuzzy mapping if merging same name"),
+ .desc = N_("If merge same name is enabled then "
+ "merge services with the same name into one channel but "
+ "using fuzzy logic such as ignoring whitespace, case and "
+ "some channel suffixes such as HD. So 'Channel 5+1', "
+ "'Channel 5 +1', 'Channel 5+1HD' and 'Channel 5 +1HD' would "
+ "all merge in to the same channel. The exact name chosen "
+ "depends on the order the channels are mapped."),
+ .off = offsetof(service_mapper_t, d.merge_same_name_fuzzy),
+ },
{
.type = PT_BOOL,
.id = "type_tags",
int check_availability; ///< Check service is receivable
int encrypted; ///< Include encrypted services
int merge_same_name; ///< Merge entries with the same name
+ int merge_same_name_fuzzy; ///< Merge entries with the same name with fuzzy matching (ignore case, etc)
int type_tags; ///< Create tags based on the service type (SDTV/HDTV/Radio)
int provider_tags; ///< Create tags based on provider name
int network_tags; ///< Create tags based on network name (useful for multi adapter equipments)