; ; in alphabetical order. If 'randstart', the files are sorted
; ; in alphabetical order as well, but the first file is chosen
; ; at random. If unspecified, the sort order is undefined.
+;loop_last=no ; If enabled, once the end of the directory is reached,
+ ; the last file played will be looped perpetually, rather than
+ ; starting over at the beginning again.
+ ; Can be used with sort=alpha or randstart so you can control
+ ; which file gets looped (the last one sorted alphabetically).
+ ; (If sort=alpha, all files will be played at least once, but
+ ; this may not be true with sort=randstart.)
+ ; Default is no.
;answeredonly=yes ; Only allow answered channels to have music on hold.
; Enabling this will prevent MOH on unanswered channels.
; (default: "no")
--- /dev/null
+"""Add loop_last to res_musiconhold
+
+Revision ID: f5b0e7427449
+Revises: f261363a857f
+Create Date: 2023-03-13 23:59:00.835055
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'f5b0e7427449'
+down_revision = 'f261363a857f'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+ yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+ op.add_column('musiconhold', sa.Column('loop_last', yesno_values))
+
+def downgrade():
+ if op.get_context().bind.dialect.name == 'mssql':
+ op.drop_constraint('musiconhold','loop_last')
+ op.drop_column('musiconhold', 'loop_last')
#define MOH_ANNOUNCEMENT (1 << 6) /*!< Do we play announcement files between songs on this channel? */
#define MOH_PREFERCHANNELCLASS (1 << 7) /*!< Should queue moh override channel moh */
+#define MOH_LOOPLAST (1 << 8) /*!< Whether to loop the last file in the music class when we reach the end, rather than starting over */
+
/* Custom astobj2 flag */
#define MOH_NOTDELETED (1 << 30) /*!< Find only records that aren't deleted? */
#define MOH_REALTIME (1 << 31) /*!< Find only records that are realtime */
} else {
/* This is easy, just increment our position and make sure we don't exceed the total file count */
state->pos++;
- state->pos %= file_count;
+ if (ast_test_flag(state->class, MOH_LOOPLAST)) {
+ state->pos = MIN(file_count - 1, state->pos);
+ } else {
+ state->pos %= file_count;
+ }
state->save_pos = -1;
state->samples = 0;
}
} else if (!strcasecmp(var->value, "randstart")) {
ast_set_flag(mohclass, MOH_RANDSTART);
}
+ } else if (!strcasecmp(var->name, "loop_last")) {
+ if (ast_true(var->value)) {
+ ast_set_flag(mohclass, MOH_LOOPLAST);
+ } else {
+ ast_clear_flag(mohclass, MOH_LOOPLAST);
+ }
} else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
ao2_cleanup(mohclass->format);
mohclass->format = ast_format_cache_get(var->value);