import logging from datetime import date, datetime, timedelta from typing import List from epg2xml.providers import EPGProgram, EPGProvider, no_endtime log = logging.getLogger(__name__.rsplit(".", maxsplit=1)[-1].upper()) G_CODE = {"0": 0, "1": 7, "2": 12, "3": 15, "4": 19} P_CATE = { "00": "영화", "02": "만화", "03": "드라마", "05": "스포츠", "06": "교육", "07": None, # 어린이/교육 "08": "연예/오락", "09": "공연/음악", "10": None, # 게임 "11": "다큐", "12": "뉴스/정보", "13": "라이프", "15": None, # 홈쇼핑 "16": None, # 경제/부동산 "31": "기타", } class LG(EPGProvider): """EPGProvider for LG 데이터: jsonapi 요청수: #channels * #days 특이사항: - 5일치만 제공 - 프로그램 시작 시각만 제공 참고: - 사이트 리뉴얼 이후 프로그램 카테고리가 아직 명확히 정해지지 않은 듯 하다. """ referer = "https://www.lguplus.com/iptv/channel-guide" title_regex = r"\s?(?:\[.*?\])?(.*?)(?:\[(.*)\])?\s?(?:\(([\d,]+)회\))?\s?(<재>)?$" def get_svc_channels(self) -> List[dict]: svc_channels = [] url = "https://www.lguplus.com/uhdc/fo/prdv/chnlgid/v1/tv-schedule-list" data = self.request(url) cate = {x["urcBrdCntrTvChnlGnreCd"]: x["urcBrdCntrTvChnlGnreNm"] for x in data["brdGnreDtoList"]} for ch in self.request(url)["brdCntrTvChnlIDtoList"]: svc_channels.append( { "Name": ch["urcBrdCntrTvChnlDscr"], "No": ch["urcBrdCntrTvChnlNo"], "ServiceId": ch["urcBrdCntrTvChnlId"], "Category": cate[ch["urcBrdCntrTvChnlGnreCd"]], } ) return svc_channels @no_endtime def get_programs(self) -> None: max_ndays = 5 if int(self.cfg["FETCH_LIMIT"]) > max_ndays: log.warning( """ *********************************************************************** %s는 당일포함 %d일치만 EPG를 제공하고 있습니다. *********************************************************************** """, self.provider_name, max_ndays, ) url = "https://www.lguplus.com/uhdc/fo/prdv/chnlgid/v1/tv-schedule-list" params = {"urcBrdCntrTvChnlId": "SVCID", "brdCntrTvChnlBrdDt": "EPGDATE"} for idx, _ch in enumerate(self.req_channels): log.info("%03d/%03d %s", idx + 1, len(self.req_channels), _ch) for nd in range(min(int(self.cfg["FETCH_LIMIT"]), max_ndays)): day = date.today() + timedelta(days=nd) params.update({"urcBrdCntrTvChnlId": _ch.svcid, "brdCntrTvChnlBrdDt": day.strftime("%Y%m%d")}) data = self.request(url, params=params) or {} data = data.get("brdCntTvSchIDtoList", []) if not data: log.warning("EPG 정보가 없거나 없는 채널입니다: %s %s", _ch, day) break # 오늘 없으면 내일도 없는 채널로 간주 try: _epgs = self.__epgs_of_day(_ch.id, data) except Exception: log.exception("프로그램 파싱 중 예외: %s, %s", _ch, day) else: _ch.programs.extend(_epgs) def __epgs_of_day(self, channelid: str, data: list) -> List[EPGProgram]: _epgs = [] for p in data: _epg = EPGProgram(channelid) _epg.title = p["brdPgmTitNm"] _epg.desc = p["brdPgmDscr"] _epg.stime = datetime.strptime(p["brdCntrTvChnlBrdDt"] + p["epgStrtTme"], "%Y%m%d%H:%M:%S") _epg.rating = G_CODE.get(p["brdWtchAgeGrdCd"], 0) _epg.extras = [p["brdPgmRsolNm"]] # 화질 if p["subtBrdYn"] == "Y": _epg.extras.append("자막") if p["explBrdYn"] == "Y": _epg.extras.append("화면해설") if p["silaBrdYn"] == "Y": _epg.extras.append("수화") if m := self.title_regex.match(_epg.title): _epg.title = m.group(1) _epg.title_sub = m.group(2) _epg.ep_num = m.group(3) _epg.rebroadcast = bool(m.group(4)) if P_CATE[p["urcBrdCntrTvSchdGnreCd"]]: _epg.categories = [P_CATE[p["urcBrdCntrTvSchdGnreCd"]]] _epgs.append(_epg) return _epgs