644 |
Multi-Service M3U8 Playlist
645 |
Script Access URL
646 |
647 | Use the following URL to access the hosted script. Replace the
648 | ADD_REGION and
649 | ADD_SERVICE placeholders with your desired values:
650 |
651 |
652 |
653 | After customizing the URL by replacing the ADD_REGION and ADD_SERVICE placeholders (e.g., \`us\` for the US region and \`PlutoTV\` for the service), copy the complete URL and paste it into the "Add Playlist" or "M3U8 URL" section of your IPTV application. Once added, the app will load both the channels and the guide information.
654 |
655 |
656 |
Available Service Parameter Values
657 |
658 | - Plex
659 | - Roku
660 | - SamsungTVPlus
661 | - PlutoTV
662 | - PBS
663 | - PBSKids
664 | - Stirr
665 | - Tubi
666 |
667 |
668 |
Available Region Parameter Values
669 |
670 | all (for all regions)
671 | ar (Argentina)
672 | br (Brazil)
673 | ca (Canada)
674 | cl (Chile)
675 | de (Germany)
676 | dk (Denmark)
677 | es (Spain)
678 | fr (France)
679 | gb (United Kingdom)
680 | mx (Mexico)
681 | no (Norway)
682 | se (Sweden)
683 | us (United States)
684 |
685 |
686 |
Available Sorting Parameter Values (optional)
687 |
688 | name (default): Sorts the channels alphabetically by their name.
689 | chno: Sorts the channels by their assigned channel number.
690 |
691 |
692 |
693 |
EPG for TV Guide Information
694 |
695 |
The EPG URLs are embedded directly within the playlists. If you'd prefer to manually add the EPG guide, you can find the relevant URLs for each service on this page.
696 |
697 |
Disclaimer:
698 |
699 |
This repository has no control over the streams, links, or the legality of the content provided by Pluto, Samsung, Stirr, Tubi, Plex, PBS, and Roku. Additionally, this script simply converts the JSON files provided by i.mjh.nz into an M3U8 playlist. It is the end user's responsibility to ensure the legal use of these streams. We strongly recommend verifying that the content complies with the laws and regulations of your country before use.
700 |
701 |
704 |
705 |
711 |
712 | `);
713 | }
714 |
715 | // function to fetch and decompresses gzipped JSON files
716 | async function fetchGzippedJson(url, maxRedirects = 5) {
717 | return new Promise((resolve, reject) => {
718 | const makeRequest = (currentUrl, redirectsRemaining) => {
719 | https.get(currentUrl, (response) => {
720 | if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
721 | if (redirectsRemaining <= 0) {
722 | reject(new Error('Too many redirects'));
723 | return;
724 | }
725 | const redirectUrl = new URL(response.headers.location, currentUrl).href;
726 | makeRequest(redirectUrl, redirectsRemaining - 1);
727 | return;
728 | }
729 |
730 | if (response.statusCode !== 200) {
731 | reject(new Error(`Request failed. Status code: ${response.statusCode}`));
732 | return;
733 | }
734 |
735 | const gunzip = zlib.createGunzip();
736 | const chunks = [];
737 | response.pipe(gunzip);
738 |
739 | gunzip.on('data', (chunk) => chunks.push(chunk));
740 | gunzip.on('end', () => {
741 | try {
742 | resolve(JSON.parse(Buffer.concat(chunks).toString('utf-8')));
743 | } catch (error) {
744 | reject(new Error('Failed to parse JSON: ' + error.message));
745 | }
746 | });
747 | gunzip.on('error', (error) => reject(error));
748 | }).on('error', (error) => reject(error));
749 | };
750 |
751 | makeRequest(url, maxRedirects);
752 | });
753 | }
754 |
755 | server.listen(port, hostname, () => {
756 | console.log('Server running at http://localhost:4242/');
757 | });
758 |
--------------------------------------------------------------------------------