import logging from datetime import date, datetime, timedelta from itertools import islice from typing import List import requests from epg2xml.providers import EPGProgram, EPGProvider log = logging.getLogger(__name__.rsplit(".", maxsplit=1)[-1].upper()) today = date.today() PRIORITY_IMG_CODE = ["CAIC2300", "CAIC1600", "CAIC0100", "CAIC0400"] G_CODE = { "CPTG0100": 0, "CPTG0200": 7, "CPTG0300": 12, "CPTG0400": 15, "CPTG0500": 19, "CMMG0100": 0, "CMMG0200": 12, "CMMG0300": 15, "CMMG0400": 19, } class TVING(EPGProvider): """EPGProvider for TVING 데이터: jsonapi 요청수: #channels/20 * #days * 24/3 특이사항: - 최대 20채널 최대 3시간 허용 """ referer = "https://www.tving.com/schedule/main.do" tps = 3.0 url = "https://api.tving.com/v2/media/schedules" base_params = { "pageNo": "1", "pageSize": "20", # maximum 20 "order": "chno", "scope": "all", "adult": "all", "free": "all", "broadDate": "20200608", "broadcastDate": "20200608", "startBroadTime": "030000", # 최대 3시간 간격 "endBroadTime": "060000", # "channelCode": "C06941,C07381,...", "screenCode": "CSSD0100", "networkCode": "CSND0900", "osCode": "CSOD0900", "teleCode": "CSCD0900", "apiKey": "1e7952d0917d6aab1f0293a063697610", } def __params(self, **params) -> dict: """returns url parameters for api requests with base ones""" p = self.base_params.copy() p.update(params) return p def __get(self, url: str, **kwargs) -> List[dict]: params = self.__params(**kwargs.pop("params", {})) _page = 1 _results = [] while True: params["pageNo"] = str(_page) _data = self.request(url=url, params=params, **kwargs) if _data["header"]["status"] != 200: raise requests.exceptions.RequestException _results.extend(_data["body"]["result"]) if _data["body"]["has_more"] == "Y": _page += 1 else: break return _results def get_svc_channels(self) -> List[dict]: def get_imgurl(_item: dict): for _code in PRIORITY_IMG_CODE: try: img_list = [x for x in _item["image"] if x["code"] == _code] if not img_list: continue return "https://image.tving.com" + (img_list[0].get("url") or img_list[0]["url2"]) except Exception: pass return None params = { "broadDate": today.strftime("%Y%m%d"), "broadcastDate": today.strftime("%Y%m%d"), "startBroadTime": datetime.now().strftime("%H0000"), "endBroadTime": (datetime.now() + timedelta(hours=3)).strftime("%H0000"), } return [ { "Name": x["channel_name"]["ko"], "Icon_url": get_imgurl(x), "ServiceId": x["channel_code"], "Category": x["schedules"][0]["channel"]["category_name"]["ko"], } for x in self.__get(self.url, params=params) if x["schedules"] is not None ] def get_programs(self) -> None: def grouper(iterable, n): it = iter(iterable) group = tuple(islice(it, n)) while group: yield group group = tuple(islice(it, n)) for gid, chgroup in enumerate(grouper(self.req_channels, 20)): schdict = {} params = {"channelCode": ",".join([x.svcid.strip() for x in chgroup])} for nd in range(int(self.cfg["FETCH_LIMIT"])): day = today + timedelta(days=nd) params.update({"broadDate": day.strftime("%Y%m%d"), "broadcastDate": day.strftime("%Y%m%d")}) for t in range(8): params.update({"startBroadTime": f"{t*3:02d}0000", "endBroadTime": f"{t*3+3:02d}0000"}) for ch in self.__get(self.url, params=params): chcode = ch["channel_code"] schdict.setdefault(chcode, []) toappend = ch.get("schedules") or [] try: # 3시간 단위로 요청된 스케줄 앞 뒤로 중복이 있을 수 있다. if schdict[chcode][-1] == toappend[0]: toappend = toappend[1:] except Exception: pass schdict[chcode] += toappend for idx, _ch in enumerate(chgroup): log.info("%03d/%03d %s", gid * 20 + idx + 1, len(self.req_channels), _ch) try: _epgs = self.__epgs_of_channel(_ch.id, schdict[_ch.svcid]) except Exception: log.exception("프로그램 파싱 중 예외: %s", _ch) else: _ch.programs.extend(_epgs) def __epgs_of_channel(self, channelid: str, schedules: List[dict]) -> List[EPGProgram]: _epgs = [] for sch in schedules: _epg = EPGProgram(channelid) # 공통 _epg.stime = datetime.strptime(str(sch["broadcast_start_time"]), "%Y%m%d%H%M%S") _epg.etime = datetime.strptime(str(sch["broadcast_end_time"]), "%Y%m%d%H%M%S") _epg.rebroadcast = sch["rerun_yn"] == "Y" get_from = "movie" if sch["movie"] else "program" img_code = "CAIM2100" if sch["movie"] else "CAIP0900" _epg.rating = G_CODE[sch[get_from].get("grade_code", "CPTG0100")] _epg.title = sch[get_from]["name"]["ko"] _epg.title_sub = sch[get_from]["name"].get("en") if cate1 := sch[get_from]["category1_name"].get("ko"): _epg.categories = [cate1] if cate2 := sch[get_from]["category2_name"].get("ko"): _epg.categories = (_epg.categories or []) + [cate2] _epg.cast = [{"name": x, "title": "actor"} for x in sch[get_from]["actor"]] _epg.crew = [{"name": x, "title": "director"} for x in sch[get_from]["director"]] poster = [x["url"] for x in sch[get_from]["image"] if x["code"] == img_code] if poster: _epg.poster_url = "https://image.tving.com" + poster[0] # _prog.poster_url += '/dims/resize/236' _epg.desc = sch[get_from]["story" if sch["movie"] else "synopsis"]["ko"] if sch["episode"]: episode = sch["episode"]["frequency"] _epg.ep_num = "" if episode == 0 else str(episode) _epg.desc = sch["episode"]["synopsis"]["ko"] _epgs.append(_epg) return _epgs