Compare commits

...

11 Commits

Author SHA1 Message Date
Natalia Arroyave 070a9f673a
Merge cbe79bfd87 into bce5e4ff6b 2026-03-13 02:11:48 +00:00
PlaylistBot bce5e4ff6b Update Playlist (GitHub Actions) 2026-03-08 20:06:27 +00:00
Aasav Chauhan e619e82306
Add four India IPTV Hindi news channels to India list (#877)
Added Aaj Tak, India TV, TV9 Bharatvarsh, and Republic Bharat with YouTube live links, logos, and official sites to the India channels table.
2026-03-08 21:06:19 +01:00
PlaylistBot 946dc0fc85 Update Playlist (GitHub Actions) 2026-03-08 20:04:59 +00:00
Un Féfé Sauvage dd3138c41a
Removed RT-France from french channel list (#868) 2026-03-08 21:04:51 +01:00
cantonbrayton-commits 8e1efe4f27
Update france.md (#856) 2026-03-08 20:53:05 +01:00
PlaylistBot 148bc990ba Update Playlist (GitHub Actions) 2026-03-08 19:41:17 +00:00
KAMI 1b0fff3d4d
Fix no-dash linting errors in Russian channel names (#991)
Replace dashes with colons in Smotrim channel names.

Co-authored-by: Kálmán „KAMI” Szalai <kami911gmail.com>
2026-03-08 20:41:10 +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
Natalia Arroyave cbe79bfd87 Updated README file for clarity. 2024-09-25 14:27:42 -04:00
90 changed files with 3473 additions and 3312 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

@ -1,7 +1,19 @@
Free TV
=======
IPTV Playlist Project
======
This is an M3U playlist for free TV channels around the World.
Welcome to the IPTV project! This repository offers a curated collection of free TV playlists for IPTV players, providing access to various channels from around the world.
Table of Contents
======
1. Introduction
2. Philosophy
3. Installation & Usage
4. Feed Sources
5. Format
7. Contributing
8. Pull Requests
9. License
Either free locally (over the air):
@ -95,6 +107,10 @@ Or free on the Internet:
To use it point your IPTV player to https://raw.githubusercontent.com/Free-TV/IPTV/master/playlist.m3u8.
Introduction
======
This project provides a collection of free IPTV playlists, allowing users to stream live TV channels thorugh compatible IPTV players. The playlists are community-driven, regularly updated, and intended for users looking for a wide range of channels.Channels are free-to-air or sourced from free online platforms, ensuring that users only access legal content.
Philosophy
==========
@ -124,19 +140,38 @@ This is a playlist for everybody.
- No channels dedicated to any particular political party
- No channels made for a country and funded by a different country
Installation and Usage
======
**To use playlists**
1. Download or clone this repository
2. Use any IPTV player (e.g. VLC, Kodi, IPTV Smarters) to load the '.m3u' file or copy the URL into your player.
**Reccomended Players:**
- VLC Media Player
- Kodi
- IPTV Smarters
**How to Use:**
1. Open your IPTV player.
Import the playlist file or URL.
Start streaming live TV channels!
Feed sources
============
It can be quite hard to find up to date URLs, here's a list of sources:
Finding up-to-date URLs can be difficult. Here are some useful sources:
- https://github.com/iptv-org/iptv/tree/master/streams
- Youtube: As long as the channel is live and its URL doesn't change (check the age of the stream, the number of viewers..)
- Dailymotion: Same criteria as for youtube
- Live streams from platforms like YouTube and Dailymotion, ensuring the stream is active and stable.
Format
======
The m3u8 playlist is generated by `make_playlist.py`, using the `.md` files located in `lists`.
The m3u8 playlist is generated by `make_playlist.py`, using the `.md` files in the `lists` directory.
Each .md file represesnts a group. The `<h1>` line is used as the group title.
@ -153,7 +188,16 @@ Issues
Only create issues for bugs and feature requests.
Do not create issues to add/edit or to remove channels. If you want to add/edit/remove channels, create a pull request directly.
Do not create issues for adding, editing, or removing channels. For these, please submit a pull request.
Contributing
======
**Contributions are welcome! To contribute:**
1. Fork the repository.
2. Create a new branch for your changes.
3. Submit a pull request with your updates.
Pull Requests
=============
@ -175,10 +219,15 @@ To add a new channel, make a Pull Request.
**Removing a Channel**
To remove a channel, make a Pull Request.
- To remove a channel, make a Pull Request.
In your Pull Request you need to provide information to show that the channel is only available via a private paid subscription.
- In your Pull Request you need to provide information to show that the channel is only available via a private paid subscription.
Note: Public taxes (whether national or regional, whether called TV License or not) do not constitute a private paid subscription.
- Note: Public taxes (whether national or regional, whether called TV License or not) do not constitute a private paid subscription.
If a stream is broken, simply move the channel to the invalid category and replace `[>]` with `[x]` in the url column.
- If a stream is broken, simply move the channel to the invalid category and replace `[>]` with `[x]` in the url column.
License
======
This project currently does not have a license. Please clarify the licensing terms before using or contributing.

View File

@ -35,7 +35,6 @@
| 29 | TV5 Monde Info | [>](https://ott.tv5monde.com/Content/HLS/Live/channel(info)/index.m3u8) | <img height="20" src="https://i.imgur.com/NcysrWH.png"/> | TV5MondeInfo.fr |
| 30 | TV5 Monde FBS | [>](https://ott.tv5monde.com/Content/HLS/Live/channel(fbs)/index.m3u8) | <img height="20" src="https://i.imgur.com/uPmwTo9.png"/> | TV5MondeFranceBelgiumSwitzerland.fr |
| 31 | TV5 Monde Europe | [>](https://ott.tv5monde.com/Content/HLS/Live/channel(europe)/index.m3u8) | <img height="20" src="https://i.imgur.com/uPmwTo9.png"/> | TV5MondeEurope.fr |
| 0 | RT France Ⓖ | [>](https://rt-fra.rttv.com/dvr/rtfrance/playlist.m3u8) | <img height="20" src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/RT-France-logo.svg/512px-RT-France-logo.svg.png"/> | RTFrance.fr |
<h2>Unreliable (other)</h2>
@ -46,7 +45,7 @@
| 15 | BFM TV | [x](https://bfmtvalive1-a.akamaihd.net/r8ef15893bf3d4c2db0105218bdfe87f4/eu-central-1/876450610001/playlist.m3u8) | <img height="20" src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Logo_BFM_TV_%282019%29.png/53px-Logo_BFM_TV_%282019%29.png"/> | BFMTV.fr |
| 18 | Gulli | [x](https://d13anarbtxy8c5.cloudfront.net/6play/short/clr/gulli/sdindex.m3u8) | <img height="20" src="https://upload.wikimedia.org/wikipedia/fr/thumb/4/43/18._Gulli.png/57px-18._Gulli.png"/> | Gulli.fr |
| 22 | 6ter | [x]() | <img height="20" src="https://upload.wikimedia.org/wikipedia/fr/thumb/a/a9/6ter_2012.png/73px-6ter_2012.png"/> | 6ter.fr |
| 26 | LCI | [x](https://sv0.data-stream.top/hls/lci.m3u8) | <img height="20" src="https://upload.wikimedia.org/wikipedia/fr/thumb/3/38/LCI_-_Logo_%28Ao%C3%BBt_2017%29.svg/62px-LCI_-_Logo_%28Ao%C3%BBt_2017%29.svg.png"/> | LCI.fr |
| 26 | LCI | [x](https://sv0.data-stream.top/hls/lci.m3u8) | <img height="20" src="https://upload.wikimedia.org/wikipedia/fr/8/85/LCI.png/> | LCI.fr |
<h2>Unreliable (tntdirect)</h2>

View File

@ -17,6 +17,10 @@ https://en.wikipedia.org/wiki/List_of_4K_channels_in_India
| 8 | DD Kisan Ⓨ | [>](https://www.youtube.com/@DDKisan/live) | <img height="20" src="https://i.imgur.com/x56WJEa.png" /> | DDKisan.in |
| 9 | DD Urdu Ⓨ | [>](https://www.youtube.com/@DDUrdu/live) | <img height="20" src="https://i.imgur.com/OiQPS34.png" /> | DDUrdu.in |
| 10 | India Today Ⓨ | [>](https://www.youtube.com/watch?v=sYZtOFzM78M) | <img height="20" src="https://i.imgur.com/C7KK3Fd.png" /> | IndiaToday.in |
| 11 | Aaj Tak Ⓨ | [>](https://www.youtube.com/watch?v=Nq2wYlWFucg) | <img height="20" src="https://upload.wikimedia.org/wikipedia/commons/2/28/Aaj_tak_logo.png" /> | AajTak.in |
| 12 | India TV Ⓨ | [>](https://www.youtube.com/watch?v=e1FIApIafWE) | <img height="20" src="https://upload.wikimedia.org/wikipedia/en/thumb/6/60/India_tv_logo-en.png/500px-India_tv_logo-en.png" /> | IndiaTV.in |
| 13 | TV9 Bharatvarsh Ⓨ | [>](https://www.youtube.com/watch?v=nSpwwcHVp80) | <img height="20" src="https://upload.wikimedia.org/wikipedia/en/thumb/a/a6/TV9_Bharatvarsh.svg/500px-TV9_Bharatvarsh.svg.png" /> | TV9Bharatvarsh.in |
| 14 | Republic Bharat Ⓨ | [>](https://www.youtube.com/watch?v=3DbTO_AMhhc) | <img height="20" src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Republic_Bharat_logo.svg/500px-Republic_Bharat_logo.svg.png" /> | RepublicBharat.in |
<h2>Invalid</h2>

View File

@ -74,9 +74,9 @@ https://ru.wikipedia.org/wiki/Цифровоеелевидение_в_Рос
| 0 | Небеса ТВ7 Ⓢ | [>](https://vod.tv7.fi/tv7-ru/tv7-ru.smil/playlist.m3u8) | <img height="20" src="https://www.nebesatv7.com/wp-content/themes/tv7-theme/assets/img/logo_nebesa_short.png"/> | NebesaTV7.ru |
| 0 | Север | [>](https://live.mediacdn.ru/sr1/sever/playlist.m3u8) | <img height="20" src="https://i.imgur.com/sTOQLYl.png"/> | Sever.ru |
| 0 | Смотрим - Детям | [x]() | <img height="20" src="https://cdn-st1.smotrim.ru/vh/pictures/r/424/215/2.png"/> |
| 0 | Смотрим - Мелодрамы | [>](https://live-vgtrksmotrim.cdnvideo.ru/vgtrksmotrim/smotrim-live-02.smil/playlist.m3u8) | <img height="20" src="https://cdn-st1.smotrim.ru/vh/pictures/r/456/967/6.png"/> |
| 0 | Смотрим - Тайны | [>](https://live-vgtrksmotrim.cdnvideo.ru/vgtrksmotrim/smotrim-live-07.smil/playlist.m3u8) | <img height="20" src="https://cdn-st3.smotrim.ru/vh/pictures/r/456/396/2.png"/> |
| 0 | Смотрим - Честный Детектив | [>](https://live-vgtrksmotrim.cdnvideo.ru/vgtrksmotrim/smotrim-live-01.smil/playlist.m3u8) | <img height="20" src="https://cdn-st3.smotrim.ru/vh/pictures/r/444/241/8.png"/> |
| 0 | Смотрим: Мелодрамы | [>](https://live-vgtrksmotrim.cdnvideo.ru/vgtrksmotrim/smotrim-live-02.smil/playlist.m3u8) | <img height="20" src="https://cdn-st1.smotrim.ru/vh/pictures/r/456/967/6.png"/> |
| 0 | Смотрим: Тайны | [>](https://live-vgtrksmotrim.cdnvideo.ru/vgtrksmotrim/smotrim-live-07.smil/playlist.m3u8) | <img height="20" src="https://cdn-st3.smotrim.ru/vh/pictures/r/456/396/2.png"/> |
| 0 | Смотрим: Честный Детектив | [>](https://live-vgtrksmotrim.cdnvideo.ru/vgtrksmotrim/smotrim-live-01.smil/playlist.m3u8) | <img height="20" src="https://cdn-st3.smotrim.ru/vh/pictures/r/444/241/8.png"/> |
| 0 | Соловьёв Live | [>](https://player.smotrim.ru/iframe/stream/live_id/63338) | <img height="20" src="https://i.imgur.com/v0OYe1d.png"/> | SolovyovLive.ru |
| 0 | Ю Ⓢ | [>](https://strm.yandex.ru/kal/utv/utv0.m3u8) | <img height="20" src="https://upload.wikimedia.org/wikipedia/ru/a/ac/%D0%9B%D0%BE%D0%B3%D0%BE%D1%82%D0%B8%D0%BF_%D1%82%D0%B5%D0%BB%D0%B5%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB%D0%B0_%C2%AB%D0%AE%C2%BB_%28%D1%81_3_%D1%81%D0%B5%D0%BD%D1%82%D1%8F%D0%B1%D1%80%D1%8F_2018_%D0%B3%D0%BE%D0%B4%D0%B0%29.png"/> | U.ru |

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