diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..a88851907 Binary files /dev/null and b/.DS_Store differ diff --git a/.github/.DS_Store b/.github/.DS_Store new file mode 100644 index 000000000..7d35036c5 Binary files /dev/null and b/.github/.DS_Store differ diff --git a/.github/workflows/.DS_Store b/.github/workflows/.DS_Store new file mode 100644 index 000000000..5008ddfcf Binary files /dev/null and b/.github/workflows/.DS_Store differ diff --git a/.github/workflows/EPG.yml b/.github/workflows/EPG.yml new file mode 100644 index 000000000..12c672cb2 --- /dev/null +++ b/.github/workflows/EPG.yml @@ -0,0 +1,53 @@ +name: Make IPTV EPG +on: + # push: + # branches: + # - main + schedule: + - cron: "50 6 * * *" + - cron: "30 16 * * *" + workflow_dispatch: +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install xmltv + run: | + cp /usr/share/zoneinfo/Asia/Taipei /etc/localtime + sudo apt -y install xmltv + cd ./EPG + pip3 install -r requirements.txt + - name: Make EPG + run: | + cd ./EPG + python3 ./gen_xml.py + mkdir xml + mv epg0.xml xml + mv README.md xml + mv PhoenixMoviesChannel.xml xml + cd xml + wget https://e.erw.cc/all.xml + wget https://raw.githubusercontent.com/taksssss/tv/refs/heads/main/epg/livednow.xml + - name: Merge EPG + run: | + cd ./EPG/xml + tv_cat all.xml livednow.xml epg0.xml PhoenixMoviesChannel.xml > EPG.xml + gzip -kf EPG.xml + # - name : Upload artifact + # uses: actions/upload-artifact@master + # with: + # name: allcc.xml + # path: allcc.xml + - name: Git push assets to "EPG" branch + run: | + cd ./EPG/xml + git init + git config --local user.name "actions" + git config --local user.email "action@github.com" + git checkout -b EPG + git add . + git commit -m "Update EPG" + git remote add origin "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}" + git push -f -u origin EPG diff --git a/.github/workflows/kctv_elufa.yml b/.github/workflows/kctv_elufa.yml deleted file mode 100644 index 39d988e94..000000000 --- a/.github/workflows/kctv_elufa.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Get kctv_elufa m3u8 -on: - schedule: - - cron: "0 */2 * * *" - workflow_dispatch: -jobs: - build: - runs-on: ubuntu-22.04 - steps: - - name: Install yt-dlp - run: | - sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp - sudo chmod a+rx /usr/local/bin/yt-dlp - - name: Get kctv_elufa m3u8 - run: | - touch ./kctv_elufa.m3u8 - sudo cat >./kctv_elufa.m3u8 < + + + 鳳凰衛視電影台 + + + 盧納莎之舞 + + + + 大地的女兒 + + + + 隨心所欲 + + + + 戰火實錄 + + + + 倩女幽魂 + + + + 情不自禁愛上你 + + + + 環球電影大觀:冬天的訪客 + + + + 阿郎的故事 + + + + 雪國機械師 + + + + 畢業證書 + + + + 記憶傳授人 + + + + 周日五星影院:王牌對王牌 + + + + 寰宇國際影院:非常時期 + + + + 荒蠻故事 + + + + 賽琳娜 + + + + 女學生和亨利先生 + + + + 清白 + + + + 莫迪與畢加索 + + + + 笑傲江湖 + + + + 環球電影大觀:內神外鬼 + + + + 冠軍 + + + + 縱橫四海 + + + + 在公交車站直到黎明 + + + + 吉米的舞廳 + + + + 動感天下:窗台上的男人 + + + + 寰宇國際影院:看不見的客人 + + + + 神鬼獵人:在結冰的路上 + + + + 小好、小麻、佐和子 + + + + 墨菲的戰爭 + + + + 安娜,我的愛 + + + + 喋血雙雄 + + + + 九三之夏 + + + + 環球電影大觀:長翅膀的猪 + + + + 戰火球星 + + + + 刺殺肯尼迪 + + + + 窗台上的男人 + + + + 中間時代 + + + + 亞洲本色:東經北緯 + + + + 寰宇國際影院:羅姆男孩 + + + + 小森林 冬春篇 + + + + 瞞天計畫 + + + + 燈塔看守人 + + + + 更好的世界 + + + + 我們來了 + + + + 不痛的死法 + + + + 環球電影大觀:滅罪拍檔 + + + + 怒海沉屍 + + + + 盧納莎之舞 + + + + 大象 + + + + 索爾之子 + + + + 緣份的天空:王者之心 + + + + 寰宇國際影院:我是殺人犯 + + + + 小森林 夏秋篇 + + + + 内神外鬼 + + + + 賽琳娜 + + + + 荒蠻故事 + + + + 我啊,走自己的路 + + + + 在緣茵場上 + + + + 環球電影大觀:法或精爸 + + + + 墨菲的戰爭 + + + + 隨心所欲 + + + + 諑鵝 + + + + 粉色月亮 + + + + 奇幻世界:獨身向前 + + + + 寰宇國際影院:亡命大畫家 + + + + 戰火球星 + + + + 東經北緯 + + + + 王者之心 + + + + 神鬼獵人:在結冰的路上 + + + + 滄海漁生 + + + + 神醫 + + + + 環球電影大觀:兩天一夜 + + + + 浩氣蓋山河 + + + + 冬天的訪客 + + + + 神秘代碼 + + + + 畢業證書 + + + + 好萊塢影院:燈塔看守人 + + + + 寰宇國際影院:苦妓追憶錄 + + + + 怒海沉屍 + + + + 滅罪拍檔 + + + + 長翅膀的猪 + + + + 小森林 冬春篇 + + + + 兩天一夜 + + + + 瑪麗蓮 + + + + 環球電影大觀:瞞天計畫 + + + + 小好、小麻、佐和子 + + + + 大地的女兒 + + + + 巴霍巴利王:開端 + + + + 緘默的迷宫 + + + + 經典影院:非洲女王號 + + + + 寰宇國際影院:貝蒂布事件簿 + + + + 金剛:傳奇重生 + + + + 獨身向前 + + + + 法國精爸 + + + + 伊斯特慷 + + + + 接近無限的白 + + + + 北齋 + + + + 環球電影大觀:女學生和亨利先生 + + + + 黃金緣起時 + + + + 戰火實録 + + + + 福岡 + + + + 野馬 + + + + 周日五星影院:更好的世界 + + + + 寰宇國際影院:黑暗瀰漫 + + + + 孩童姿勢 + + + + 無名戰士 + + + + 金魚 + + + + 怒海沉屍 + + + + 隨愛沉淪 + + + + 小曼哈頓 + + + + 環球電影大觀:我們這種叛徒 + + + + 草琴 + + + + 尋找聖地 + + + + 記憶傳授人 + + + + 諑鵝 + + + + 動感天下:謀海危情 + + + + 寰宇國際影院:繼承人 + + + + 美麗心境界 + + + + 東經北緯 + + + + 熱情暑假 + + + + 清白 + + + + 消防犬 + + + + 怒火救援 + + + + 環球電影大觀:與生命有約 + + + + 黃金緣起時 + + + + 冬天的訪客 + + + + 饑餓的心 + + + + 九三之夏 + + + + 亞洲本色:寧靜咖啡館之歌 + + + + 寰宇國際影院:糜骨之壤 + + + + 安娜,我的愛 + + + + 獨身向前 + + + + 多重譏諷 + + + + 小森林 冬春篇 + + + + 委訐人 + + + + 失控陪富團 + + + + 環球電影大觀:火山 + + + + 冠軍 + + + + 隨心所欲 + + + + 畢業證書 + + + + 中間時代 + + + + 緣伤的天空:泡沫人生 + + + + 寰宇國際影院:暗夜驚狂 + + + + 神鬼獵人:在結冰的路上 + + + + 瞞天計畫 + + + + 諜海危情 + + + + 榮耀時刻 + + + + 男孩的生活 + + + + 超膽俠 + + + + 環球電影大觀:美麗心境界 + + + + 怒海沉屍 + + + + 草琴 + + + + 索爾之子 + + + + 慶州 + + + + 奇幻世界:深空失憶 + + + + 寰宇國際影院:被告 + + + + 必是天堂 + + + + 女學生和亨利先生 + + + + 東經北緯 + + + + 寧靜咖啡館之歌 + + + + 洛城機密 + + + + 我與夢露的一周 + + + + 環球電影大觀:孩頃姿勢 + + + + 獨身向前 + + + + 幻影凶間 + + + + 深空失憶 + + + + 琅璃城堡 + + + + 好萊塢影院:多重譏諷 + + + + 寰宇國際影院:醜聞調查 + + + + 小森林 冬春篇 + + + + 我們這種叛徒 + + + + 吾乃母親 + + + + 托尼歐 + + + + 妙想天開 + + + + 喜劇之王 + + + + 環球電影大觀:無名戰士 + + + + 金剛:傳奇重生 + + + + 盧納莎之舞 + + + + 福岡 + + + + 盲點 + + + + 經典影院:熱情暑假 + + + + 寰宇國際影院:我是殺人犯 + + + + 小森林 夏秋篇 + + + + 火山 + + + + 與生命有約 + + + + 非洲女王號 + + + + 我們來了 + + + + 不痛的死法 + + + + 環球電影大觀:金魚 + + + + 怒海驕陽 + + + + 洪吉東 + + + + 恐襲波士頓 + + + + 紙花 + + + + 周日五星影院:聖人文森特 + + + + 寰宇國際影院:嗅覺神探 + + + + 氣息 + + + + 冰媽媽 + + + + 沙拉克 + + + + 戰火實録 + + + + 我啊,走自己的路 + + + + 滄海漁生 + + + + 環球電影大觀:我所有的愛 + + + + 剛好遇見你 + + + + 瑪麗蓮 + + + + 聖人文森特 + + + + 在公交車站直到黎明 + + + + 動感天下:攔截目擊者 + + + + 寰宇國際影院:非法制裁 + + + + 榮耀時刻 + + + + 東經北緯 + + + + 安娜,我的愛 + + + + 影子部隊 + + + + 接近無限的白 + + + + 北齋 + + + + 環球電影大觀:郵差的白夜 + + + + 尋找聖地 + + + + 一個女學生的日記 + + + + 埃爾塞:將近改變世界的人 + + + + 無名戰士 + + + + 亞洲本色:意料之外的你 + + + + 寰宇國際影院:絶命追兇 + + + + 托尼歐 + + + + 美麗心境界 + + + + 須彌山 + + + + 金魚 + + + + 刺殺肯尼迪 + + + + 粉色月亮 + + + + 環球電影大觀:必是天堂 + + + + 洪吉東 + + + + 阿郎的故事 + + + + 隨愛沉淪 + + + + 紙花 + + + + 緣份的天空:一年之癢 + + + + 寰宇國際影院:亡命大畫家 + + + + 影子部隊 + + + + 與生命有約:北壁 + + + + 我所有的愛 + + + + 小曼哈頓 + + + + 消防犬 + + + + 環球電影大觀:氣息 + + + + 我很在乎 + + + + 海雲洞的兩個家庭 + + + + 怒火救援 + + + + 在緑茵場上 + + + + 奇幻世界:吾乃母親 + + + + 寰宇國際影院:貝蒂布事件簿 + + + + 郵差的白夜 + + + + 意料之的你 + + + + 東經北緯 + + + + 攔截目擊者 + + + + 失控陪審團 + + + + 男孩的生活 + + + + 環球電影大觀:清白 + + + + 須彌山 + + + + 平壤納帕蘭 + + + + 大眼睛 + + + + 我啊,走自己的路 + + + + 好萊塢影院:抗癌的我 + + + + 寰宇國際影院:黑暗瀰漫 + + + + 浩氣蓋山河 + + + + 神鬼獵人:在結冰的路上 + + + + 一年之癢 + + + + 美麗心境界 + + + + 超膽俠 + + + + 洛城機密 + + + + 環球電影大觀:榮耀時刻 + + + + 啊,青春! + + + + 小公女 + + + + 抗癌的我 + + + + 被嫌棄的松子的一生 + + + + 經典影院:沙拉克 + + + + 寰宇國際影院:看不見的客人 + + + + 黃金緣起時 + + + + 怒海驕陽 + + + + 冬天的訪客 + + + + 隨心所欲 + + + + 妙想天開 + + + + 圖騰 + + + + 環球電影大觀:托尼歐 + + + + 倩女幽魂 + + + + 喜劇之王 + + + + 愛的就是你 + + + + 這裎是亞美子 + + + + 周日五星影院:北壁 + + + + 寰宇國際影院:糜骨之壤 + + + + 啊,青春! + + + + + + + + 海雲洞的兩個家庭 + + + + 人造怪物 + + + + 九三之夏 + + + + 我們來了 + + + + 環球電影大觀:冰媽媽 + + + + 非常時期 + + + + 不痛的死法 + + + + 滄海漁生 + + + + 刺殺肯尼迪, + + + + 動感天下:海上函戰 + + + + 寰宇國際影院:末日危途 + + + + 縱横四海 + + + + 朋友 + + + + 神鬼獵人,在結冰的路上 + + + + 平壤納帕蘭 + + + + 瑪麗蓮 + + + + 接近無限的白 + + + + 環球電影大觀:尋找聖地 + + + + 我家的故事 + + + + 王牌對王牌 + + + + 在緣茵場上 + + + + 圖騰 + + + + 亞洲本色:為你取名的朋一天 + + + + 寰宇國際影院:嗅覺神探 + + + + + + + + 我很在乎 + + + + 夢想的課程 + + + + 心所欲 + + + + 北齋 + + + + 小曼哈頓 + + + + 環球電影大觀:洪吉東 + + + + 黃金緣起時 + + + + 愛沉淪 + + + + 偷天陷阱 + + + + 温暖的屍體 + + + + 緣份的天空:妙筆生花 + + + + 寰宇國際影院:苦妓追憶録 + + + + 末日危途 + + + + 安娜,我的愛 + + + + 金魚 + + + + 一個女學生的日記 + + + + 消防犬 + + + + 怒火救援 + + + + 環球電影大觀:啊,青春! + + + + 剛好遇見你 + + + + 委託人 + + + + 妙筆生花 + + + + 緘默的迷宫 + + + + 奇幻世界:超驗駭客 + + + + 寰宇國際影院:醜聞調查 + + + + 倩女幽魂 + + + + 為你取名的那一天:郵差的白夜 + + + + 我家的故事 + + + + 失控陪審團 + + + + 男孩的生活 + + + + 環球電影大觀:海雲洞的兩個家庭 + + + + 偷天陷阱 + + + + 超膽俠 + + + + 超驗駭客 + + + + 妙想天開 + + + + 好萊塢影院:朋友 + + + + 寰宇國際影院:暗夜驚狂 + + + + 洪吉東 + + + + 冰媽媽 + + + + 海上毒戰 + + + + 縱横四海 + + + + 洛城機密 + + + + 福岡 + + + + 環球電影大觀:平壤納帕蘭 + + + + 埃舍爾街的紅色郵筒 + + + + 紙花 + + + + 記憶傳授人 + + + + 里業證書 + + + + 經典影院:蛇 + + + + 宇際影院:繼承人 + + + + 人造怪物 + + + + 嗅覺神探 + + + + 戰火實鋒 + + + + 末日危途 + + + + 迷霧 + + + + 圖騰 + + + + 環球電影大觀:一個女學生的日記 + + + + 逭裎是亞美子 + + + + 非常時期 + + + + 百萬富翁的初戀 + + + + 鮑爾的抗爭 + + + + 周日五星影院:夢想的課程 + + + + 寰宇國際影院:非法制裁 + + + + 慶州 + + + + 愛的就是你 + + + + 温暖的屍體 + + + + 盲點 + + + + 决戰猶馬鎮 + + + + 瑪麗蓮 + + + + 環球電影大觀:我家的故事 + + + + 在公交車站直到黎明 + + + + 詠鵝 + + + + 神秘代碼 + + + + 雪國機械師 + + + + 動感天下:備天陷阱 + + + + 寰宇國際影院:絕命追兇 + + + + 鮑爾的抗爭 + + + + 剛好遛見你 + + + + 王牌對王牌 + + + + 饑餓的心 + + + + 索爾之子 + + + + 埃爾塞:將近改變世界的人 + + + + 環球電影大觀:埃舍爾街的紅色郵筒 + + + + 人造怪物 + + + + 福岡 + + + + 粉色月亮 + + + + 在緑茵場上 + + + + 亞洲本色:開心家族 + + + + 寰宇國際影院:暗夜驚狂 + + + diff --git a/EPG/cctv.py b/EPG/cctv.py new file mode 100644 index 000000000..2a431abeb --- /dev/null +++ b/EPG/cctv.py @@ -0,0 +1,80 @@ +import httpx +import re +import datetime +import json +import os +from bs4 import BeautifulSoup as bs + + +async def get_epgs_cctv(channel, dt): + epgs = [] + msg = '' + success = 1 + need_date = dt.strftime('%Y%m%d') + channel_id = channel['id'] + channel_id0 = channel['id0'] + url = 'http://api.cntv.cn/epg/getEpgInfoByChannelNew?c=%s&serviceId=tvcctv&d=%s&t=jsonp&cb=set' % (channel_id0, need_date) + try: + async with httpx.AsyncClient() as client: + res = await client.get(url, timeout=10) + res.encoding = 'utf-8' + programs = json.loads(res.text[4:-2]) + prog_lists = programs['data'][channel_id0]['list'] + for prog_list in prog_lists: + title = prog_list['title'] + starttime = datetime.datetime.fromtimestamp(prog_list['startTime']) + endtime = datetime.datetime.fromtimestamp(prog_list['endTime']) + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': '' + } + epgs.append(epg) + # print(epg) + epglen = len(epgs) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs, + 'msg': msg, + 'last_program_date': dt, + 'ban': 0, + } + return ret + + +async def get_channels_cctv(): + channels = [] + async with httpx.AsyncClient() as client: + res = await client.get('https://tv.cctv.com/epg/index.shtml') + res.encoding = 'utf-8' + soup = bs(res.text, 'html.parser') + lis = soup.select('div.channel_con > div > ul > li') + need_date = datetime.datetime.now().strftime('%Y%m%d') + for li in lis: + id = li.select('img')[0].attrs['title'].strip() + # logo = 'http://' + li.select('img')[0].attrs['src'].strip() + url_info = 'http://api.cntv.cn/epg/getEpgInfoByChannelNew?c=%s&serviceId=tvcctv&d=%s&t=jsonp&cb=set' % (id, need_date) + async with httpx.AsyncClient() as client: + res = await client.get(url_info, timeout=5) + res.encoding = 'utf-8' + research = re.search('"channelName":"(.+?)".+?"lvUrl":"(.+?)"', res.text) + name = research.group(1) + # url = research.group(2) + channel = { + 'id': 'cctv_' + id, + 'name': name, + 'id0': id, + 'source': 'cctv' + } + channels.append(channel) + print(channel) + return channels + + +# await get_channels_cctv() +# get_epgs_cctv({'id': 'cctv_cctv1', 'name': 'CCTV-1 综合', 'id0': 'cctv1', 'source': 'cctv'}, datetime.datetime.now()) diff --git a/EPG/epgpw.py b/EPG/epgpw.py new file mode 100644 index 000000000..de596f5a8 --- /dev/null +++ b/EPG/epgpw.py @@ -0,0 +1,44 @@ +# -*- coding:utf-8 -*- +import httpx +import datetime +import os + + +async def get_epgs_epgpw(channel): + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + channel_id0 = channel['id0'] + url = f'https://epg.pw/api/epg.json?channel_id={channel_id0}&timezone=QXNpYS9UYWlwZWk=' + try: + async with httpx.AsyncClient() as client: + res = await client.get(url) + epg_list = res.json()['epg_list'] + starttime = datetime.datetime.strptime(epg_list[-1]['start_date'][:8], '%Y%m%d') + datetime.timedelta(days=1) + for i in epg_list[::-1]: + title = i['title'] + desc = i['desc'] if i['desc'] != None else '' + endtime = starttime + starttime = datetime.datetime.strptime(i['start_date'][:14], '%Y%m%d%H%M%S') + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': desc, + } + epgs.append(epg) + # print(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs[::-1], + 'msg': msg, + 'ban': 0, + } + return ret + +# await get_epgs_epgpw({'id': 'tvb_C28', 'name': '28AI智慧賽馬', 'id0': '394087', 'source': 'epg.pw'}) diff --git a/EPG/ettvamerica.py b/EPG/ettvamerica.py new file mode 100644 index 000000000..2b5050229 --- /dev/null +++ b/EPG/ettvamerica.py @@ -0,0 +1,64 @@ +# -*- coding:utf-8 -*- +import datetime +import os +import httpx +from bs4 import BeautifulSoup +import asyncio +import pytz + +pst = pytz.timezone('America/Los_Angeles') # PST (UTC-8) +bjt = pytz.timezone('Asia/Shanghai') # BJT (UTC+8) + +async def get_epgs_ettvamerica(channel, dt): + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + provider, channel_id0 = channel['id0'].split('-') + try: + for hour in range(0, 24, 3): + channel_id = channel['id'] + url = f'https://www.ettvamerica.com/Schedule/ScheduleList.cshtml?Region=1&Provider={provider}&Y={dt.year}&M={dt.month}&D={dt.day}&H={hour}' + async with httpx.AsyncClient() as client: + res = await client.get(url, timeout=10) + res.encoding = 'utf-8' + soup = BeautifulSoup(res.text, 'html.parser') + channel_list = res.text.split('; grid-column: 1; text-align: center;">') + for ch in channel_list: + if channel_id0 in ch: + epg_text = ch + soup = BeautifulSoup(epg_text, 'html.parser') + for item in soup.find_all('div', class_='ScheduleDiv'): + span = item.select('span') + title = span[0].text + date = span[1].text.split(' ~ ') + starttime0 = datetime.datetime.strptime(date[0], "%I:%M %p") + starttime = pst.localize(datetime.datetime(dt.year, dt.month, dt.day, starttime0.hour, starttime0.minute)).astimezone(bjt) + endtime0 = datetime.datetime.strptime(date[1], "%I:%M %p") + if endtime0 > starttime0: + endtime = pst.localize(datetime.datetime(dt.year, dt.month, dt.day, endtime0.hour, endtime0.minute)).astimezone(bjt) + else: + endtime = pst.localize(datetime.datetime(dt.year, dt.month, dt.day+1, endtime0.hour, endtime0.minute)).astimezone(bjt) + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': '', + } + epgs.append(epg) + # print(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs, + 'msg': msg, + 'ban': 0, + } + return ret + + +# asyncio.run(get_epgs_ettvamerica({'name': '東森中國台', 'id': 'ETTVAmerica_China', 'id0': '1-中國台', 'source': 'ETTVAmerica'}, datetime.datetime.now().date())) +# asyncio.run(get_epgs_ettvamerica({'name': '東森美東衛視台', 'id': 'ETTVAmerica_East', 'id0': '20-美東衛視台', 'source': 'ETTVAmerica'}, datetime.datetime.now().date())) diff --git a/EPG/fourgtv.py b/EPG/fourgtv.py new file mode 100644 index 000000000..6cfb11185 --- /dev/null +++ b/EPG/fourgtv.py @@ -0,0 +1,92 @@ +# -*- coding:utf-8 -*- +import httpx +import datetime +import os + + +async def get_epgs_4gtv(channel): + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + channel_id0 = channel['id0'] + url = 'https://www.4gtv.tv/proglist/%s.txt' % (channel_id0) + try: + async with httpx.AsyncClient() as client: + res = await client.get(url, timeout=8) + res.encoding = 'utf-8' + res_json = res.json() + for j in res_json: + title = j['title'] + start_date = j['sdate'] + start_time = j['stime'] + end_date = j['edate'] + end_time = j['etime'] + starttime = datetime.datetime.strptime( + '%s%s' % (start_date, start_time), '%Y-%m-%d%H:%M:%S') + endtime = datetime.datetime.strptime( + '%s%s' % (end_date, end_time), '%Y-%m-%d%H:%M:%S') + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': '', + 'program_date': starttime.date(), + } + epgs.append(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs, + 'msg': msg, + 'ban': 0, + + } + return ret + + +async def get_channels_4gtv(): + url = 'http://api2.4gtv.tv/Channel/GetAllChannel/pc/L' + headers = { + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'zh-CN,zh;q=0.9,en-CA;q=0.8,en;q=0.7,zh-TW;q=0.6', + 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': "Windows", + 'Sec-Fetch-Dest': 'document', + 'Sec-Fetch-Mode': 'navigate', + 'Sec-Fetch-User': '?1', + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36', + } + async with httpx.AsyncClient() as client: + res = await client(url, headers=headers) + res.encoding = 'utf-8' + js = res.json()['Data'] + channels = [] + for j in js: + id = str(j['fnID']) + type = j['fsTYPE'] + name = j['fsNAME'] + gtvid = j['fs4GTV_ID'] + logo = j['fsLOGO_MOBILE'] + desc = j['fsDESCRIPTION'] if 'fsDESCRIPTION' in j else '' + all = [name, gtvid, logo] + + channel = { + 'name': name, + 'id': [gtvid], + 'url': 'https://www.4gtv.tv/channel', + 'source': '4gtv', + 'logo': logo, + 'desc': desc, + } + channels.append(channel) + print(channel) + return channels + +# ret = await get_epgs_4gtv({'id': '4gtv_litv-ftv03', 'name': 'VOA美國之音', 'id0': '4gtv-live050', 'source': '4gtv'}) diff --git a/EPG/gen_xml.py b/EPG/gen_xml.py new file mode 100644 index 000000000..903565582 --- /dev/null +++ b/EPG/gen_xml.py @@ -0,0 +1,440 @@ +# -*- coding:utf-8 -*- +import datetime +import pytz +import html +import asyncio +import logging +from cctv import * +from tvmao import * +from nowtv import * +from mod import * +from tbc import * +from jxgdw import * +from epgpw import * +from ntdtv import * +from ettvamerica import * +from fourgtv import * +from tdm import * +from homeplus import * + +beijing_tz = pytz.timezone('Asia/Shanghai') + + + +async def get_epgs(c): + logging.info(c) + epgs = [] + times = 0 + success = '✅' + if c['source'] == 'cctv': + for get_days in range(-6, 2): # 7+1天 + need_date = datetime.datetime.now().date() + datetime.timedelta(days=get_days) + while times < 5: + ret = await get_epgs_cctv(c, need_date) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + if c['source'] == 'tvmao': + for get_days in range(-6, 2): # 7+1天 + need_date = datetime.datetime.now().date() + datetime.timedelta(days=get_days) + while times < 5: + ret = await get_epgs_tvmao(c, need_date) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + if c['source'] == 'nowtv': + for get_days in [-1, 0, 1]: # 昨今明3天 + need_date = datetime.datetime.now().date() + datetime.timedelta(days=get_days) + while times < 5: + ret = await get_epgs_nowtv(c, need_date) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + elif c['source'] == 'mod': + for get_days in [-1, 0, 1]: # 昨今明3天 + need_date = datetime.datetime.now().date() + datetime.timedelta(days=get_days) + while times < 5: + ret = await get_epgs_mod(c, need_date) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + if c['source'] == 'ETTVAmerica': + for get_days in [-1, 0, 1]: # 昨今明3天 + need_date = datetime.datetime.now().date() + datetime.timedelta(days=get_days) + while times < 5: + ret = await get_epgs_ettvamerica(c, need_date) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + if c['source'] == 'tdm': + for get_days in [-1, 0, 1]: # 昨今明3天 + need_date = datetime.datetime.now().date() + datetime.timedelta(days=get_days) + while times < 5: + ret = await get_epgs_tdm(c, need_date) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + elif c['source'] == 'tbc': + while times < 5: + ret = await get_epgs_tbc(c) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + elif c['source'] == 'jxgdw': + need_date = datetime.datetime.now().date() + while times < 5: + ret = await get_epgs_jxgdw(c, need_date) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + elif c['source'] == 'epg.pw': + while times < 5: + ret = await get_epgs_epgpw(c) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + elif c['source'] == 'ntdtv': + while times < 5: + ret = await get_epgs_ntdtv(c) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + elif c['source'] == '4gtv': + while times < 5: + ret = await get_epgs_4gtv(c) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + elif c['source'] == 'homeplus': + while times < 5: + ret = await get_epgs_homeplus(c) + if ret['success'] == True: + epg = ret['epgs'] + break + else: + msg = ret['msg'] + times += 1 + logging.warning(f"{msg}, 将进行第{times}次重试!") + else: + logging.warning(f"{c}获取失败!") + epg = [] + success = '❌' + for i in epg: + epgs.append(i) + return epgs, f"|{c['id']}|{c['name']}|{success}|\n" + + +async def gen_xml(channels, filename): + xmlhead = '\n\n' + xmlbottom = '' + tz = ' +0800' + tasks = [get_epgs(c) for c in channels] + epgs0 = await asyncio.gather(*tasks) + epgs = [] + README = ['|tvg-id|tvg-name|EPG状态|\n', '|:---:|:---:|:---:|\n'] + for i, text in epgs0: + README.append(text) + for k in i: + epgs.append(k) + f = open('README.md', 'w', encoding='utf-8') + f.writelines(README) + f.close() + f = open(filename, 'w', encoding='utf-8') + f.write(xmlhead) + for channel in channels: + c = (' \n' + ' %s\n' + ' \n') % (channel['id'], channel['name']) + f.write(c) + # noepg_channel = 'noepg' + # f.write(noepg_channel) + for epg in epgs: + # print(epg) + start = epg['starttime'].astimezone(tz=beijing_tz).strftime('%Y%m%d%H%M%S') + tz + end = epg['endtime'].astimezone(tz=beijing_tz).strftime('%Y%m%d%H%M%S') + tz + id = epg['channel_id'] + title = html.escape(epg['title']) + desc = html.escape(epg['desc']) + programinfo = (' \n' + ' %s\n' + ' %s\n' + ' \n') % (start, end, id, title, desc) + f.write(programinfo) + # for x in range(10): + # noepg_program_day = noepg('noepg','9999',(datetime.datetime.now().date() + datetime.timedelta(days=x-5))) + # f.write(noepg_program_day) + f.write(xmlbottom) + f.close() + +if __name__ == '__main__': + channels = [{'id': 'cctv_cctv1', 'name': 'CCTV-1 综合', 'id0': 'cctv1', 'source': 'cctv'}, + {'id': 'cctv_cctv2', 'name': 'CCTV-2 财经', 'id0': 'cctv2', 'source': 'cctv'}, + {'id': 'cctv_cctv3', 'name': 'CCTV-3 综艺', 'id0': 'cctv3', 'source': 'cctv'}, + {'id': 'cctv_cctv4', 'name': 'CCTV-4 (亚洲)', 'id0': 'cctv4', 'source': 'cctv'}, + {'id': 'cctv_cctv5', 'name': 'CCTV-5 体育', 'id0': 'cctv5', 'source': 'cctv'}, + {'id': 'cctv_cctv6', 'name': 'CCTV-6 电影', 'id0': 'cctv6', 'source': 'cctv'}, + {'id': 'cctv_cctv7', 'name': 'CCTV-7 国防军事', 'id0': 'cctv7', 'source': 'cctv'}, + {'id': 'cctv_cctv8', 'name': 'CCTV-8 电视剧', 'id0': 'cctv8', 'source': 'cctv'}, + {'id': 'cctv_cctvjilu', 'name': 'CCTV-9 纪录', 'id0': 'cctvjilu', 'source': 'cctv'}, + {'id': 'cctv_cctv10', 'name': 'CCTV-10 科教', 'id0': 'cctv10', 'source': 'cctv'}, + {'id': 'cctv_cctv11', 'name': 'CCTV-11 戏曲', 'id0': 'cctv11', 'source': 'cctv'}, + {'id': 'cctv_cctv12', 'name': 'CCTV-12 社会与法', 'id0': 'cctv12', 'source': 'cctv'}, + {'id': 'cctv_cctv13', 'name': 'CCTV-13 新闻', 'id0': 'cctv13', 'source': 'cctv'}, + {'id': 'cctv_cctvchild', 'name': 'CCTV-14 少儿', 'id0': 'cctvchild', 'source': 'cctv'}, + {'id': 'cctv_cctv15', 'name': 'CCTV-15 音乐', 'id0': 'cctv15', 'source': 'cctv'}, + {'id': 'cctv_cctv5plus', 'name': 'CCTV-5+ 体育赛事', 'id0': 'cctv5plus', 'source': 'cctv'}, + {'id': 'cctv_cctv16', 'name': 'CCTV-16奥林匹克', 'id0': 'cctv16', 'source': 'cctv'}, + {'id': 'cctv_cctv17', 'name': 'CCTV-17农业农村', 'id0': 'cctv17', 'source': 'cctv'}, + {'id': 'cctv_cctveurope', 'name': 'CCTV-4 (欧洲)', 'id0': 'cctveurope', 'source': 'cctv'}, + {'id': 'cctv_cctvamerica', 'name': 'CCTV-4 (美洲)', 'id0': 'cctvamerica', 'source': 'cctv'}, + {'id': 'tvmao_NANCHANG-NANCHANG1', 'name': '南昌电视台新闻综合频道', 'id0': 'NANCHANG-NANCHANG1', 'source': 'tvmao'}, + {'id': 'tvmao_NANCHANG-NANCHANG4', 'name': '南昌电视台都市频道', 'id0': 'NANCHANG-NANCHANG4', 'source': 'tvmao'}, + {'id': 'tvmao_NANCHANG-NANCHANG3', 'name': '南昌电视台资讯频道', 'id0': 'NANCHANG-NANCHANG3', 'source': 'tvmao'}, + {'id': 'jxgdw_87', 'name': '江西卫视', 'id0': '87', 'source': 'jxgdw'}, + {'id': 'jxgdw_86', 'name': '都市频道', 'id0': '86', 'source': 'jxgdw'}, + {'id': 'jxgdw_153', 'name': '经济生活', 'id0': '153', 'source': 'jxgdw'}, + {'id': 'jxgdw_84', 'name': '影视旅游', 'id0': '84', 'source': 'jxgdw'}, + {'id': 'jxgdw_83', 'name': '公共农业', 'id0': '83', 'source': 'jxgdw'}, + {'id': 'jxgdw_82', 'name': '少儿频道', 'id0': '82', 'source': 'jxgdw'}, + {'id': 'jxgdw_81', 'name': '新闻频道', 'id0': '81', 'source': 'jxgdw'}, + {'id': 'jxgdw_112', 'name': '移动电视', 'id0': '112', 'source': 'jxgdw'}, + {'id': 'jxgdw_78', 'name': '陶瓷频道', 'id0': '78', 'source': 'jxgdw'}, + {'id': 'jxgdw_79', 'name': '风尚购物', 'id0': '79', 'source': 'jxgdw'}, + {'id': 'mod_010', 'name': '010 中視', 'id0': '010', 'source': 'mod'}, + {'id': 'mod_098', 'name': '098 公視兒少台', 'id0': '098', 'source': 'mod'}, + {'id': 'mod_320', 'name': '320 新唐人亞太台', 'id0': '320', 'source': 'mod'}, + {'id': 'mod_551', 'name': '551 BBC NEWS', 'id0': '368825', 'source': 'epg.pw'}, + {'id': 'mod_558', 'name': '558 TaiwanPlus', 'id0': '558', 'source': 'mod'}, + {'id': 'mod_610', 'name': '610 美亞電影台', 'id0': '368837', 'source': 'epg.pw'}, + {'id': 'mod_619', 'name': '619 amc電影台', 'id0': '368844', 'source': 'epg.pw'}, + {'id': 'mod_628', 'name': '628 壹電視電影台', 'id0': '368850', 'source': 'epg.pw'}, + {'id': 'mod_626', 'name': '626 CatchPlay電影台', 'id0': '368848', 'source': 'epg.pw'}, + {'id': 'nowtv_102', 'name': 'Viu 頻道', 'id0': '102', 'source': 'nowtv'}, + {'id': 'nowtv_105', 'name': 'Now華劇台', 'id0': '105', 'source': 'nowtv'}, + {'id': 'nowtv_108', 'name': 'NowJelli', 'id0': '108', 'source': 'nowtv'}, + {'id': 'nowtv_133', 'name': 'Now 爆谷台', 'id0': '133', 'source': 'nowtv'}, + {'id': 'nowtv_138', 'name': 'Now爆谷星影台', 'id0': '138', 'source': 'nowtv'}, + {'id': 'nowtv_162', 'name': '東森亞洲衛視', 'id0': '162', 'source': 'nowtv'}, + {'id': 'nowtv_218', 'name': 'Love Nature 4K', 'id0': '218', 'source': 'nowtv'}, + {'id': 'nowtv_316', 'name': 'CNN 國際新聞網絡', 'id0': '316', 'source': 'nowtv'}, + {'id': 'nowtv_329', 'name': 'RT', 'id0': '329', 'source': 'nowtv'}, + {'id': 'nowtv_331', 'name': 'Now直播台', 'id0': '331', 'source': 'nowtv'}, + {'id': 'nowtv_332', 'name': 'Now新聞台', 'id0': '332', 'source': 'nowtv'}, + {'id': 'nowtv_333', 'name': 'Now財經台', 'id0': '333', 'source': 'nowtv'}, + {'id': 'nowtv_371', 'name': '東森亞洲新聞台', 'id0': '371', 'source': 'nowtv'}, + {'id': 'nowtv_538', 'name': '中天亞洲台', 'id0': '538', 'source': 'nowtv'}, + {'id': 'nowtv_552', 'name': 'OneTV 綜合頻道', 'id0': '552', 'source': 'nowtv'}, + {'id': 'nowtv_621', 'name': 'Now Sports 英超1台', 'id0': '621', 'source': 'nowtv'}, + {'id': 'nowtv_622', 'name': 'Now Sports 英超2台', 'id0': '622', 'source': 'nowtv'}, + {'id': 'nowtv_623', 'name': 'Now Sports 英超3台', 'id0': '623', 'source': 'nowtv'}, + {'id': 'nowtv_624', 'name': 'Now Sports 英超4台', 'id0': '624', 'source': 'nowtv'}, + {'id': 'nowtv_625', 'name': 'Now Sports 英超5台', 'id0': '625', 'source': 'nowtv'}, + {'id': 'nowtv_626', 'name': 'Now Sports 英超6台', 'id0': '626', 'source': 'nowtv'}, + {'id': 'nowtv_627', 'name': 'Now Sports 英超7台', 'id0': '627', 'source': 'nowtv'}, + {'id': 'nowtv_630', 'name': 'Now Sports 精選', 'id0': '630', 'source': 'nowtv'}, + {'id': 'nowtv_631', 'name': 'Now Sports 1', 'id0': '631', 'source': 'nowtv'}, + {'id': 'nowtv_632', 'name': 'Now Sports 2', 'id0': '632', 'source': 'nowtv'}, + {'id': 'nowtv_633', 'name': 'Now Sports 3', 'id0': '633', 'source': 'nowtv'}, + {'id': 'nowtv_634', 'name': 'Now Sports 4', 'id0': '634', 'source': 'nowtv'}, + {'id': 'nowtv_635', 'name': 'Now Sports 5', 'id0': '635', 'source': 'nowtv'}, + {'id': 'nowtv_636', 'name': 'Now Sports 6', 'id0': '636', 'source': 'nowtv'}, + {'id': 'nowtv_637', 'name': 'Now Sports 7', 'id0': '637', 'source': 'nowtv'}, + {'id': 'nowtv_638', 'name': 'beIN SPORTS 1', 'id0': '638', 'source': 'nowtv'}, + {'id': 'nowtv_639', 'name': 'beIN SPORTS 2', 'id0': '639', 'source': 'nowtv'}, + {'id': 'nowtv_641', 'name': 'Now Sports 641', 'id0': '641', 'source': 'nowtv'}, + {'id': 'nowtv_642', 'name': 'NBA TV', 'id0': '642', 'source': 'nowtv'}, + {'id': 'nowtv_643', 'name': 'beIN SPORTS 3', 'id0': '643', 'source': 'nowtv'}, + {'id': 'nowtv_680', 'name': 'Now Sports Plus', 'id0': '680', 'source': 'nowtv'}, + {'id': 'nowtv_683', 'name': 'Now Golf 2', 'id0': '683', 'source': 'nowtv'}, + {'id': 'nowtv_684', 'name': 'Now Golf 3', 'id0': '684', 'source': 'nowtv'}, + {'id': 'astro_148', 'name': '8TV', 'id0': '1122', 'source': 'epg.pw'}, + {'id': 'astro_300', 'name': 'iQIYI HD', 'id0': '3290', 'source': 'epg.pw'}, + {'id': 'astro_306', 'name': 'Astro AEC', 'id0': '2226', 'source': 'epg.pw'}, + {'id': 'astro_308', 'name': 'Astro QJ', 'id0': '1781', 'source': 'epg.pw'}, + {'id': 'astro_309', 'name': 'Celestial Movies HD', 'id0': '1298', 'source': 'epg.pw'}, + {'id': 'astro_311', 'name': 'Astro AOD', 'id0': '2124', 'source': 'epg.pw'}, + {'id': 'astro_319', 'name': 'TVB Xing He HD', 'id0': '3493', 'source': 'epg.pw'}, + {'id': 'astro_333', 'name': 'Astro Hua Hee Dai', 'id0': '1951', 'source': 'epg.pw'}, + {'id': 'astro_393', 'name': 'ONE HD', 'id0': '1242', 'source': 'epg.pw'}, + {'id': 'astro_392', 'name': 'KBS World HD', 'id0': '1889', 'source': 'epg.pw'}, + {'id': 'astro_514', 'name': 'Sky News HD', 'id0': '1744', 'source': 'epg.pw'}, + {'id': 'tbc_025', 'name': '東森幼幼台', 'id0': '368941', 'source': 'epg.pw'}, + {'id': 'tbc_029', 'name': '三立台灣台', 'id0': '368952', 'source': 'epg.pw'}, + {'id': 'tbc_030', 'name': '三立都會台', 'id0': '369190', 'source': 'epg.pw'}, + {'id': 'tbc_032', 'name': '東森綜合台', 'id0': '369188', 'source': 'epg.pw'}, + {'id': 'tbc_033', 'name': '東森超視', 'id0': '369189', 'source': 'epg.pw'}, + {'id': 'tbc_036', 'name': '中天綜合台', 'id0': '369192', 'source': 'epg.pw'}, + {'id': 'tbc_038', 'name': '年代MUCH TV', 'id0': '369182', 'source': 'epg.pw'}, + {'id': 'tbc_039', 'name': '中天娛樂台', 'id0': '369183', 'source': 'epg.pw'}, + {'id': 'tbc_040', 'name': '東森戲劇台', 'id0': '369227', 'source': 'epg.pw'}, + {'id': 'tbc_050', 'name': '年代新聞台', 'id0': '369247', 'source': 'epg.pw'}, + {'id': 'tbc_054', 'name': '三立新聞台', 'id0': '369243', 'source': 'epg.pw'}, + {'id': 'tbc_055', 'name': 'TVBS 新聞台', 'id0': '369244', 'source': 'epg.pw'}, + {'id': 'tbc_056', 'name': 'TVBS', 'id0': '369245', 'source': 'epg.pw'}, + {'id': 'tbc_063', 'name': '緯來電影台', 'id0': '369262', 'source': 'epg.pw'}, + {'id': 'tbc_065', 'name': 'HBO', 'id0': '369264', 'source': 'epg.pw'}, + {'id': 'tbc_127', 'name': 'Channel NewsAsia', 'id0': '368931', 'source': 'epg.pw'}, + {'id': 'tbc_207', 'name': 'HBO HD', 'id0': '369313', 'source': 'epg.pw'}, + {'id': 'tbc_208', 'name': 'HBO 強檔鉅獻', 'id0': '369199', 'source': 'epg.pw'}, + {'id': 'tbc_209', 'name': 'HBO 原創鉅獻', 'id0': '369197', 'source': 'epg.pw'}, + {'id': 'tbc_210', 'name': 'HBO 溫馨家庭', 'id0': '368911', 'source': 'epg.pw'}, + {'id': 'tbc_249', 'name': 'Euronews', 'id0': '369329', 'source': 'epg.pw'}, + {'id': 'tbc_405', 'name': '彩虹e台', 'id0': '368936', 'source': 'epg.pw'}, + {'id': 'tbc_406', 'name': '彩虹電影台', 'id0': '368935', 'source': 'epg.pw'}, + {'id': 'tbc_408', 'name': '松視1台', 'id0': '368939', 'source': 'epg.pw'}, + {'id': 'tbc_409', 'name': '松視2台', 'id0': '368938', 'source': 'epg.pw'}, + {'id': 'tbc_410', 'name': '松視3台', 'id0': '369161', 'source': 'epg.pw'}, + {'id': 'tbc_412', 'name': '潘朵啦高畫質玩美台', 'id0': '369203', 'source': 'epg.pw'}, + {'id': 'tbc_415', 'name': '驚豔成人電影台', 'id0': '369158', 'source': 'epg.pw'}, + {'id': 'tbc_416', 'name': '香蕉台', 'id0': '369159', 'source': 'epg.pw'}, + {'id': 'tvb_CWIN', 'name': 'SUPER FREE', 'id0': '368376', 'source': 'epg.pw'}, + {'id': 'tvb_C28', 'name': '28AI智慧賽馬', 'id0': '394087', 'source': 'epg.pw'}, + {'id': 'tvb_TVG', 'name': '黃金翡翠台', 'id0': '368358', 'source': 'epg.pw'}, + {'id': 'tvb_J', 'name': '翡翠台', 'id0': '368366', 'source': 'epg.pw'}, + {'id': 'tvb_B', 'name': 'TVB Plus', 'id0': '368361', 'source': 'epg.pw'}, + {'id': 'tvb_C', 'name': '無綫新聞台', 'id0': '368359', 'source': 'epg.pw'}, + {'id': 'tvb_P', 'name': '明珠台', 'id0': '368369', 'source': 'epg.pw'}, + {'id': 'tvb_CTVC', 'name': '千禧經典台', 'id0': '368325', 'source': 'epg.pw'}, + {'id': 'tvb_CTVS', 'name': '亞洲劇台', 'id0': '368335', 'source': 'epg.pw'}, + {'id': 'tvb_CDR3', 'name': '華語劇台', 'id0': '368344', 'source': 'epg.pw'}, + {'id': 'tvb_TVO', 'name': '黃金華劇台', 'id0': '368351', 'source': 'epg.pw'}, + {'id': 'tvb_CTVE', 'name': '娛樂新聞台', 'id0': '368323', 'source': 'epg.pw'}, + {'id': 'tvb_CCOC', 'name': '戲曲台', 'id0': '368353', 'source': 'epg.pw'}, + {'id': 'tvb_KID', 'name': 'SUPER Kids Channel', 'id0': '368380', 'source': 'epg.pw'}, + {'id': 'tvb_CNIKO', 'name': 'Nickelodeon', 'id0': '368336', 'source': 'epg.pw'}, + {'id': 'tvb_CCLM', 'name': '粵語片台', 'id0': '368381', 'source': 'epg.pw'}, + {'id': 'tvb_CMAM', 'name': '美亞電影台', 'id0': '368348', 'source': 'epg.pw'}, + {'id': 'tvb_POPC', 'name': 'PopC', 'id0': '368322', 'source': 'epg.pw'}, + {'id': 'tvb_LN4', 'name': 'Love Nature 4K', 'id0': '368364', 'source': 'epg.pw'}, + {'id': 'tvb_CTS1', 'name': '無線衛星亞洲台', 'id0': '368357', 'source': 'epg.pw'}, + {'id': 'tvb_CMN1', 'name': '神州新聞台', 'id0': '368352', 'source': 'epg.pw'}, + {'id': 'tvb_CNHK', 'name': 'NHK World-Japan', 'id0': '368337', 'source': 'epg.pw'}, + {'id': 'tvb_EVT2', 'name': 'myTV SUPER直播足球2台', 'id0': '397763', 'source': 'epg.pw'}, + {'id': 'tvb_EVT3', 'name': 'myTV SUPER直播足球3台', 'id0': '368345', 'source': 'epg.pw'}, + {'id': 'tvb_EVT4', 'name': 'myTV SUPER直播足球4台', 'id0': '368328', 'source': 'epg.pw'}, + {'id': 'tvb_EVT5', 'name': 'myTV SUPER直播足球5台', 'id0': '368379', 'source': 'epg.pw'}, + {'id': 'tvb_EVT6', 'name': 'myTV SUPER直播足球6台', 'id0': '398976', 'source': 'epg.pw'}, + {'id': 'ETTVAmerica_China', 'name': '東森中國台', 'id0': '1-中國台', 'source': 'ETTVAmerica'}, + {'id': 'ETTVAmerica_East', 'name': '東森美東衛視台', 'id0': '20-美東衛視台', 'source': 'ETTVAmerica'}, + {'id': 'ntd_china', 'name': '新唐人中國台', 'id0': 'ntd_china', 'source': 'ntdtv'}, + {'id': '4gtv_litv-ftv03', 'name': 'VOA美國之音', 'id0': 'litv-ftv03', 'source': '4gtv'}, + {'id': 'tdm_1', 'name': '澳視澳門 Ch. 91', 'id0': '1', 'source': 'tdm'}, + {'id': 'tdm_2', 'name': '澳視葡文 Ch. 92', 'id0': '2', 'source': 'tdm'}, + {'id': 'tdm_3', 'name': '澳門電台 FM100.7', 'id0': '3', 'source': 'tdm'}, + {'id': 'tdm_4', 'name': 'Rádio Macau FM98', 'id0': '4', 'source': 'tdm'}, + {'id': 'tdm_5', 'name': '澳門資訊 Ch.94', 'id0': '5', 'source': 'tdm'}, + {'id': 'tdm_6', 'name': '澳門體育 Ch.93', 'id0': '6', 'source': 'tdm'}, + {'id': 'tdm_7', 'name': '澳門綜藝 Ch.95', 'id0': '7', 'source': 'tdm'}, + {'id': 'tdm_8', 'name': '澳門 - MACAU 衛星頻道 Ch.96', 'id0': '8', 'source': 'tdm'} + ] + asyncio.run(gen_xml(channels, 'epg0.xml')) diff --git a/EPG/homeplus.py b/EPG/homeplus.py new file mode 100644 index 000000000..e7debfba3 --- /dev/null +++ b/EPG/homeplus.py @@ -0,0 +1,79 @@ +# -*- coding:utf-8 -*- +import httpx +import datetime +import re +import os + + +async def get_epgs_homeplus(channel): + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + channel_id0 = channel['id0'] + headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'} + url = 'https://www.homeplus.net.tw/cable/Product_introduce/digital_tv/get_channel_content' + payload = f"so=210&channelid={channel_id0}" + try: + async with httpx.AsyncClient() as client: + res = await client.post(url=url, headers=headers, data=payload, timeout=10) + date_program = res.json()['date_program'] + for date in date_program: + for i in date_program[date]: + for j in i: + try: + name = j['name'] + except: + j = i[j] + name = j['name'] + description = j['description'] + beginTime = datetime.datetime.strptime(date + j['beginTime'], '%Y-%m-%d%H:%M') + endTime = datetime.datetime.strptime(date + j['endTime'], '%Y-%m-%d%H:%M') + if beginTime > endTime: + beginTime = beginTime - datetime.timedelta(days=1) + epg = {'channel_id': channel_id, + 'starttime': beginTime, + 'endtime': endTime, + 'title': name, + 'desc': description + } + print(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs, + 'msg': msg, + 'ban': 0, + } + return ret + + +# 下载TBC所有频道ID及名称 +async def get_channels_homeplus(): + channels = [] + headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'} + url = 'https://www.homeplus.net.tw/cable/Product_introduce/digital_tv/getCategoryAndContent' + for i in range(7): + payload = f"so=210&page={i}&category=all" + async with httpx.AsyncClient() as client: + res = await client.post(url=url, headers=headers, data=payload, timeout=10) + for channel_list in res.json()['channel']['channel']: + for ch in channel_list: + id = ch['channelid'] + name = ch['name'] + channel = { + 'id': 'homeplus_' + id, + 'name': name, + 'id0': id, + 'source': 'homeplus', + } + print(channel) + channels.append(channel) + return channels + + +# await get_channels_homeplus() +# await get_epgs_homeplus({'id': 'homeplus_13', 'name': '公共電視台', 'id0': '13', 'source': 'homeplus'}) diff --git a/EPG/jxgdw.py b/EPG/jxgdw.py new file mode 100644 index 000000000..cda7dfa26 --- /dev/null +++ b/EPG/jxgdw.py @@ -0,0 +1,70 @@ +# -*- coding:utf-8 -*- +import datetime +import os +import httpx + +async def get_epgs_jxgdw(channel, dt): # channel_id,dt ,每次获取当天开始共7天数据 + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + channel_id0 = channel['id0'] + starttime = datetime.datetime( + dt.year, dt.month, dt.day, 0, 0) + datetime.timedelta(days=2) + try: + for i in range(1, -6, -1): + date = dt + datetime.timedelta(days=i) + date_str = date.strftime('%Y-%m-%d') + channel_id = channel['id'] + url = 'https://app.jxgdw.com/api/tv/channel/%s/menus?playDate=%s ' % (channel_id0, date_str) + async with httpx.AsyncClient() as client: + res = await client.get(url, timeout=10) + res.encoding = 'utf-8' + res_j = res.json() + items = res_j['result'] + for item in items[::-1]: + title = item['programName'] + endtime = starttime + starttime = datetime.datetime.strptime(item['playTime'], '%Y-%m-%d %H:%M:%S') + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': '', + 'program_date': date, + } + epgs.append(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs[::-1], + 'msg': msg, + 'ban': 0, + } + return ret + + +async def get_channels_jxgdw(): + url = 'https://app.jxgdw.com/api/tv/channel/page?pageSize=100' + async with httpx.AsyncClient() as client: + res = await client.get(url) + res_channels = res.json()['result']['list'] + channels = [] + for li in res_channels: + name = li['channelName'] + id = li['id'] + channel = { + 'name': name, + 'id': str(id), + 'source': 'jxgdw', + } + # print(channel) + channels.append(channel) + return channels + + +# get_channels_jxgdw() +# get_epgs_jxgdw({'name': '都市频道', 'id': '86', 'source': 'jxgdw'}, datetime.datetime.now().date()) diff --git a/EPG/mod.py b/EPG/mod.py new file mode 100644 index 000000000..86d5aa924 --- /dev/null +++ b/EPG/mod.py @@ -0,0 +1,90 @@ +# -*- coding:utf-8 -*- +import httpx +import datetime +import os +import datetime +import os +import datetime +from bs4 import BeautifulSoup as bs + + +async def get_epgs_mod(channel, dt): + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + channel_id0 = channel['id0'] + days = (dt - datetime.datetime.now().date()).days + url = 'https://modweb2.chtmod.tv/tv/channel.php?id=%s&d=%s' % ( + channel_id0, days) # d=0表示当天,至少能获取最近七天数据 http://mod.cht.com.tw/tv/channel.php?id=6&d=2 + try: + async with httpx.AsyncClient() as client: + res = await client.get(url) + res.encoding = 'utf-8' + soup = bs(res.text, 'html.parser') + lis = soup.select('ul.striped-time-table > li') + starttime = datetime.datetime(dt.year, dt.month, dt.day, 0, 0) + datetime.timedelta(days=1) + timestr0 = 2400 + for li in lis[::-1]: + title = li.select('h4')[0].text.replace('\t', '').replace('\r', '').replace('\n', '').strip() + endtime = starttime + timestr = li.select('time.time')[0].text.strip().replace(':', '') + if int(timestr) < int(timestr0): + starttime = datetime.datetime(dt.year, dt.month, dt.day, int(timestr[:2]), int(timestr[-2:])) + else: + starttime = datetime.datetime(dt.year, dt.month, dt.day, int(timestr[:2]), int(timestr[-2:])) - datetime.timedelta(days=1) + timestr0 = timestr + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': '', + 'program_date': dt, + } + epgs.append(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs[::-1], + 'msg': msg, + 'ban':0, + } + return ret + + +async def get_channels_mod(): + # http://mod.cht.com.tw/tv/channel.php?id=006 采集节目表地址 + url = 'https://modweb2.chtmod.tv/bepg2/' + async with httpx.AsyncClient() as client: + res = await client.get(url, timeout=10) + res.encoding = 'utf-8' + soup = bs(res.text, 'html.parser') + divs = soup.select('div.rowat') + divs2 = soup.select('div.rowat_gray') + divs += divs2 + channels = [] + for div in divs: + try: + # urlid = div.select('div > a')[0].attrs['href'] + name = div.select('div.channel_info')[0].text + id = name[:3].strip() + # img = 'http://mod.cht.com.tw' + \ + # re.sub('\?rand=\d*', '', div.select('img') + # [0].attrs['src']).strip() + channel = { + 'id': 'mod_' + id, + 'name': name, + 'id0': id, + 'source': 'mod', + } + print(channel) + channels.append(channel) + except Exception as e: + print(div) + return channels + +# print(get_epgs_mod({'name': '006 民視', 'id': '006', 'source': 'mod'}, datetime.datetime.now().date())) +# await get_channels_mod() \ No newline at end of file diff --git a/EPG/mytvsuper.py b/EPG/mytvsuper.py new file mode 100644 index 000000000..ed570dfa4 --- /dev/null +++ b/EPG/mytvsuper.py @@ -0,0 +1,93 @@ +# -*- coding:utf-8 -*- +import httpx +import datetime +import os +import asyncio + +headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' + ' AppleWebKit/537.36 (KHTML, like Gecko)' + ' Chrome/99.0.4844.82 Safari/537.36' +} + + +async def get_epgs_mytvsuper(channel, dt): # channel_id,dt ,每次获取当天开始共7天数据 + epgs = [] + msg = '' + success = 1 + start_date_str = dt.strftime('%Y%m%d') + end_date = dt + datetime.timedelta(days=6) + end_date_str = end_date.strftime('%Y%m%d') + channel_id = channel['id'] + channel_id0 = channel['id0'] + url = 'https://content-api.mytvsuper.com/v1/epg?network_code=%s&from=%s&to=%s&platform=web ' % ( + channel_id0, start_date_str, end_date_str) + try: + async with httpx.AsyncClient() as client: + res = await client.get(url, timeout=8, headers=headers) + res.encoding = 'utf-8' + res_j = res.json() + items = res_j[0]['item'] + print(items) + for item in items: + epg_list = item['epg'] + firtst_line_date = 1 + for li in epg_list: + starttime = datetime.datetime.strptime(li['start_datetime'], '%Y-%m-%d %H:%M:%S') + title = li['programme_title_tc'] + title_en = li['programme_title_en'] + desc = li['episode_synopsis_tc'] + desc_en = li['episode_synopsis_en'] + url = 'https://www.mytvsuper.com/tc/programme/%s' % li['programme_path'] + program_date = starttime.date() if 'starttime' in locals() else dt + if firtst_line_date: + last_program_date = starttime + first_line_date = 0 + # print(title,starttime,title_en) + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': None, + 'title': title, + 'desc': desc, + 'program_date': program_date, + } + epgs.append(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs, + 'msg': msg, + 'ban': 0, + } + return ret + + +async def get_channels_mytvsuper(): + url = 'https://content-api.mytvsuper.com/v1/channel/list?platform=web' + async with httpx.AsyncClient() as client: + res = await client.get(url, headers=headers) + res_channels = res.json()['channels'] + channels = [] + for li in res_channels: + name = li['name_tc'] + # name_en = li['name_en'] + cn = li['channel_no'] + # href = 'https://www.mytvsuper.com/tc/epg/%s/'%cn + # logo = li['path'] if 'path' in li else '' + id = li['network_code'] + # desc = '' + channel = { + 'id': 'tvb_' + id, + 'name': name, + 'id0': id, + 'source': 'mytvsuper', + } + print(channel) + channels.append(channel) + return channels + +# asyncio.run(get_channels_mytvsuper()) +# get_epgs_mytvsuper({'name': '亞洲劇台', 'name_en': 'Asian Drama', 'id': 'CTVS', 'source': 'mytvsuper'}, datetime.datetime.now().date()) diff --git a/EPG/nowtv.py b/EPG/nowtv.py new file mode 100644 index 000000000..27965db53 --- /dev/null +++ b/EPG/nowtv.py @@ -0,0 +1,107 @@ +# -*- coding:utf-8 -*- +import asyncio +import re +import datetime +import json +import os +import datetime +import re +import os +import datetime +import httpx + +# requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL' + +headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36', + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Referer': 'http://nowtv.now.com/epg/', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', + 'Accept': 'application/json, text/javascript, */*', + 'Pragma': 'no-cache', + 'Accept-Encoding': 'gzip, deflate', + 'Proxy-Connection': 'keep-alive', +} + + +async def get_epg_nowtv(channelepg, channel_id): + starttime = datetime.datetime.fromtimestamp(channelepg['start'] / 1000) + endtime = datetime.datetime.fromtimestamp(channelepg['end'] / 1000) + title = channelepg['name'] + vimProgramId = channelepg['vimProgramId'] + async with httpx.AsyncClient() as client: + response = await client.get(url=f'https://nowplayer.now.com/tvguide/epgprogramdetail?programId={vimProgramId}', headers=headers, timeout=8) + res2 = json.loads(response.text) + desc = res2['chiSynopsis'] if 'chiSynopsis' in res2 else '' + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': desc, + 'program_date': starttime.date(), + } + return epg + + +async def get_epgs_nowtv(channel, dt): + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + channel_id0 = channel['id0'] + url = 'https://now-tv.now.com/gw-epg/epg/zh_tw/%s/prf136/resp-ch/ch_%s.json' % (dt.strftime('%Y%m%d'), channel_id0) + try: + async with httpx.AsyncClient() as client: + res = await client.get(url, headers=headers, timeout=10) + res.encoding = 'utf-8' + j = json.loads(res.text) + chs = j['data']['chProgram'] # 很多频道的这一天的节目表 + for ch in chs: + tasks = [get_epg_nowtv(channelepg, channel_id) for channelepg in chs[ch] if ch == channel_id0] + epgs_ch = await asyncio.gather(*tasks) + for i in epgs_ch: + epgs.append(i) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs, + 'msg': msg, + 'ban': 0, + } + return ret + + +async def get_channels_nowtv(): + url = 'https://now-tv.now.com/gw-epg/epg/channelMapping.zh-TW.js' + async with httpx.AsyncClient() as client: + res = await client.get(url, headers=headers, timeout=10) + res.encoding = 'utf-8' + reinfo = re.search('.+?var ChannelMapping=(.*)var GenreToChanne', res.text, re.DOTALL) + cs = reinfo.group(1)[:-2] + cs = json.loads(cs) + channels = [] + for channel_id in cs: + if 'name' not in cs[channel_id]: + continue + # id1 = cs[channel_id]['genreKeys'][0] + # print(cs[channel_id]) + name = cs[channel_id]['name'] + channel = { + 'id': 'nowtv_' + channel_id, + 'name': name, + 'id0': channel_id, + 'source': 'nowtv', + } + print(channel) + channels.append(channel) + return channels + + +# if __name__ == '__main__': +# channels = asyncio.run(get_channels_nowtv()) + # epgs = asyncio.run(get_epgs_nowtv({'id': 'nowtv_331', 'name': 'Now直播台', 'id0': '331', 'source': 'nowtv'}, datetime.datetime.now().date())) + # print(epgs) diff --git a/EPG/ntdtv.py b/EPG/ntdtv.py new file mode 100644 index 000000000..f875931eb --- /dev/null +++ b/EPG/ntdtv.py @@ -0,0 +1,55 @@ +# -*- coding:utf-8 -*- +import datetime +import os +import httpx +import asyncio +from bs4 import BeautifulSoup +import json + + +async def get_epgs_ntdtv(channel): + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + channel_id0 = channel['id0'] + try: + async with httpx.AsyncClient() as client: + res = await client.get('https://www.ntdtv.com/b5/program-schedule') + res.encoding = 'utf-8' + soup = BeautifulSoup(res.text, 'html.parser') + data_schedules = soup.find('div', class_="json-data")['data-schedules'] + data_schedules = json.loads(data_schedules) + items = [] + for channel in data_schedules: + if channel == channel_id0: + for date in data_schedules[channel]: + for item in data_schedules[channel][date]: + items.append(item) + enddate = datetime.datetime.fromtimestamp(item['time_start']).date() + endtime = datetime.datetime(enddate.year, enddate.month, enddate.day, 13, 0) + for item in items[::-1]: + title = item['title'] + starttime = datetime.datetime.fromtimestamp(item['time_start']) + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': '', + } + endtime = starttime + epgs.append(epg) + # print(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs[::-1], + 'msg': msg, + 'ban': 0, + } + return ret + +# asyncio.run(get_epgs_ntdtv({'id': 'ntd_china', 'name': '新唐人中國台', 'id0': 'ntd_china', 'source': 'ntdtv'})) diff --git a/EPG/requirements.txt b/EPG/requirements.txt new file mode 100644 index 000000000..104f87702 --- /dev/null +++ b/EPG/requirements.txt @@ -0,0 +1,4 @@ +beautifulsoup4==4.12.3 +httpx==0.27.2 +pytz==2024.1 +Requests==2.32.3 diff --git a/EPG/tbc.py b/EPG/tbc.py new file mode 100644 index 000000000..7214a16fe --- /dev/null +++ b/EPG/tbc.py @@ -0,0 +1,97 @@ +# -*- coding:utf-8 -*- +import asyncio +import httpx +import datetime +import re +import os +from bs4 import BeautifulSoup as bs + + +async def get_epgs_tbc(channel): + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + channel_id0 = channel['id0'] + url = 'https://www.tbc.net.tw/EPG/Channel?channelId=%s' % channel_id0 + try: + async with httpx.AsyncClient() as client: + res = await client.get(url, timeout=30) + res.encoding = 'utf-8' + soup = bs(res.text, 'html.parser') + uls = soup.select('ul.list_program2') + for ul in uls: + # n1 = 0 # 记录当天的节目数 + lis = ul.select('li') + for li in lis: + try: + title = li.attrs['title'] + except: + title = li.attrs['data.name'] + desc = li.attrs['desc'] + date_ = li.attrs['date'] + time_delay = li.attrs['time'].strip() + time_delay_re = re.search('(\d+:\d+)~(\d+:\d+)', time_delay) + if time_delay_re: # 有节目信息则解析 + start_str, end_str = time_delay_re.group(1), time_delay_re.group(2) # 将开始与结束时间的文本分开 + starttime = datetime.datetime.strptime(date_ + start_str, '%Y/%m/%d%H:%M') + endtime = datetime.datetime.strptime(date_ + end_str, '%Y/%m/%d%H:%M') + if starttime > endtime: + endtime = endtime + datetime.timedelta(days=1) + # if starttime.date() < dt: + # continue + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': desc, + 'program_date': starttime.date(), + } + if epg not in epgs: + epgs.append(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs, + 'msg': msg, + 'ban': 0, + } + return ret + + +# 下载TBC所有频道ID及名称 +async def get_channels_tbc(): + channels = [] + cookies = { + 'ASP.NET_SessionId': 'v111fiox1mzc0wpc0d4iue5c' + } + url = 'https://www.tbc.net.tw/EPG' + async with httpx.AsyncClient() as client: + res = await client.get(url=url, cookies=cookies, timeout=6) + res.encoding = 'utf-8' + soup = bs(res.text, 'html.parser') + lis = soup.select('ul.list_tv > li') + for li in lis: + name = li['title'] + id = li['id'] + # img = li.select('img')[0]['src'] + # url = li.a['href'] + channel = { + 'id': 'tbc_' + id, + 'name': name, + 'id0': id, + 'source': 'tbc', + } + print(channel) + channels.append(channel) + return channels + + +# 今天的日期数字 20190325 一次抓取多天数据,不能重复爬取 +# today_int = int(time.strftime('%Y%m%d', time.localtime())) + +# asyncio.run(get_channels_tbc()) +# get_epgs_tbc({'name': '寰宇新聞台灣台', 'id': '227', 'source': 'tbc'}) diff --git a/EPG/tdm.py b/EPG/tdm.py new file mode 100644 index 000000000..1efb98c5b --- /dev/null +++ b/EPG/tdm.py @@ -0,0 +1,73 @@ +# -*- coding:utf-8 -*- +import asyncio +import datetime +import json +import os +import datetime +import re +import os +import datetime +import httpx + +headers = { + 'Accept': 'application/json, text/javascript, */*', + 'Api-Language': 'zh_TW', +} + + +async def get_epgs_tdm(channel, dt): + epgs = [] + msg = '' + success = 1 + channel_id = channel['id'] + channel_id0 = channel['id0'] + dt1 = dt + datetime.timedelta(days=1) + url = 'https://www.tdm.com.mo/api/v1.0/program-list/%s?channelId=%s' % (str(dt), channel_id0) + url1 = 'https://www.tdm.com.mo/api/v1.0/program-list/%s?channelId=%s' % (str(dt1), channel_id0) + try: + async with httpx.AsyncClient() as client: + res = await client.get(url1, headers=headers, timeout=8) + res.encoding = 'utf-8' + j = json.loads(res.text) + starttime = datetime.datetime.strptime(j['data'][0]['date'], '%Y-%m-%d %H:%M:%S') + async with httpx.AsyncClient() as client: + res = await client.get(url, headers=headers, timeout=8) + res.encoding = 'utf-8' + j = json.loads(res.text) + data = j['data'] + for d in data[::-1]: + title = d['title'] + endtime = starttime + starttime = datetime.datetime.strptime(d['date'], '%Y-%m-%d %H:%M:%S') + if d['slug']: + async with httpx.AsyncClient() as client: + res = await client.get(f"https://www.tdm.com.mo/api/v1.0/program/{d['slug']}/details", headers=headers, timeout=8) + res.encoding = 'utf-8' + j = json.loads(res.text) + data0 = j['data'] + desc = data0['content'] if data0['content'] else '' + else: + desc = '' + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': desc, + } + epgs.append(epg) + # print(epg) + except Exception as e: + success = 0 + spidername = os.path.basename(__file__).split('.')[0] + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs[::-1], + 'msg': msg, + 'ban': 0, + } + return ret + + +# if __name__ == '__main__': +# epgs = await get_epgs_tdm({'id': 'tdm_1', 'name': '澳視澳門 Ch. 91', 'id0': '1', 'source': 'tdm'}, datetime.datetime.now().date()) diff --git a/EPG/tvmao.py b/EPG/tvmao.py new file mode 100644 index 000000000..78b1f8a9e --- /dev/null +++ b/EPG/tvmao.py @@ -0,0 +1,61 @@ +# -*- coding:utf-8 -*- +import httpx +import datetime + + +async def get_epgs_tvmao(channel, dt): + epgs = [] + desc = '' + msg = '' + success = 1 + ban = 0 # 标识是否被BAN掉了,此接口不确定是否有反爬机制 + channel_id = channel['id'] + channel_id0 = channel['id0'] + now_date = datetime.datetime.now().date() + delta = dt - now_date + now_weekday = now_date.weekday() + need_weekday = now_weekday + delta.days + 1 + id_split = channel_id0.split('-') + if len(id_split) == 2: + id = id_split[1] + elif len(id_split) == 3: + id = '-'.join(id_split[1:3]) + else: + id = channel_id0 + url = f"https://lighttv.tvmao.com/qa/qachannelschedule?epgCode={id}&op=getProgramByChnid&epgName=&isNew=on&day={need_weekday}" + headers = { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36", + } + try: + async with httpx.AsyncClient() as client: + res = await client.get(url, headers=headers) + res_j = res.json() + datas = res_j[2]['pro'] + endtime = datetime.datetime.combine(dt, datetime.time(23, 59)) + for data in datas[::-1]: + title = data['name'] + starttime_str = data['time'] + starttime = datetime.datetime.combine(dt, datetime.time(int(starttime_str[:2]), int(starttime_str[-2:]))) + epg = {'channel_id': channel_id, + 'starttime': starttime, + 'endtime': endtime, + 'title': title, + 'desc': '' + } + epgs.append(epg) + # print(epg) + endtime = starttime + except Exception as e: + success = 0 + msg = 'spider-%s-%s' % (channel_id, e) + ret = { + 'success': success, + 'epgs': epgs[::-1], + 'msg': msg, + 'ban': 0 + } + return ret + + +# await get_epgs_tvmao({'id': 'tvmao_NANCHANG-NANCHANG1', 'name': '南昌电视台新闻综合频道', 'id0': 'NANCHANG-NANCHANG1', 'source': 'tvmao'}, datetime.datetime.now().date()) diff --git a/TVlogos/.DS_Store b/TVlogos/.DS_Store new file mode 100644 index 000000000..5008ddfcf Binary files /dev/null and b/TVlogos/.DS_Store differ diff --git a/TVlogos/CanalMacau.png b/TVlogos/CanalMacau.png new file mode 100644 index 000000000..4256647ff Binary files /dev/null and b/TVlogos/CanalMacau.png differ diff --git a/TVlogos/HK01.png b/TVlogos/HK01.png new file mode 100644 index 000000000..afb0b8643 Binary files /dev/null and b/TVlogos/HK01.png differ diff --git a/TVlogos/JiangxiEdu.png b/TVlogos/JiangxiEdu.png new file mode 100644 index 000000000..d1cd71507 Binary files /dev/null and b/TVlogos/JiangxiEdu.png differ diff --git a/TVlogos/NHKChinese.png b/TVlogos/NHKChinese.png new file mode 100644 index 000000000..3508d46a4 Binary files /dev/null and b/TVlogos/NHKChinese.png differ diff --git a/TVlogos/NewTV.png b/TVlogos/NewTV.png new file mode 100644 index 000000000..8da6c8edc Binary files /dev/null and b/TVlogos/NewTV.png differ diff --git a/TVlogos/PTSNews.png b/TVlogos/PTSNews.png new file mode 100755 index 000000000..5607fee20 Binary files /dev/null and b/TVlogos/PTSNews.png differ diff --git a/TVlogos/PhoenixMoviesChannel.png b/TVlogos/PhoenixMoviesChannel.png new file mode 100644 index 000000000..59ee1c5c2 Binary files /dev/null and b/TVlogos/PhoenixMoviesChannel.png differ diff --git a/TVlogos/RFA.png b/TVlogos/RFA.png new file mode 100644 index 000000000..1a58d8ff3 Binary files /dev/null and b/TVlogos/RFA.png differ diff --git a/TVlogos/RFI.png b/TVlogos/RFI.png new file mode 100644 index 000000000..3984e7a12 Binary files /dev/null and b/TVlogos/RFI.png differ diff --git a/TVlogos/RTHK.png b/TVlogos/RTHK.png new file mode 100644 index 000000000..ac526f6f7 Binary files /dev/null and b/TVlogos/RTHK.png differ diff --git a/TVlogos/RTHK31.png b/TVlogos/RTHK31.png new file mode 100755 index 000000000..232ab3fce Binary files /dev/null and b/TVlogos/RTHK31.png differ diff --git a/TVlogos/RTHK32.png b/TVlogos/RTHK32.png new file mode 100755 index 000000000..ffeff4f2c Binary files /dev/null and b/TVlogos/RTHK32.png differ diff --git a/TVlogos/RTHK33.png b/TVlogos/RTHK33.png new file mode 100755 index 000000000..cc136cc8d Binary files /dev/null and b/TVlogos/RTHK33.png differ diff --git a/TVlogos/RTHK34.png b/TVlogos/RTHK34.png new file mode 100755 index 000000000..f3dc5584b Binary files /dev/null and b/TVlogos/RTHK34.png differ diff --git a/TVlogos/RTHK35.png b/TVlogos/RTHK35.png new file mode 100755 index 000000000..baef5763a Binary files /dev/null and b/TVlogos/RTHK35.png differ diff --git a/TVlogos/RTHK36.png b/TVlogos/RTHK36.png new file mode 100755 index 000000000..f1d37be0b Binary files /dev/null and b/TVlogos/RTHK36.png differ diff --git a/TVlogos/RTHKPTHRadio.png b/TVlogos/RTHKPTHRadio.png new file mode 100644 index 000000000..308cc53c6 Binary files /dev/null and b/TVlogos/RTHKPTHRadio.png differ diff --git a/TVlogos/RegionalEmblemOfMacau.png b/TVlogos/RegionalEmblemOfMacau.png new file mode 100644 index 000000000..c3ec2ea49 Binary files /dev/null and b/TVlogos/RegionalEmblemOfMacau.png differ diff --git a/TVlogos/TDMEntertainment.png b/TVlogos/TDMEntertainment.png new file mode 100644 index 000000000..91033d78b Binary files /dev/null and b/TVlogos/TDMEntertainment.png differ diff --git "a/TVlogos/TDMInforma\303\247\303\243o.png" "b/TVlogos/TDMInforma\303\247\303\243o.png" new file mode 100644 index 000000000..0822f08ed Binary files /dev/null and "b/TVlogos/TDMInforma\303\247\303\243o.png" differ diff --git "a/TVlogos/TDMMacauSat\303\251llite.png" "b/TVlogos/TDMMacauSat\303\251llite.png" new file mode 100644 index 000000000..1cd605a1e Binary files /dev/null and "b/TVlogos/TDMMacauSat\303\251llite.png" differ diff --git a/TVlogos/TDMOuMun.png b/TVlogos/TDMOuMun.png new file mode 100644 index 000000000..22bf0e2f7 Binary files /dev/null and b/TVlogos/TDMOuMun.png differ diff --git a/TVlogos/TDMSport.png b/TVlogos/TDMSport.png new file mode 100644 index 000000000..09a87a6b2 Binary files /dev/null and b/TVlogos/TDMSport.png differ diff --git a/TVlogos/VOA.png b/TVlogos/VOA.png new file mode 100644 index 000000000..614cebfe5 Binary files /dev/null and b/TVlogos/VOA.png differ diff --git a/TVlogos/VOAChina.png b/TVlogos/VOAChina.png new file mode 100644 index 000000000..3773f363e Binary files /dev/null and b/TVlogos/VOAChina.png differ diff --git a/TVlogos/oncc.png b/TVlogos/oncc.png new file mode 100644 index 000000000..232d62dce Binary files /dev/null and b/TVlogos/oncc.png differ diff --git a/WebGrab++.config.xml b/WebGrab++.config.xml deleted file mode 100644 index 161f28af7..000000000 --- a/WebGrab++.config.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - guide.xml - - rex - Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 Edg/79.0.309.71 - - - on - 4 - 0 - f - - - - 01 香港開電視 * - 02 香港國際財經台 * - 108 財經資訊台 * - 109 新聞台 * - 110 直播新聞台 * - 201 有線電影台 * - 301 綜合娛樂台 * - 601 高清體育台 * - 602 Sports Plus 1 * - 603 高清603台 * - 604 Sports Plus 2 * - 605 Sports Plus 3 * - 618 有線18台 - 661 有線體育台 - 662 Sports Plus 1 - 664 Sports Plus 2 - 665 Sports Plus 3 - 668 賽馬1台 - 669 賽馬2台 - - dummy -