MCPcopy Create free account
hub / github.com/Dispatcharr/Dispatcharr / build_programme_index

Function build_programme_index

apps/epg/tasks.py:4486–4580  ·  view source on GitHub ↗

Scan the XML file with raw binary I/O to build a {tvg_id: [byte_offset, ...]} map. Persists the result to the EPGSourceIndex table. Most XMLTV files group programmes by channel, but some split a channel across multiple non-contiguous blocks, so we record block starts up to _OFFSET_C

(source_id)

Source from the content-addressed store, hash-verified

4484
4485
4486def build_programme_index(source_id):
4487 """
4488 Scan the XML file with raw binary I/O to build a {tvg_id: [byte_offset, ...]} map.
4489 Persists the result to the EPGSourceIndex table. Most XMLTV files group programmes
4490 by channel, but some split a channel across multiple non-contiguous blocks, so we
4491 record block starts up to _OFFSET_CAP and mark only channels that exceed the cap
4492 as interleaved.
4493 """
4494 try:
4495 source = EPGSource.objects.get(id=source_id)
4496 except EPGSource.DoesNotExist:
4497 logger.error(f'[build_programme_index] EPGSource {source_id} not found')
4498 return
4499
4500 file_path = _resolve_source_file(source)
4501 if not file_path or not os.path.exists(file_path):
4502 logger.warning(
4503 f'[build_programme_index] File not found for source {source_id}: {file_path}'
4504 )
4505 return
4506
4507 logger.debug(
4508 f'[build_programme_index] Building byte-offset index for source {source_id} from {file_path}'
4509 )
4510 start = time.monotonic()
4511 index = {}
4512 prev_channel = None
4513 interleaved_channels = set()
4514
4515 CHUNK = 8 * 1024 * 1024 # 8MB
4516
4517 with open(file_path, 'rb') as f:
4518 buf = bytearray()
4519 buf_offset = 0 # absolute file offset of buf[0]
4520
4521 while True:
4522 chunk = f.read(CHUNK)
4523 if not chunk and not buf:
4524 break
4525 buf.extend(chunk)
4526 search_from = 0
4527
4528 while True:
4529 idx, tag_end = _find_programme_tag(buf, search_from)
4530 if idx == -1:
4531 break
4532 if tag_end == -1 and chunk:
4533 break # incomplete tag at buffer edge, need more data
4534
4535 abs_pos = buf_offset + idx
4536 m = _CHANNEL_ATTR_RE.search(
4537 buf, idx, tag_end + 1 if tag_end != -1 else idx + _MAX_START_TAG
4538 )
4539 if m:
4540 channel_id = _decode_channel_id(m.group(1) or m.group(2))
4541 if channel_id not in index:
4542 index[channel_id] = [abs_pos]
4543 elif channel_id != prev_channel:

Calls 8

_resolve_source_fileFunction · 0.85
_find_programme_tagFunction · 0.85
_decode_channel_idFunction · 0.85
readMethod · 0.80
extendMethod · 0.80
searchMethod · 0.80
getMethod · 0.45
existsMethod · 0.45