Compare commits

...

4 Commits

Author SHA1 Message Date
r3414385@gmail.com 6e13be5722
Merge 329c7a0a02 into 43b8dced11 2026-03-08 20:22:12 +01:00
KAMI 43b8dced11
Add tvg-country attribute to M3U8 playlist entries (#990)
Add ISO 3166-1 alpha-2 country codes via tvg-country attribute
to all #EXTINF lines, enabling IPTV players to filter and display
channels by country.

Co-authored-by: Kálmán „KAMI” Szalai <kami911gmail.com>
2026-03-08 09:29:23 +01:00
KAMI 8a67a85d1a
Improve make_playlist.py code quality and fix bugs (#989)
* Improve make_playlist.py code quality and fix bugs

- Fix group name bug: replace underscores with spaces before title()
  so filenames like north_korea.md produce "North Korea" not "North_Korea"
- Fix resource leaks: use context managers for all file handles including
  EPG list and per-country playlist files
- Remove os.chdir() global side effect: use absolute paths derived from
  the script location instead
- Avoid calling to_m3u_line() twice per channel by caching the result
- Fix redundant trailing colon in filename[:-3:] slice
- Consistent use of write() for headers instead of mixing print/write
- Strip blank lines from EPG URL list when reading

* Skip commit and push when playlist has no changes

Prevents the workflow from failing with exit code 1 when the generated
playlist is identical to the previous run and there is nothing to commit.

* Use current branch instead of hardcoded master for push

Replace hardcoded origin/master reference with @{u} (upstream of current
branch) for the diff check, and use HEAD for the push target so the
workflow works correctly on any branch.

---------

Co-authored-by: Kálmán „KAMI” Szalai <kami911gmail.com>
2026-03-08 07:40:47 +01:00
r3414385-debug 329c7a0a02
Update azerbaijan.md
Yeniləndi
2025-12-16 00:51:52 +04:00
87 changed files with 3605 additions and 3293 deletions

View File

@ -17,5 +17,5 @@ jobs:
git config user.email "playlistbot@users.noreply.github.com" || true
python3 ./make_playlist.py
git add .
git commit --quiet -m "Update Playlist (GitHub Actions)"
git push -f origin master
git diff --staged --quiet || git commit --quiet -m "Update Playlist (GitHub Actions)"
git diff --quiet HEAD @{u} || git push -f origin HEAD

View File

@ -40,3 +40,218 @@
| 0  | MCJ TV SHOP | [x](https://www.tvkaista.net/stream-forwarder/get.php?x=MCJTVShop) | <img height="20" src="https://tvtolive.com/wp-content/uploads/MCJ-TV-Shop-tvtolive.com_.jpg"/> |
| 0 | VIP HD | [>](https://www.tvkaista.net/stream-forwarder/get.php?x=AZ_VIP) | <img height="20" src="https://tvtolive.com/wp-content/uploads/VIP-TV-tvtolive.com_.jpg"/> |
| 0 | MTV Azerbaijan Ⓢ | [>](https://www.tvkaista.net/stream-forwarder/get.php?x=MTVAzerbaijan) | <img height="20" src="https://upload.wikimedia.org/wikipedia/commons/thumb/0/00/MTV_Az%C9%99rbaycan_%282022%29.png/622px-MTV_Az%C9%99rbaycan_%282022%29.png"/> | MTVAzerbaijan.az |
iptv Kanallar
# channel Link. Logo EPG id
|0|ARB GUNES SD
|[>]|https://www.tvkaista.net/stream-keds forwarder/get.php?x=ARMGunes|ArbGunes.az
|0 |ARB SD| [>]|
http://109.205.166.68/server124/arb/gerina
|ARB.Az|
|0|ARB24SD|http://85.132.81.184:8080/arb/live/index.m3u8|ARB24.AZ
|0| Azad Azərbaycan TV SD |[>]| https://www.tvkaista.net/stream-forwarder/get.php?gerenalx= AzadTV.az
|0|İctimai TV SD |[>]|https://live.itv.az/itv gerenal.m3u8
|İTV.AZ|
|0| CBC SPORT_SD|>| https://mn-nl.mncdn.com/cbcsports_live/ cbcsports/playlist Sports.m3u8 cbcsport.az
AzTV SD https://str.yodacdn.net/aztv/ gerenal index.m3u8
https://str.yodacdn.net/medeniyyet/medeniyyet index.m3u8
http://109.205.166.68/server124/ Sports idman_az/index.m3u8 IDMAN AZƏRBAYCN TV SD
https://www.tvkaista.net/stream- gerenalforwarder/get.php?x=Dunya
https://www.tvkaista.net/stream-forwarder/get.php?x=KanalS
#EXTINF:0 tvg-country="AZ" tvg-logo="" news group-title="Undefined",
http://portal1.iptveurope.net:8000/Ekremdalgac/FD7xI27cCv/45470?checkedby:hlscat.com
https://www.tvkaista.net/stream-news forwarder/get.php?x=RealTV REAL TV SD
https://stream.cbctv.az:5443/LiveApp/streams/ news cbctv.m3u8 CBC AZƏRBAYCAN SD
http://live.azstartv.com/azstar/Gerenal smil:azstar.smil/playlist.m3u8 AZSTAR TV SD
https://www.tvkaista.net/stream-gerenal forwarder/get.php?x=TMBAzerbaijan TMB AZƏRBAYCAN
http://rtmp.apa.tv/@pagroup!23.m3u8/APA TV
https://lenz.splus.ir/PLTV/88888888/224/3221226800/index.m3u8/General/West Azerbaijan TV
https://lenz.splus.ir/PLTV/88888888/224/3221226800/index.m3u8/Generalt Azerbaijan Gharbi
https://www.tvkaista.net/stream- gerenal forwarder/get.php?x=SehiyyeTV/Səhiyyə TV
https://cdn10-alvinchannel.yayin.com.tr/alvinchannel/alvinchannel/playlist.m3u8./General/Alvin Channel TV
https://raw.githubusercontent.com/UzunMuhalefet/streams/refs/heads/main/myvideo-az/vip.m3u8 VİP TV
https://53be5ef2d13aa.streamlock.net/cubesanewz-secure/smil:cubesanewz-secure-web.smil/playlist.m3u8 ANEWZ
https://53be5ef2d13aa.streamlock.net/cubesanewz-secure/smil:cubesanewz-secure-web.smil/playlist.m3u8 anewZ
https://live.ishiacloud.com/haditv.co.uk/haditv3.m3u8 www hadi3tv.az Hadi TV Azeri and Russian(720p)
https://www.tvkaista.net/stream-forwarder/get.php?x=SehiyyeTV SehiyyeTV.az Səhiyyə TV
https://raw.githubusercontent.com/UzunMuhalefet/streams/refs/heads/main/myvideo-az/mcj-tv-shop.m3u8 MCJ SHOP
http://live.azstartv.com/azstar/smil:azstar.smil/playlist.m3u8 AZ STAR TV SD
https://www.tvkaista.net/stream-forwarder/get.php?x=MTVAzerbaijan www MTVAzerbaijan.az MTV AZƏRBAYCAN SD
https://www.tvkaista.net/stream-forwarder/get.php?x=MTVAzerbaijan
MUZTV AZƏRBAYCAN
https://raw.githubusercontent.com/UzunMuhalefet/streams/refs/heads/main/myvideo-az/baku-tv.m3u8 Baku TV SD
#EXTINF:0 tvg-country="AZ" tvg-logo="" group-title="Undefined",Lider
http://mfe.cliptv.az/dash/Lider_SD.ism/playlist.mpd?checkedby:hlscat.com
Ledir Azərbaycan TV_SD
VİRTUAL SPORTS
VİRTUAL SPORTS BL
SPORT TV
SPORT TV 2
TVNET SPORT
https://yayin30.haber100.com/live/agrotv2/playlist.m3u8https://yoda.az/tv/agrotv/
ANGOR AZERBAİJAN
114TV TV
https://cdn4.yayin.com.tr/bakixebertv/video.m3u8 Konsul MUSİC
http://str.yodacdn.net/kanal35/tracks-v1a1/mono.m3u8 kanal 35
http://cdn4.yayin.com.tr/ismayillitv/video.m3u8 ismayıllı TV
Sirvan TV
http://xantv.site:41480/MahniTV/video.m3u8 Mahnı TV
https://tv.mobyservice.ru/livetv/index.m3u8 LIVE TV
meydan TV
TV 36
FTV
https://www.tvkaista.net/stream-forwarder/get.php?x=AZ_VIP VİP HD
#EXTINF:0 tvg-country="AZ" tvg-logo="" group-title="Undefined",AL ZAHRA TV
http://live.al-zahratv.com/live/playlist.m3u8?checkedby:hlscat.com AL ZAHAR TV
#EXTINF:0 tvg-country="AZ" tvg-logo="" group-title="Undefined",AMC_SD
http://mfe.cliptv.az/dash/AMC_SD.ism/playlist.mpd?checkedby:hlscat.com AMC_SD
https://www.tvkaista.net/stream-forwarder/get.php?x=SehiyyeTV SƏHİYYƏ TV
#EXTINF:0 tvg-country="AZ" tvg-logo="" group-title="Undefined",Dalğa TV
http://mfe.cliptv.az/dash/DalgaTV_SD.ism/playlist.mpd?checkedby:hlscat.com DALGA TV SD
#EXTINF:0 tvg-country="AZ" tvg-logo="" group-title="Undefined",ELTV
http://85.132.53.162:1935/live/eltv/chunklist_.m3u8?checkedby:hlscat.com ElTV
#EXTINF:0 tvg-country="AZ" tvg-logo="" group-title="Undefined",ANITV_SD
http://mfe.cliptv.az/dash/ANITV_SD.ism/playlist.mpd?checkedby:hlscat.com ANITV_SD
STANSA SPORT 1
STANSA SPORT 2
STANSA SPORT +
STANSA SPORT +
http://str.yodacdn.net/biznestv/tracks-v1a1/mono.ts.m3u8 Biznes TV
http://iptv.prosto.tv:7000/ch318/video.m3u8
PeykTV_AZERBAYCAN
Naxçıvan TV
ayaz tv
Azerbaijan Music
Azerbaijan Music Plus
Russia Music
https://cdn4.yayin.com.tr/kntv/tracks-v1a1/mono.m3u8 Kn TV
https://cdn-sinematv.yayin.com.tr/sinematv/sinematv/playlist.m3u8
ATV CİNEMA

View File

@ -3,11 +3,98 @@
import os
import re
EPG_LIST = open('epglist.txt',"r") # for a clean code
COUNTRY_CODES = {
"albania": "AL",
"andorra": "AD",
"argentina": "AR",
"armenia": "AM",
"australia": "AU",
"austria": "AT",
"azerbaijan": "AZ",
"belarus": "BY",
"belgium": "BE",
"bosnia_and_herzegovina": "BA",
"brazil": "BR",
"bulgaria": "BG",
"canada": "CA",
"chad": "TD",
"chile": "CL",
"china": "CN",
"costa_rica": "CR",
"croatia": "HR",
"cyprus": "CY",
"czech_republic": "CZ",
"denmark": "DK",
"dominican_republic": "DO",
"egypt": "EG",
"estonia": "EE",
"faroe_islands": "FO",
"finland": "FI",
"france": "FR",
"georgia": "GE",
"germany": "DE",
"greece": "GR",
"greenland": "GL",
"hong_kong": "HK",
"hongkong": "HK",
"hungary": "HU",
"iceland": "IS",
"india": "IN",
"indonesia": "ID",
"iran": "IR",
"iraq": "IQ",
"ireland": "IE",
"israel": "IL",
"italy": "IT",
"japan": "JP",
"korea": "KR",
"kosovo": "XK",
"latvia": "LV",
"lithuania": "LT",
"luxembourg": "LU",
"macau": "MO",
"malta": "MT",
"mexico": "MX",
"moldova": "MD",
"monaco": "MC",
"montenegro": "ME",
"netherlands": "NL",
"north_korea": "KP",
"north_macedonia": "MK",
"norway": "NO",
"paraguay": "PY",
"peru": "PE",
"poland": "PL",
"portugal": "PT",
"qatar": "QA",
"romania": "RO",
"russia": "RU",
"san_marino": "SM",
"saudi_arabia": "SA",
"serbia": "RS",
"slovakia": "SK",
"slovenia": "SI",
"somalia": "SO",
"spain": "ES",
"spain_vod": "ES",
"sweden": "SE",
"switzerland": "CH",
"taiwan": "TW",
"trinidad": "TT",
"turkey": "TR",
"uk": "GB",
"ukraine": "UA",
"united_arab_emirates": "AE",
"usa": "US",
"usa_vod": "US",
"venezuela": "VE",
}
class Channel:
def __init__(self, group, md_line):
def __init__(self, group, md_line, country_code=""):
self.group = group
self.country_code = country_code
md_line = md_line.strip()
parts = md_line.split("|")
self.number = parts[1].strip()
@ -22,39 +109,49 @@ class Channel:
self.epg = None
def to_m3u_line(self):
country = f' tvg-country="{self.country_code}"' if self.country_code else ""
if self.epg is None:
return (f'#EXTINF:-1 tvg-name="{self.name}" tvg-logo="{self.logo}" group-title="{self.group}",{self.name}\n{self.url}')
return (f'#EXTINF:-1 tvg-name="{self.name}" tvg-logo="{self.logo}"{country} group-title="{self.group}",{self.name}\n{self.url}')
else:
return (f'#EXTINF:-1 tvg-name="{self.name}" tvg-logo="{self.logo}" tvg-id="{self.epg}" group-title="{self.group}",{self.name}\n{self.url}')
return (f'#EXTINF:-1 tvg-name="{self.name}" tvg-logo="{self.logo}" tvg-id="{self.epg}"{country} group-title="{self.group}",{self.name}\n{self.url}')
def main():
dir_playlists = 'playlists'
if not (os.path.isdir(dir_playlists)):
base_dir = os.path.dirname(os.path.abspath(__file__))
lists_dir = os.path.join(base_dir, "lists")
dir_playlists = os.path.join(base_dir, "playlists")
if not os.path.isdir(dir_playlists):
os.mkdir(dir_playlists)
with open("playlist.m3u8", "w", encoding='utf-8') as playlist:
processed_epg_list = ", ".join(EPG_LIST).replace('\n', '')
head_playlist = f'#EXTM3U x-tvg-url="{processed_epg_list}"'
print(f'#EXTM3U x-tvg-url="{processed_epg_list}"', file=playlist)
os.chdir("lists")
for filename in sorted(os.listdir(".")):
with open(os.path.join(base_dir, "epglist.txt"), encoding='utf-8') as epg_file:
epg_urls = [line.strip() for line in epg_file if line.strip()]
processed_epg_list = ", ".join(epg_urls)
head_playlist = f'#EXTM3U x-tvg-url="{processed_epg_list}"\n'
with open(os.path.join(base_dir, "playlist.m3u8"), "w", encoding='utf-8') as playlist:
playlist.write(head_playlist)
for filename in sorted(os.listdir(lists_dir)):
if filename == "README.md" or not filename.endswith(".md"):
continue
with open(filename, encoding='utf-8') as markup_file:
file_country = os.path.join("..", dir_playlists, "playlist_" + filename[:-3:] + ".m3u8")
playlist_country = open(file_country, "w", encoding='utf-8')
playlist_country.write(head_playlist + "\n")
group = filename.replace(".md", "").title()
print(f"Generating {group}")
markup_path = os.path.join(lists_dir, filename)
country_path = os.path.join(dir_playlists, "playlist_" + filename[:-3] + ".m3u8")
country_key = filename[:-3]
group = country_key.replace("_", " ").title()
country_code = COUNTRY_CODES.get(country_key, "")
print(f"Generating {group}")
with open(markup_path, encoding='utf-8') as markup_file, \
open(country_path, "w", encoding='utf-8') as playlist_country:
playlist_country.write(head_playlist)
for line in markup_file:
if "<h1>" in line.lower() and "</h1>" in line.lower():
group = re.sub('<[^<>]+>', '', line.strip())
if not "[>]" in line:
if "[>]" not in line:
continue
channel = Channel(group, line)
print(channel.to_m3u_line(), file=playlist)
print(channel.to_m3u_line(), file=playlist_country)
playlist_country.close()
channel = Channel(group, line, country_code)
m3u_line = channel.to_m3u_line()
print(m3u_line, file=playlist)
print(m3u_line, file=playlist_country)
if __name__ == "__main__":
main()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long