#!/usr/bin/env python # # Migration version 3 configuration to version 4 # # Files that need updating: # autorec/* : Auto-mapped by code, but mapped here as well # dvr/log/* : Auto-mapped by code, but mapped here as well # channels/* : map services # dvb/* : map network/mux but nothing else # epggrab/otamux : remove # epggrab/*/channels/* : update channels # # Create new "version" file to validate config version # # # Imports # import os, sys, re, json, glob import pprint from optparse import OptionParser # # Utilities # # Generate UUID def uuid (): import uuid return uuid.uuid4().hex # # DVB - input # # Adapters def load_adapters ( path ): adps = {} for f in glob.glob(os.path.join(path, 'dvbadapters', '*')): s = open(f).read() d = json.loads(s) t = d['type'] if t.startswith('DVB'): t = t[4] else: t = 'A' adps[os.path.basename(f)] = { 'type' : t, 'nets' : {} } # Done return adps # Muxes def load_muxes ( path, adps ): maps = { 'transportstreamid' : 'tsid', 'originalnetworkid' : 'onid', 'initialscan' : 'initscan', 'default_authority' : 'cridauth', 'delivery_system' : 'delsys', 'symbol_rate' : 'symbolrate' } muxs = {} for f in glob.glob(os.path.join(path, 'dvbmuxes', '*', '*')): a = os.path.basename(os.path.dirname(f)) if a not in adps: continue t = adps[a]['type'] s = open(f).read() d = json.loads(s) if not 'transportstreamid' in d: continue tsid = d['transportstreamid'] onid = d['originalnetworkid'] if 'transportstreamid' in d else None # Build m = {} for k in d: if k in maps: m[maps[k]] = d[k] else: m[k] = d[k] m['type'] = t m['key'] = k = '%s:%04X:%04X' % (t, onid, tsid) m['svcs'] = {} # Fixups if 'delsys' in m: m['delsys'] = m['delsys'][4:] elif t == 'A': m['delsys'] = 'ATSC' elif t == 'T': m['delsys'] = 'DVBT' elif t == 'C': m['delsys'] = 'DVBC_ANNEX_AC' elif t == 'S': m['delsys'] = 'DVBS' if 'polarisation' in m: m['polarisation'] = m['polarisation'][0] if 'modulation' in m and m['polarisation'] == 'PSK_8': m['modulation'] = '8PSK' if 'rolloff' in m: m['rolloff'] = m['rolloff'][8:] # Store muxs[os.path.basename(f)] = m # Network n = None if 'satconf' in d: n = d['satconf'] if n not in adps[a]['nets']: adps[a]['nets'][n] = {} adps[a]['nets'][n][k] = m # Done return muxs # Servies def load_services ( path, muxs ): svcs = {} maps = { 'service_id' : 'sid', 'servicename' : 'svcname', 'stype' : 'dvb_servicetype', 'channel' : 'lcn', 'default_authority' : 'cridauth' } for f in glob.glob(os.path.join(path, 'dvbtransports', '*', '*')): m = os.path.basename(os.path.dirname(f)) if m not in muxs: continue # Load data m = muxs[m] s = open(f).read() d = json.loads(s) # Validate if 'service_id' not in d: continue if 'stream' in d: del d['stream'] # Map fields s = {} for k in d: if k in maps: s[maps[k]] = d[k] else: s[k] = d[k] k = '%s:%04X:%04X:%04X' % (m['type'], m['onid'], m['tsid'], s['sid']) m['svcs'][k] = svcs[k] = s return svcs # # Channels # def load_channels ( path, nets ): maps = { 'channel_number' : 'number', 'dvr_extra_time_pre' : 'dvr_pre_time', 'dvr_extra_time_pst' : 'dvr_pst_time', } chns = {} # Process channels for f in glob.glob(os.path.join(path, 'channels', '*')): # Load data s = open(f).read() d = json.loads(s) c = { 'services' : [] } # Map fields for k in d: if k in maps: c[maps[k]] = d[k] else: c[k] = d[k] # Find services for n in nets: for m in n['muxs']: m = n['muxs'][m] for s in m['svcs']: s = m['svcs'][s] if 'uuid' not in s: continue if 'channelname' in s and s['channelname'] == c['name']: c['services'].append(s['uuid']) # Store chns[int(os.path.basename(f))] = c # Done return chns # # IPTV # def iptv_network ( nets, opts ): muxes = {} for f in glob.glob(os.path.join(path, 'iptvservices', '*')): s = open(f).read() d = json.loads(s) url = '%s://%s:%d' % (opts.iptv, d['group'], d['port']) if url not in muxes: e = 'disabled' not in d or not d['disabled'] i = '' if 'interface' in d: i = d['interface'] muxes[url] = m = { 'iptv_url' : url, 'enabled' : e, 'svcs' : {}, 'iptv_interface': i } else: m = muxes[url] # Create service entry if 'channelname' in d: d['svcname'] = d['channelname'] else: d['svcname'] = '' d['dvb_servicetype'] = d['stype'] d['sid'] = 1 # Let's hope! d['enabled'] = e m['svcs'] = { '1' : d } # Remove for f in [ 'stream', 'group', 'stype', 'interface' ]: if f in d: del d[f] nets.append({ 'type' : 'iptv', 'muxs' : muxes,\ 'skipinitscan' : True,\ 'autodiscovery' : False }) return nets # # Output # # Build networks def build_networks ( adps, opts ): nets = [] # Process each adapter for a in adps: a = adps[a] # Process each network for m in a['nets']: m = a['nets'][m] f = False # Look for overlap and combine for n in nets: if n['type'] != a['type']: continue x = set(n['muxs'].keys()) y = set(m.keys()) i = x.intersection(x, y) c = (2.0 * len(i)) / (len(x) + len(y)) #print 'comp %d %d %d %f' % (len(x), len(y), len(i), c) if c > 0.5: f = True for k in m: if k not in n['muxs']: n['muxs'][k] = m[k] else: n['muxs'][k]['svcs'].update(m[k]['svcs']) if not f: n = { 'type': a['type'], 'muxs': m } nets.append(n) # Set network name i = 0 for n in nets: i = i + 1 names = {} for m in n['muxs']: m = n['muxs'][m] if 'network' not in m: continue if m['network'] not in names: names[m['network']] = 1 else: names[m['network']] = 1 + names[m['network']] name = None for na in names: if not name: name = na elif names[na] > names[name]: name = na if name: n['name'] = name else: n['name'] = 'Network %s %d' % (n['type'], i) # Done return nets # Output services def output_services ( path, svcs, opts ): ignore = { 'type', 'key', 'channelname', 'mapped' } if not os.path.exists(path): os.makedirs(path) # Process services for s in svcs: s = svcs[s] # Copy d = {} for k in s: if k not in ignore: d[k] = s[k] # Output u = uuid() spath = os.path.join(path, u) open(os.path.join(spath), 'w').write(json.dumps(d, indent=2)) s['uuid'] = u # Output muxes def output_muxes ( path, muxs, opts ): ignore = [ 'type', 'key', 'svcs', 'network', 'quality', 'status' ] # Process each for m in muxs: m = muxs[m] # Copy d = {} for k in m: if k not in ignore: d[k] = m[k] # Output u = uuid() mpath = os.path.join(path, u) os.makedirs(mpath) open(os.path.join(mpath, 'config'), 'w').write(json.dumps(d, indent=2)) # Services output_services(os.path.join(mpath, 'services'), m['svcs'], opts) # Output networks def output_networks ( path, nets, opts ): # Ensure dir exists path = os.path.join(path, 'input', 'linuxdvb', 'networks') if not os.path.exists(path): os.makedirs(path) # Write each network for n in nets: # Network config if n['type'] == 'A': c = 'linuxdvb_network_atsc' else: c = 'linuxdvb_network_dvb' + n['type'].lower() d = { 'networkname' : n['name'], 'nid' : 0, 'autodiscovery' : False, 'skipinitscan' : True, 'class' : c } # Write u = uuid() npath = os.path.join(path, u) os.mkdir(npath) open(os.path.join(npath, 'config'), 'w').write(json.dumps(d, indent=2)) # Muxes output_muxes(os.path.join(npath, 'muxes'), n['muxs'], opts) # Output IPTV def output_iptv ( path, nets, opts ): d = None # Find for n in nets: if n['type'] == 'iptv': d = n if not d: return # Ensure dir exists path = os.path.join(path, 'input', 'iptv') if not os.path.exists(path): os.makedirs(path) # Write u = uuid() n = { 'uuid' : u, 'skipinitscan' : True, 'autodiscovery' : False } open(os.path.join(path, 'config'), 'w').write(json.dumps(n, indent=2)) # Muxes output_muxes(os.path.join(path, 'muxes'), d['muxs'], opts) # Channels def output_channels ( path, chns, opts ): path = os.path.join(path, 'channel') if not os.path.exists(path): os.makedirs(path) # Each channels for c in chns: c = chns[c] u = uuid() # Output open(os.path.join(path, u), 'w').write(json.dumps(c, indent=2)) # Store c['uuid'] = u # # DVR # # Find channel by name def find_channel_by_name ( chns, name ): for c in chns: c = chns[c] if 'name' in c and c['name'] == name: return c return None def update_dvr ( path, chns ): # Update all DVR log entries for f in glob.glob(os.path.join(path, 'dvr', 'log', '*')): # Load s = open(f).read() d = json.loads(s) # Already done if 'channelname' in d: continue # Update all channels if 'channel' in d: d['channelname'] = d['channel'] c = find_channel_by_name(chns, d['channelname']) if c and 'uuid' in c: d['channel'] = c['uuid'] # Save open(f, 'w').write(json.dumps(d, indent=2)) # Update autorec rules for f in glob.glob(os.path.join(path, 'autorec', '*')): # Load s = open(f).read() d = json.loads(s) # Update all channels if 'channel' in d: d['channelname'] = d['channel'] c = find_channel_by_name(chns, d['channelname']) if c and 'uuid' in c: d['channel'] = c['uuid'] # Save open(f, 'w').write(json.dumps(d, indent=2)) # # EPG grab # def update_epg ( path, chns ): # Remove otamux p = os.path.join(path, 'epggrab', 'otamux') if os.path.isfile(p): os.unlink(p) # Remove epgdb? # Map grab channels for f in glob.glob(os.path.join(path, 'epggrab', '*', 'channels', '*')): # Load s = open(f).read() d = json.loads(s) # Update all channels t = [] if 'channels' in d: for c in d['channels']: if c in chns and 'uuid' in chns[c]: t.append(chns[c]['uuid']) d['channels'] = t # Save open(f, 'w').write(json.dumps(d, indent=2)) # # Main # optp = OptionParser() optp.add_option('-o', '--overlap', type='float', default=0.5, help='Percentage overlap at which networks considered same') optp.add_option('-i', '--iptv', type='string', default='udp', help='IPTV input type, rtp/udp') (opts,args) = optp.parse_args() path = args[0] adps = load_adapters(path) muxs = load_muxes(path, adps) svcs = load_services(path, muxs) nets = build_networks(adps, opts) output_networks(path, nets, opts) nets = iptv_network(nets, opts) output_iptv(path, nets, opts) chns = load_channels(path, nets) output_channels(path, chns, opts) update_dvr(path, chns) update_epg(path, chns) sys.exit(0)