diff --git a/m3u2strm.php b/m3u2strm.php new file mode 100644 index 0000000..2e65b6a --- /dev/null +++ b/m3u2strm.php @@ -0,0 +1,211 @@ + $this->clean_filename(trim(rtrim(preg_replace("'\.'", " ", $n[1]), ' - '))), + 'season' => str_pad(intval($n[3], 10), 2, '0', STR_PAD_LEFT), + 'episode' => str_pad(intval($n[4], 10), 2, '0', STR_PAD_LEFT) + ]; + } elseif (preg_match("'^(.+)\.*S([0-9]+).*E([0-9]+).*$'i", $filename, $n)) { + return [ + 'name' => $this->clean_filename(trim(rtrim(preg_replace("'\.'", " ", $n[1]), ' - '))), + 'season' => str_pad(intval($n[2], 10), 2, '0', STR_PAD_LEFT), + 'episode' => str_pad(intval($n[3], 10), 2, '0', STR_PAD_LEFT) + ]; + } else { + return [ + 'name' => $this->clean_filename($filename), + 'season' => '', + 'episode' => '' + ]; + } + } + + // Extract movie title and year from filename + private function extract_movie_info ($filename) { + if (preg_match("/^.+?(?=\\s*[(.]?(\\d{4}))/mi", $filename, $n)) { + return [ + 'name' => $this->clean_filename(trim(rtrim($n[0], '-'))), + 'year' => $n[1] + ]; + } else { + return [ + 'name' => $this->clean_filename($filename), + 'year' => '' + ]; + } + } + + // Extract year from filename (movie/serie) + private function extract_year ($filename) { + if (preg_match("(19\d{2}|20(?:0\d|1[0-9]|2[0-9]))", $filename, $n)) { + return $n[0]; + } + return ''; + } + + // Parse M3U file to array + public function parse_m3u ($url) { + $content = preg_split('/\r\n|\r|\n/', file_get_contents($url)); + $output = []; + $entry = []; + $group = ''; + foreach ($content as $line) { + if (preg_match('/\#EXTM3U/i', $line)) { + continue; + } + if (preg_match('/\#EXTINF/i', $line)) { + $group = ''; + if (preg_match('/\#EXTINF:(?P-?\d*\.?\d+)/i', $line, $result)) { + $entry['play_length'] = $result['play_length']; + } + if (preg_match('/(?<=channel-id=")(?P.*?)(?=")/i', $line, $result)) { + $entry['channel_id'] = $result['channel_id']; + } + if (preg_match('/(?<=radio=")(?P.*?)(?=")/i', $line, $result)) { + $entry['radio'] = json_decode($result['radio']) == true; + } + if (preg_match('/(?<=tvg-id=")(?P.*?)(?=")/i', $line, $result)) { + $entry['tvg_id'] = $result['tvg_id']; + } + if (preg_match('/(?<=tvg-name=")(?P.*?)(?=")/i', $line, $result)) { + $entry['tvg_name'] = $result['tvg_name']; + } + if (preg_match('/(?<=tvg-logo=")(?P.*?)(?=")/i', $line, $result)) { + $entry['tvg_logo'] = $result['tvg_logo']; + } + if (preg_match('/(?<=tvg-shift=")(?P.*?)(?=")/i', $line, $result)) { + $entry['tvg_shift'] = $result['tvg_shift']; + } + if (preg_match('/(?<=tvg-chno=")(?P.*?)(?=")/i', $line, $result)) { + $entry['tvg_chno'] = $result['tvg_chno']; + } + if (preg_match('/(?<=group-title=")(?P.*?)(?=")/i', $line, $result)) { + $group = $result['group_title']; + } + if (preg_match('/(?<=parent-code=")(?P.*?)(?=")/i', $line, $result)) { + $entry['parent_code'] = $result['parent_code']; + } + if (preg_match('/(?<=audio-track=")(?P.*?)(?=")/i', $line, $result)) { + $entry['audio_track'] = $result['audio_track']; + } + if (preg_match('/(?<=,)(?P.*?)$/i', $line, $result)) { + $entry['name'] = $result['name']; + } else { + $entry['name'] = ''; + } + } elseif (preg_match("#\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))#iS", $line)) { + $entry['url'] = $line; + $entry['type'] = 'Live'; + if (preg_match('/(?P[0-9a-z]+)(?:[\?#]|$)/i', $line, $result)) { + $entry['ext'] = $result['ext']; + } + if (preg_match('/.*\/play\/vod\//', $line)) { + $entry['type'] = 'VOD'; + } + if (preg_match('/.*\/movie\//i', $line)) { + $entry['type'] = 'Movie'; + } + if (preg_match('/.*\/series\//i', $line) || preg_match('/.*\/tvshow\//i', $line)) { + $entry['type'] = 'Series'; + } + if (array_key_exists($group, $output)) { + array_push($output[$group], $entry); + } else { + $output[$group] = [$entry]; + } + $entry = []; + } + } + return $output; + } + + private function absolute_filename ($directory, $filename) { + return str_replace('\'', '', join(DIRECTORY_SEPARATOR, array($directory, $filename))); + } + + // Convert to strm files + public function convert_strm ($url, $directory, $name_tag = 'tvg_name') { + $playlist = $this->parse_m3u($url); + foreach ($playlist as $group => $stations) { + foreach ($stations as $station) { + if ($station['type'] === 'Movie') { + $movie = $this->extract_movie_info($station[$name_tag]); + $movie_name = !empty($movie['year']) ? sprintf('%s/%s (%s).strm', $this->clean_filename($group), $movie['name'], $movie['year']) : sprintf('%s/%s.strm', $this->clean_filename($group), $movie['name']); + $this->create_strm_file($this->absolute_filename($directory, $movie_name), $station['url']); + } + if ($station['type'] === 'Series') { + $series = $this->extract_series_info($station[$name_tag]); + $year = $this->extract_year($station[$name_tag]); + $series_season = !empty($series['season']) ? sprintf('S%s', $series['season']) : ''; + $series_episod = !empty($series['episode']) ? sprintf('E%s', $series['episode']) : ''; + $series_filenm = !empty($year) ? sprintf('%s (%s) %s%s', $series['name'], $year, $series_season, $series_episod) : sprintf('%s %s%s', $series['name'], $series_season, $series_episod); + $series_folder = !empty($year) ? sprintf('%s/%s (%s)/%s.strm', $this->clean_filename($group), $series['name'], $year, trim($series_filenm)) : sprintf('%s/%s/%s.strm', $this->clean_filename($group), $series['name'], trim($series_filenm)); + $this->create_strm_file($this->absolute_filename($directory, $series_folder), $station['url']); + } + } + } + } +} + +$converter = new converter(); +echo "Filename / URL: " . $_GET['filename'] . PHP_EOL . "Output Directory: " . $_GET['directory'] . PHP_EOL; +$converter->convert_strm($_GET['filename'], $_GET['directory']); \ No newline at end of file diff --git a/m3u2strm.sh b/m3u2strm.sh new file mode 100644 index 0000000..8d1e324 --- /dev/null +++ b/m3u2strm.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +# M3U 2 STRM - v1.0.0 (November 2020) +# Coded by: ERDesigns - Ernst Reidinga (c) 2020 + +trap 'printf "\n";stop;exit 1;clear;' 2 + + +dependencies () { + # Check if PHP is installed + command -v php > /dev/null 2>&1 || { + echo >&2 "PHP is required! Please install PHP first and try again."; + exit 1; + } + # Check if CURL is installed + command -v curl > /dev/null 2>&1 || { + echo >&2 "CURL is required! Please install PHP first and try again."; + exit 1; + } +} + +banner () { + printf "\e[1;34m \e[0m\n" + printf "\e[1;34m ███╗ ███╗██████╗ ██╗ ██╗██████╗ ███████╗████████╗██████╗ ███╗ ███╗ \e[0m\n" + printf "\e[1;34m ████╗ ████║╚════██╗██║ ██║╚════██╗██╔════╝╚══██╔══╝██╔══██╗████╗ ████║ \e[0m\n" + printf "\e[1;34m ██╔████╔██║ █████╔╝██║ ██║ █████╔╝███████╗ ██║ ██████╔╝██╔████╔██║ \e[0m\n" + printf "\e[1;34m ██║╚██╔╝██║ ╚═══██╗██║ ██║██╔═══╝ ╚════██║ ██║ ██╔══██╗██║╚██╔╝██║ \e[0m\n" + printf "\e[1;34m ██║ ╚═╝ ██║██████╔╝╚██████╔╝███████╗███████║ ██║ ██║ ██║██║ ╚═╝ ██║ \e[0m\n" + printf "\e[1;34m ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ \e[0m\n" + printf "\n" + printf "\e[1;34m .:.:.\e[0m\e[1;94m By Ernst Reidinga - ERDesigns \e[0m\e[1;34m.:.:.\e[0m\n" + printf "\n" +} + +menu () { + printf "\e[1;34m [\e[0m\e[1;31m01\e[0m\e[1;34m]\e[0m\e[1;94m Convert local M3U file\e[0m\n" + printf "\e[1;34m [\e[0m\e[1;31m02\e[0m\e[1;34m]\e[0m\e[1;94m Convert remote M3U file\e[0m\n" + printf "\e[1;34m [\e[0m\e[1;31m03\e[0m\e[1;34m]\e[0m\e[1;94m Command Line options\e[0m\n" + printf "\n" + printf "\e[1;34m ------------------------------------------------------------------------------\n" + printf "\e[1;34m [\e[0m\e[1;31m99\e[0m\e[1;34m]\e[0m\e[1;31m Exit\e[0m\n" + + # Read selection + read -p $'\n\e[1;34m [\e[0m\e[1;91m*\e[0m\e[1;34m] Enter your selection: \e[0m' option + + # Read filename / URL + if [[ $option == 1 || $option == 01 ]]; then + mode="LOCAL" + read -p $'\e[1;34m [\e[0m\e[1;91m*\e[0m\e[1;34m] Local M3U filename: \e[0m' filename + filename=(${filename[@]//\'/}) + if [[ ! -e $filename ]]; then + printf "\e[1;34m [!]\e[31m File DOES NOT exist!\e[0m\n" + sleep 1 + clear + banner + menu + fi + elif [[ $option == 2 || $option == 02 ]]; then + mode="REMOTE" + read -p $'\e[1;34m [\e[0m\e[1;91m*\e[0m\e[1;34m] Remote M3U URL: \e[0m' filename + regex='^(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]\.[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]$' + if [[ ! $filename =~ $regex ]]; then + printf "\e[1;34m [!]\e[31m Invalid URL!\e[0m\n" + sleep 1 + clear + banner + menu + fi + fi + + # Read output directory + if [[ $option == 1 || $option == 01 || $option == 2 || $option == 02 ]]; then + read -p $'\e[1;34m [\e[0m\e[1;91m*\e[0m\e[1;34m] Output directory: \e[0m' directory + if [[ $directory == "" ]]; then + printf "\e[1;34m [!]\e[31m Please enter a valid directory!\e[0m\n" + clear + banner + menu + fi + start + fi + + # Command Line options + if [[ $option == 3 || $option == 03 ]]; then + printf "\n" + printf "\e[1;34m This script takes 2 parameters, you can run this script from the CRON with these parameters: \n" + printf "\n" + printf "\e[1;34m [\e[0m\e[1;31m Option 1\e[0m\e[1;34m]\e[0m\e[1;94m M3U filename or URL\e[0m\n" + printf "\e[1;34m [\e[0m\e[1;31m Option 2\e[0m\e[1;34m]\e[0m\e[1;94m Output directory\e[0m\n" + printf "\n" + printf "\e[1;34m [\e[0m\e[1;31m Example:\e[0m\e[1;34m]\e[0m\e[1;94m sudo bash m3u2strm.sh http://my-provider.com/get.php?username=abc&password=def&type=m3u_plus /home/username/desktop/strm \e[0m\n" + elif [[ $option == 99 ]]; then + # User wants to exit + exit 1 + else + # No valid input! + printf "\e[1;34m [!]\e[31m Invalid selection!\e[0m\n" + sleep 1 + clear + banner + menu + fi + +} + +start () { + printf "\n" + printf "\e[1;34m Please wait while converting the M3U to STRM files. \n" + currentdir=$(dirname "$0") + php -f "$currentdir/m3u2strm.php" filename=$filename directory=$directory > "$currentdir/log.log" + wait + printf "\e[1;34m All done! \n" + sleep 20 +} + +stop () { + PHP=$(ps aux | grep -o "php" | head -n1) + # Kill PHP + if [[ $PHP == *'php'* ]]; then + pkill -f -2 php > /dev/null 2>&1 + killall -2 php > /dev/null 2>&1 + fi +} + +if [[ ! $1 == "" && ! $2 == "" ]]; then + filename="$1" + directory="$2" + start +else + clear + banner + menu +fi \ No newline at end of file