Merge "res_musiconhold: Add new 'playlist' mode" into 16

This commit is contained in:
Friendly Automation 2019-09-27 07:44:52 -05:00 committed by Gerrit Code Review
commit 1903dcb43d
5 changed files with 139 additions and 2 deletions

View File

@ -95,6 +95,7 @@
;queue_rules => odbc,asterisk
;acls => odbc,asterisk
;musiconhold => mysql,general
;musiconhold_entry => mysql,general
;queue_log => mysql,general
;
;

View File

@ -13,6 +13,7 @@
; valid mode options:
; files -- read files from a directory in any Asterisk supported
; media format
; playlist -- provide a fixed list of filenames or URLs to play
; quietmp3 -- default
; mp3 -- loud
; mp3nb -- unbuffered
@ -44,6 +45,22 @@
; this, res_musiconhold will skip the files it is not able to
; understand when it loads.
;
; =========
; Playlist (native) music on hold
; =========
;
; This mode is similar to 'files' mode in that it plays through a list
; of files, but instead of scanning a directory the files are
; explicitly configured using one or more 'entry' options.
;
; Each entry must be one of:
;
; * An absolute path to the file to be played, without an extension.
; * A URL
;
; The entries are played in the order in which they appear in the
; configuration. The 'sort' option is not used for this mode.
;
[default]
mode=files
@ -71,6 +88,12 @@ directory=moh
;directory=moh
;sort=alpha ; Sort the files in alphabetical order.
;[sales-queue-hold]
;mode=playlist
;entry=/var/lib/asterisk/sounds/en/yourcallisimportant
;entry=http://example.local/sales-queue-hold-music.ulaw
;entry=/var/lib/asterisk/moh/macroform-robot_dity
; =========
; Other (non-native) playback methods
; =========

View File

@ -0,0 +1,54 @@
"""add playlist to moh
Revision ID: fbb7766f17bc
Revises: 3a094a18e75b
Create Date: 2019-09-18 10:24:18.731798
"""
# revision identifiers, used by Alembic.
revision = 'fbb7766f17bc'
down_revision = '3a094a18e75b'
from alembic import op
import sqlalchemy as sa
def enum_update(table_name, column_name, enum_name, enum_values):
if op.get_context().bind.dialect.name != 'postgresql':
if op.get_context().bind.dialect.name == 'mssql':
op.drop_constraint('ck_musiconhold_mode_moh_mode_values', 'musiconhold')
op.alter_column(table_name, column_name,
type_=sa.Enum(*enum_values, name=enum_name))
return
# Postgres requires a few more steps
tmp = enum_name + '_tmp'
op.execute('ALTER TYPE ' + enum_name + ' RENAME TO ' + tmp)
updated = sa.Enum(*enum_values, name=enum_name)
updated.create(op.get_bind(), checkfirst=False)
op.execute('ALTER TABLE ' + table_name + ' ALTER COLUMN ' + column_name +
' TYPE ' + enum_name + ' USING mode::text::' + enum_name)
op.execute('DROP TYPE ' + tmp)
def upgrade():
op.create_table(
'musiconhold_entry',
sa.Column('name', sa.String(80), primary_key=True, nullable=False),
sa.Column('position', sa.Integer, primary_key=True, nullable=False),
sa.Column('entry', sa.String(1024), nullable=False)
)
op.create_foreign_key('fk_musiconhold_entry_name_musiconhold', 'musiconhold_entry', 'musiconhold', ['name'], ['name'])
enum_update('musiconhold', 'mode', 'moh_mode_values',
['custom', 'files', 'mp3nb', 'quietmp3nb', 'quietmp3', 'playlist'])
def downgrade():
enum_update('musiconhold', 'mode', 'moh_mode_values',
['custom', 'files', 'mp3nb', 'quietmp3nb', 'quietmp3'])
op.drop_table('musiconhold_entry')

View File

@ -0,0 +1,5 @@
Subject: res_musiconhold
A new mode - playlist - has been added to res_musiconhold. This mode allows the
user to specify the files (or URLs) to play explicitly by putting them directly
in musiconhold.conf.

View File

@ -1081,6 +1081,20 @@ static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclas
ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
} else if (!strcasecmp(var->name, "mode")) {
ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
} else if (!strcasecmp(var->name, "entry")) {
if (ast_begins_with(var->value, "/") || ast_begins_with(var->value, "http://") || ast_begins_with(var->value, "https://")) {
char *dup = ast_strdup(var->value);
if (!dup) {
continue;
}
if (ast_begins_with(dup, "/") && strrchr(dup, '.')) {
ast_log(LOG_WARNING, "The playlist entry '%s' may include an extension, which could prevent it from playing.\n",
dup);
}
AST_VECTOR_APPEND(&mohclass->files, dup);
} else {
ast_log(LOG_ERROR, "Playlist entries must be a URL or absolute path, '%s' provided.\n", var->value);
}
} else if (!strcasecmp(var->name, "directory")) {
ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
} else if (!strcasecmp(var->name, "application")) {
@ -1130,6 +1144,8 @@ static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclas
}
}
}
AST_VECTOR_COMPACT(&mohclass->files);
}
static int moh_scan_files(struct mohclass *class) {
@ -1333,6 +1349,13 @@ static int _moh_register(struct mohclass *moh, int reload, int unref, const char
}
return -1;
}
} else if (!strcasecmp(moh->mode, "playlist")) {
if (!AST_VECTOR_SIZE(&moh->files)) {
if (unref) {
moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
}
return -1;
}
} else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
!strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
!strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
@ -1485,6 +1508,32 @@ static struct mohclass *_moh_class_malloc(const char *file, int line, const char
static struct ast_variable *load_realtime_musiconhold(const char *name)
{
struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
if (var) {
const char *mode = ast_variable_find_in_list(var, "mode");
if (ast_strings_equal(mode, "playlist")) {
struct ast_variable *entries = ast_load_realtime("musiconhold_entry", "name", name, SENTINEL);
struct ast_variable *cur = entries;
size_t entry_count = 0;
for (; cur; cur = cur->next) {
if (!strcmp(cur->name, "entry")) {
struct ast_variable *dup = ast_variable_new(cur->name, cur->value, "");
if (dup) {
entry_count++;
ast_variable_list_append(&var, dup);
}
}
}
ast_variables_destroy(entries);
if (entry_count == 0) {
/* Behave as though this class doesn't exist */
ast_variables_destroy(var);
var = NULL;
}
}
}
if (!var) {
ast_log(LOG_WARNING,
"Music on Hold class '%s' not found in memory/database. "
@ -1551,7 +1600,7 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con
ast_variables_destroy(var);
if (ast_strlen_zero(mohclass->dir)) {
if (!strcasecmp(mohclass->mode, "custom")) {
if (!strcasecmp(mohclass->mode, "custom") || !strcasecmp(mohclass->mode, "playlist")) {
strcpy(mohclass->dir, "nodir");
} else {
ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
@ -1605,6 +1654,11 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con
}
ast_set_flag(mohclass, MOH_RANDOMIZE);
}
} else if (!strcasecmp(mohclass->mode, "playlist")) {
if (!AST_VECTOR_SIZE(&mohclass->files)) {
mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no playlist entries)");
return -1;
}
} else if (!strcasecmp(mohclass->mode, "mp3") || !strcasecmp(mohclass->mode, "mp3nb") || !strcasecmp(mohclass->mode, "quietmp3") || !strcasecmp(mohclass->mode, "quietmp3nb") || !strcasecmp(mohclass->mode, "httpmp3") || !strcasecmp(mohclass->mode, "custom")) {
if (!strcasecmp(mohclass->mode, "custom"))
@ -1846,7 +1900,7 @@ static int load_moh_classes(int reload)
ast_copy_string(class->name, cat, sizeof(class->name));
if (ast_strlen_zero(class->dir)) {
if (!strcasecmp(class->mode, "custom")) {
if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
strcpy(class->dir, "nodir");
} else {
ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);