diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 677e884e..9fb66ea4 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -262,6 +262,8 @@ const char *dvb_mux_add_by_params(th_dvb_adapter_t *tda, int polarisation, const char *satconf); +int dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src); + /** * DVB Transport (aka DVB service) */ diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index b2f9d723..9b6a157d 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -359,69 +359,19 @@ void dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src) { th_dvb_mux_instance_t *tdmi_src, *tdmi_dst; - th_transport_t *t_src, *t_dst; - th_stream_t *st_src, *st_dst; lock_assert(&global_lock); while((tdmi_dst = LIST_FIRST(&dst->tda_muxes)) != NULL) dvb_mux_destroy(tdmi_dst); - LIST_FOREACH(tdmi_src, &src->tda_muxes, tdmi_adapter_link) { + LIST_FOREACH(tdmi_src, &src->tda_muxes, tdmi_adapter_link) + dvb_mux_copy(dst, tdmi_src); - tdmi_dst = dvb_mux_create(dst, - &tdmi_src->tdmi_conf, - tdmi_src->tdmi_transport_stream_id, - tdmi_src->tdmi_network, - "copy operation", tdmi_src->tdmi_enabled, - NULL); - - - assert(tdmi_dst != NULL); - - LIST_FOREACH(t_src, &tdmi_src->tdmi_transports, tht_group_link) { - t_dst = dvb_transport_find(tdmi_dst, - t_src->tht_dvb_service_id, - t_src->tht_pmt_pid, NULL); - - t_dst->tht_pcr_pid = t_src->tht_pcr_pid; - t_dst->tht_enabled = t_src->tht_enabled; - t_dst->tht_servicetype = t_src->tht_servicetype; - t_dst->tht_scrambled = t_src->tht_scrambled; - - if(t_src->tht_provider != NULL) - t_dst->tht_provider = strdup(t_src->tht_provider); - - if(t_src->tht_svcname != NULL) - t_dst->tht_svcname = strdup(t_src->tht_svcname); - - if(t_src->tht_ch != NULL) - transport_map_channel(t_dst, t_src->tht_ch, 0); - - pthread_mutex_lock(&t_src->tht_stream_mutex); - - LIST_FOREACH(st_src, &t_src->tht_components, st_link) { - - st_dst = transport_stream_create(t_dst, - st_src->st_pid, - st_src->st_type); - - st_dst->st_tb = (AVRational){1, 90000}; - - memcpy(st_dst->st_lang, st_src->st_lang, 4); - st_dst->st_frame_duration = st_src->st_frame_duration; - st_dst->st_caid = st_src->st_caid; - } - - pthread_mutex_unlock(&t_src->tht_stream_mutex); - t_dst->tht_config_save(t_dst); // Save config - - } - dvb_mux_save(tdmi_dst); - } tda_save(dst); } + /** * */ diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 1022d99a..0598a551 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -875,3 +875,69 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, return NULL; } + +/** + * + */ +int +dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src) +{ + th_dvb_mux_instance_t *tdmi_dst; + th_transport_t *t_src, *t_dst; + th_stream_t *st_src, *st_dst; + + tdmi_dst = dvb_mux_create(dst, + &tdmi_src->tdmi_conf, + tdmi_src->tdmi_transport_stream_id, + tdmi_src->tdmi_network, + "copy operation", tdmi_src->tdmi_enabled, + NULL); + + if(tdmi_dst == NULL) + return -1; // Already exist + + LIST_FOREACH(t_src, &tdmi_src->tdmi_transports, tht_group_link) { + t_dst = dvb_transport_find(tdmi_dst, + t_src->tht_dvb_service_id, + t_src->tht_pmt_pid, NULL); + + t_dst->tht_pcr_pid = t_src->tht_pcr_pid; + t_dst->tht_enabled = t_src->tht_enabled; + t_dst->tht_servicetype = t_src->tht_servicetype; + t_dst->tht_scrambled = t_src->tht_scrambled; + + if(t_src->tht_provider != NULL) + t_dst->tht_provider = strdup(t_src->tht_provider); + + if(t_src->tht_svcname != NULL) + t_dst->tht_svcname = strdup(t_src->tht_svcname); + + if(t_src->tht_ch != NULL) + transport_map_channel(t_dst, t_src->tht_ch, 0); + + pthread_mutex_lock(&t_src->tht_stream_mutex); + pthread_mutex_lock(&t_dst->tht_stream_mutex); + + LIST_FOREACH(st_src, &t_src->tht_components, st_link) { + + + st_dst = transport_stream_create(t_dst, + st_src->st_pid, + st_src->st_type); + + st_dst->st_tb = (AVRational){1, 90000}; + + memcpy(st_dst->st_lang, st_src->st_lang, 4); + st_dst->st_frame_duration = st_src->st_frame_duration; + st_dst->st_caid = st_src->st_caid; + } + + pthread_mutex_unlock(&t_dst->tht_stream_mutex); + pthread_mutex_unlock(&t_src->tht_stream_mutex); + + t_dst->tht_config_save(t_dst); // Save config + + } + dvb_mux_save(tdmi_dst); + return 0; +} diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 9b585f09..fc426a23 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -946,9 +946,10 @@ static int extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) { htsbuf_queue_t *hq = &hc->hc_reply; - th_dvb_adapter_t *tda; + th_dvb_adapter_t *tda, *ref; htsmsg_t *out, *array, *r; const char *op = http_arg_get(&hc->hc_req_args, "op"); + const char *sibling = http_arg_get(&hc->hc_req_args, "sibling"); const char *s, *sc; th_dvb_mux_instance_t *tdmi; th_transport_t *t; @@ -958,11 +959,14 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) if(remain == NULL) { /* Just list all adapters */ + ref = sibling != NULL ? dvb_adapter_find_by_identifier(sibling) : NULL; + array = htsmsg_create_list(); - TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) - htsmsg_add_msg(array, NULL, dvb_adapter_build_msg(tda)); - + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { + if(ref == NULL || (ref != tda && ref->tda_type == tda->tda_type)) + htsmsg_add_msg(array, NULL, dvb_adapter_build_msg(tda)); + } pthread_mutex_unlock(&global_lock); out = htsmsg_create_map(); htsmsg_add_msg(out, "entries", array); @@ -1504,6 +1508,62 @@ extjs_dvb_addmux(http_connection_t *hc, const char *remain, void *opaque) } +/** + * + */ +static int +extjs_dvb_copymux(http_connection_t *hc, const char *remain, void *opaque) +{ + htsmsg_t *out; + htsbuf_queue_t *hq = &hc->hc_reply; + th_dvb_adapter_t *tda; + htsmsg_t *in; + const char *entries = http_arg_get(&hc->hc_req_args, "entries"); + const char *id; + htsmsg_field_t *f; + th_dvb_mux_instance_t *tdmi; + + in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL; + + if(in == NULL) + return 400; + + pthread_mutex_lock(&global_lock); + + if(remain == NULL || + (tda = dvb_adapter_find_by_identifier(remain)) == NULL) { + pthread_mutex_unlock(&global_lock); + return 404; + } + + + TAILQ_FOREACH(f, &in->hm_fields, hmf_link) { + if((id = htsmsg_field_get_string(f)) != NULL && + (tdmi = dvb_mux_find_by_identifier(id)) != NULL && + tda != tdmi->tdmi_adapter) { + + if(dvb_mux_copy(tda, tdmi)) { + char buf[100]; + dvb_mux_nicename(buf, sizeof(buf), tdmi); + + tvhlog(LOG_NOTICE, "DVB", + "Skipped copy of mux %s to adapter %s, mux already exist", + buf, tda->tda_displayname); + } + } + } + + pthread_mutex_unlock(&global_lock); + + out = htsmsg_create_map(); + htsmsg_json_serialize(out, hq, 0); + http_output_content(hc, "text/x-json; charset=UTF-8"); + htsmsg_destroy(out); + + return 0; +} + + /** * */ @@ -1737,6 +1797,9 @@ extjs_start(void) http_path_add("/dvb/addmux", NULL, extjs_dvb_addmux, ACCESS_ADMIN); + http_path_add("/dvb/copymux", + NULL, extjs_dvb_copymux, ACCESS_ADMIN); + http_path_add("/iptv/services", NULL, extjs_iptvservices, ACCESS_ADMIN); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 8fb75dff..fd4300a6 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -194,6 +194,78 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) { }) } } + + + function copySelected() { + + function doCopy() { + var selectedKeys = grid.selModel.selections.keys; + var target = panel.getForm().getValues('targetID').targetID; + + Ext.Ajax.request({ + url: "dvb/copymux/" + target, + params: { + entries:Ext.encode(selectedKeys) + }, + failure:function(response,options) { + Ext.MessageBox.alert('Server Error','Unable to copy'); + }, + success: function() { + win.close(); + } + }); + } + + targetStore = new Ext.data.JsonStore({ + root:'entries', + id: 'identifier', + url:'dvb/adapter', + fields: ['identifier', + 'name'], + baseParams: {sibling: adapterId} + }); + + var panel = new Ext.FormPanel({ + frame:true, + border:true, + bodyStyle:'padding:5px', + labelAlign: 'right', + labelWidth: 110, + defaultType: 'textfield', + items: [ + + new Ext.form.ComboBox({ + store: targetStore, + fieldLabel: 'Target adapter', + name: 'targetadapter', + hiddenName: 'targetID', + editable: false, + allowBlank: false, + triggerAction: 'all', + mode: 'remote', + displayField:'name', + valueField:'identifier', + emptyText: 'Select target adapter...' + }) + ], + buttons: [{ + text: 'Copy', + handler: doCopy + }] + }); + + win = new Ext.Window({ + title: 'Copy multiplex configuration', + layout: 'fit', + width: 500, + height: 120, + modal: true, + plain: true, + items: panel + }); + win.show(); + } + function saveChanges() { var mr = store.getModifiedRecords(); @@ -226,13 +298,22 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) { var delBtn = new Ext.Toolbar.Button({ tooltip: 'Delete one or more selected muxes', iconCls:'remove', - text: 'Delete selected', + text: 'Delete selected...', handler: delSelected, disabled: true }); + var copyBtn = new Ext.Toolbar.Button({ + tooltip: 'Copy selected multiplexes to other adapter', + iconCls:'clone', + text: 'Copy to other adapter...', + handler: copySelected, + disabled: true + }); + selModel.on('selectionchange', function(s) { delBtn.setDisabled(s.getCount() == 0); + copyBtn.setDisabled(s.getCount() == 0); }); var saveBtn = new Ext.Toolbar.Button({ @@ -263,8 +344,8 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) { viewConfig: {forceFit:true}, selModel: selModel, tbar: [ - delBtn, '-', saveBtn, rejectBtn, '-', { - text: 'Add mux(es) manually', + delBtn, copyBtn, '-', saveBtn, rejectBtn, '-', { + text: 'Add mux(es) manually...', iconCls:'add', handler: function() { tvheadend.addMuxManually(adapterData, satConfStore) diff --git a/src/webui/static/app/ext.css b/src/webui/static/app/ext.css index 6813760f..31dde14a 100644 --- a/src/webui/static/app/ext.css +++ b/src/webui/static/app/ext.css @@ -189,6 +189,10 @@ background-image:url(../icons/world.png) !important; } +.clone { + background-image:url(../icons/layers.png) !important; +} + .x-smallhdr { diff --git a/src/webui/static/icons/layers.png b/src/webui/static/icons/layers.png new file mode 100644 index 00000000..00818f63 Binary files /dev/null and b/src/webui/static/icons/layers.png differ