├── BC_Model.txt ├── screenshot0.jpg ├── screenshot1.jpg ├── Requirements.txt ├── config.ini ├── bc.bat ├── README.md ├── bongacams_sl.py ├── bongacams_yt.py ├── bongacams_ls.py ├── bongacams_ffmpeg.py └── bongacams.py /BC_Model.txt: -------------------------------------------------------------------------------- 1 | titanic-tits 2 | evadreams 3 | AliceandSven 4 | agvhotink 5 | One111Love 6 | -------------------------------------------------------------------------------- /screenshot0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/horacio9a/streamlink-bongacams/HEAD/screenshot0.jpg -------------------------------------------------------------------------------- /screenshot1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/horacio9a/streamlink-bongacams/HEAD/screenshot1.jpg -------------------------------------------------------------------------------- /Requirements.txt: -------------------------------------------------------------------------------- 1 | streamlink==0.9.0 2 | livestreamer==1.12.2 3 | DateTime==4.1.1 4 | requests==2.21.0 5 | Command==0.1.0 6 | colorama==0.4.0 7 | termcolor==1.1.0 8 | configparser==3.5.0 -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [folders] 2 | output_folder_BC: C:/Videos/BC/ 3 | output_folder_C4: C:/Videos/Cam4/ 4 | [files] 5 | rtmpdump: C:/Windows/rtmpdump.exe 6 | ffplay: C:/Windows/ffplay.exe 7 | ffmpeg: C:/Windows/ffmpeg.exe 8 | youtube: C:/Windows/youtube-dl.exe 9 | livestreamer: C:/Livestreamer/livestreamer.exe 10 | streamlink: C:/Streamlink/bin/streamlink.exe 11 | -------------------------------------------------------------------------------- /bc.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL EnableDelayedExpansion 3 | :START 4 | SET OUT_DIR=C:/Videos/BC/ 5 | CLS 6 | SET n=0 7 | FOR /F "tokens=*" %%A IN (C:\Windows\BC_Model.txt) DO ( 8 | SET /A n=n+1 9 | SET _fav!n!=%%A 10 | ECHO !n! %%A 11 | ) 12 | ECHO. 13 | SET /P MODEL=Choose BC MODEL Name (%M% %MODEL%): 14 | FOR /L %%f IN (1,1,!n!) DO ( 15 | IF /I '%MODEL%'=='%%f' SET M=%%f 16 | ) 17 | SET n=0 18 | FOR /F "tokens=*" %%A IN (C:\Windows\BC_Model.txt) DO ( 19 | SET /A n=n+1 20 | IF !n!==%M% SET MODEL=%%A 21 | ) 22 | ECHO. 23 | SET /P MODE=EXIT(3) START(2) BC-SL-R(24/7)(1) BC-SL(0)(ENTER)(%MODE%): 24 | IF "%MODE%"=="" GOTO BC-SL 25 | IF "%MODE%"=="0" GOTO BC-SL 26 | IF "%MODE%"=="1" GOTO BC-SL-R 27 | IF "%MODE%"=="2" GOTO START 28 | IF "%MODE%"=="3" GOTO EXIT 29 | ECHO. 30 | :BC-SL 31 | COLOR 0F 32 | SET MODELNAME=%MODEL% ######### %M% ########################### 33 | SET _MODEL_=%MODELNAME:~0,33% 34 | ECHO. 35 | CLS && ECHO ################################################### 36 | ECHO ### BC-SL ####### R E C O R D I N G ############### 37 | ECHO ################# %_MODEL_% 38 | ECHO ################################################### 39 | ECHO. 40 | cd C:/Python27/Scripts 41 | START STREAMLINK "https://en.bongacams.com/%MODEL%/" 42 | cd %OUT_DIR% 43 | FOR /R %%F in (*) DO IF %%~ZF LSS 40 DEL "%%F" 44 | PAUSE 45 | GOTO START 46 | :BC-SL-R 47 | COLOR 0F 48 | SET MODELNAME=%MODEL% ######### %M% ########################### 49 | SET _MODEL_=%MODELNAME:~0,33% 50 | ECHO. 51 | CLS && ECHO ################################################### 52 | ECHO ### BC-SL-R ##### R E C O R D I N G ###### 24/7 ### 53 | ECHO ################# %_MODEL_% 54 | ECHO ################################################### 55 | ECHO. 56 | cd C:/Python27/Scripts 57 | STREAMLINK "https://en.bongacams.com/%MODEL%/" 58 | cd %OUT_DIR% 59 | FOR /R %%F in (*) DO IF %%~ZF LSS 40 DEL "%%F" 60 | TIMEOUT 30 61 | GOTO BC-SL-R 62 | :EXIT 63 | GOTO :EOF 64 | ENDLOCAL 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # streamlink-bongacams 2 | streamlink-bongacams lets you follow and archive your selected models shows on www.bongacams.com and Bongacams white labels sites. 3 | BC streamlink-bongacams plugin that you see here use the SM_Model.txt where is a list of your favourites BC models. 4 | You don't need to be registered bongacams user for recording models with this streamlink-bongacams plugin. 5 | For this plugin I used python code from @sdfwv, thank you very much for that. I adapted that plugin for ffmpeg recordng. 6 | 7 | Requirements 8 | ============ 9 | 1. Download here [Python 2.7.14](https://www.python.org/ftp/python/2.7.14/python-2.7.14.msi) instalation. Those who need to install python should watch this [video](https://www.youtube.com/watch?v=QYUBz4mrnFU) 10 | 2. Last version of [streamlink](https://github.com/streamlink/streamlink). 11 | 3. Last version of [ffmpeg and ffplay](https://ffmpeg.zeranoe.com/builds/). Default location is C:/Windows, otherwise `config.cfg` must be edited. 12 | 13 | Setup 14 | ===== 15 | 1. Install requirements `pip install -r Requirements.txt`. Streamlink can be installed as a stand-alone program but my recommendation is to install it as a python module along with other modules with command `pip install streamlink==0.9.0` 16 | 2. Download and unpack the [code](https://codeload.github.com/horacio9a/streamlink-bongacams/zip/master). 17 | 3. BC streamlink-bongacams plugin `bongacams.py` must replace old plugin in `C:/Python27/Lib/site-packages/streamlink/plugins/bongacams.py`. It should be noted that bongacams can only be one plugin and therefore need to be renamed bongacams_.py in bongacams.py if you need 'online' version. 18 | 4. Batch script bc.bat can be anywhere (default is `C:/Windows`). 19 | 5. BC favourite list `BC_Model.txt` can be anywhere (default is `C:/Windows`). 20 | 6. `config.cfg` must be in `C:/Python27/Scripts/config.cfg`. Edit `config.cfg` depending on your situation or accept default data. You must enter the correct location on your computer for all the files that are there. In that case, those files do not have to be in the path. 21 | 22 | Running & Output 23 | ================ 24 | It's best to use 'Command Promt' window to install `Requirements.txt`. 25 | For easier use of this plugin it would be good to make a shortcut and put it in the task bar for easier startup. 26 | However, if you want to record a certain model permanently (24/7), then you need to use `bc.bat`, options number `1` and `0` for online use. 27 | For permanently recording more than one model at the same time you need to start another copy of `bc.bat`. 28 | Recording is best abort with Ctrl-C or by clicking 'x' at the top right corner of the script window If Ctrl-C does not react. 29 | 30 | The look of the default `bongacams.py` for 24/7 permanent one model tracking 31 | 32 | ![alt screenshot](./screenshot0.jpg) 33 | 34 | If you don't want to keep track of a specific model, then you can use `bongacams_.py` (must be renamed to `bongacams.py`) which has many options and it is possible to use the same script window to start many models. 35 | 36 | ![alt screenshot](./screenshot1.jpg) 37 | -------------------------------------------------------------------------------- /bongacams_sl.py: -------------------------------------------------------------------------------- 1 | # Bongacams Streamlink Remote 24/7 Plugin v.1.0.4 by @horacio9a for Python 2.7.16 - Credits also to @sdfwv 2 | # coding: utf-8 3 | 4 | import sys, os, json, re, time, datetime, random, command 5 | from streamlink.compat import urljoin, urlparse, urlunparse 6 | from streamlink.exceptions import PluginError, NoStreamsError 7 | from streamlink.plugin.api import validate, http, useragents 8 | from streamlink.plugin import Plugin 9 | from streamlink.stream import HLSStream 10 | from streamlink.utils import update_scheme 11 | from colorama import init, Fore, Back, Style 12 | from termcolor import colored 13 | import ConfigParser 14 | config = ConfigParser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | init() 18 | 19 | CONST_AMF_GATEWAY_LOCATION = '/tools/amf.php' 20 | CONST_AMF_GATEWAY_PARAM = 'x-country' 21 | CONST_DEFAULT_COUNTRY_CODE = 'en' 22 | 23 | CONST_HEADERS = {} 24 | CONST_HEADERS['User-Agent'] = useragents.CHROME 25 | 26 | url_re = re.compile(r'(http(s)?://)?(\w{2}.)?(bongacams\.com)/([\w\d_-]+)') 27 | 28 | amf_msg_schema = validate.Schema( 29 | {'status': 'success','userData': 30 | {'username': validate.text},'localData': 31 | {'videoServerUrl': validate.text},'performerData': 32 | {'username': validate.text,'displayName': validate.text,'userId': validate.get}}) 33 | 34 | class bongacams(Plugin): 35 | @classmethod 36 | def can_handle_url(self, url): 37 | return url_re.match(url) 38 | 39 | def _get_streams(self): 40 | match = url_re.match(self.url) 41 | 42 | stream_page_scheme = 'https' 43 | stream_page_domain = match.group(4) 44 | stream_page_path = match.group(5) 45 | country_code = CONST_DEFAULT_COUNTRY_CODE 46 | 47 | # create http session and set headers 48 | http_session = http 49 | http_session.headers.update(CONST_HEADERS) 50 | 51 | # get cookies 52 | r = http_session.get(urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) 53 | 54 | # redirect to profile page means stream is offline 55 | if '/profile/' in r.url: 56 | print(colored('\n => Performer is OFFLINE <=','yellow','on_red')) 57 | print(colored('\n => END <= ', 'yellow','on_blue')) 58 | time.sleep(6) 59 | sys.exit() 60 | 61 | if not r.ok: 62 | self.logger.debug('Status code for {0}: {1}', r.url, r.status_code) 63 | raise NoStreamsError(self.url) 64 | if len(http_session.cookies) == 0: 65 | raise PluginError("Can't get a cookies") 66 | 67 | if urlparse(r.url).netloc != stream_page_domain: 68 | # then redirected to regional subdomain 69 | country_code = urlparse(r.url).netloc.split('.')[0].lower() 70 | 71 | # time to set variables 72 | baseurl = urlunparse((stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) 73 | amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) 74 | stream_page_url = urljoin(baseurl, stream_page_path) 75 | 76 | headers = { 77 | 'User-Agent': useragents.CHROME, 78 | 'Referer': stream_page_url, 79 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 80 | 'X-Requested-With': 'XMLHttpRequest' 81 | } 82 | 83 | data = 'method=getRoomData&args%5B%5D={0}&args%5B%5D=false'.format(stream_page_path) 84 | self.logger.debug('DATA: {0}'.format(str(data))) 85 | # send request and close http-session 86 | r = http_session.post(url=amf_gateway_url, 87 | headers=headers, 88 | params={CONST_AMF_GATEWAY_PARAM: country_code}, 89 | data=data) 90 | http_session.close() 91 | 92 | if r.status_code != 200: 93 | raise PluginError('unexpected status code for {0}: {1}', r.url, r.status_code) 94 | 95 | stream_source_info = amf_msg_schema.validate(json.loads(r.text)) 96 | self.logger.debug('source stream info:\n{0}', stream_source_info) 97 | 98 | if not stream_source_info: 99 | return 100 | 101 | performer = stream_source_info['performerData']['username'] 102 | real_name = stream_source_info['performerData']['displayName'] 103 | performer_id = stream_source_info['performerData']['userId'] 104 | print (colored('\n => Performer => {} <=', 'yellow', 'on_blue')).format(real_name) 105 | print (colored('\n => Performer ID => {} <=', 'yellow', 'on_blue')).format(performer_id) 106 | urlnoproto = stream_source_info['localData']['videoServerUrl'] 107 | urlnoproto = update_scheme('https://', urlnoproto) 108 | hls_url = '{0}/hls/stream_{1}/playlist.m3u8'.format(urlnoproto, performer) 109 | server = re.sub('https://', '', urlnoproto) 110 | print (colored('\n => Server => {} <=', 'yellow', 'on_blue')).format(server) 111 | 112 | if hls_url: 113 | try: 114 | for s in HLSStream.parse_variant_playlist(self.session, hls_url, headers=headers).items(): 115 | timestamp = str(time.strftime('%d%m%Y-%H%M%S')) 116 | path = config.get('folders', 'output_folder_BC') 117 | fn = real_name + '_BC_' + timestamp + '.mp4' 118 | pf = (path + fn) 119 | streamlink = config.get('files', 'streamlink') 120 | print (colored('\n => SL-24/7-REC => {} <=','yellow','on_red')).format(fn) 121 | print 122 | command = ('{} hls://"{}" best -Q -o "{}"'.format(streamlink,hls_url,pf)) 123 | os.system(command) 124 | print(colored(' => END <= ', 'yellow','on_blue')) 125 | sys.exit() 126 | 127 | except Exception as e: 128 | if '404' in str(e): 129 | print(colored('\n => Performer is AWAY or PRIVATE <=','yellow','on_red')) 130 | print(colored('\n => END <= ', 'yellow','on_blue')) 131 | time.sleep(6) 132 | sys.exit() 133 | 134 | __plugin__ = bongacams 135 | -------------------------------------------------------------------------------- /bongacams_yt.py: -------------------------------------------------------------------------------- 1 | # Bongacams Streamlink YOUTUBE-DL Remote 24/7 Plugin v.1.0.4 by @horacio9a for Python 2.7.16 - Credits also to @sdfwv 2 | # coding: utf-8 3 | 4 | import sys, os, json, re, time, datetime, random, command 5 | from streamlink.compat import urljoin, urlparse, urlunparse 6 | from streamlink.exceptions import PluginError, NoStreamsError 7 | from streamlink.plugin.api import validate, http, useragents 8 | from streamlink.plugin import Plugin 9 | from streamlink.stream import HLSStream 10 | from streamlink.utils import update_scheme 11 | from colorama import init, Fore, Back, Style 12 | from termcolor import colored 13 | import ConfigParser 14 | config = ConfigParser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | init() 18 | 19 | CONST_AMF_GATEWAY_LOCATION = '/tools/amf.php' 20 | CONST_AMF_GATEWAY_PARAM = 'x-country' 21 | CONST_DEFAULT_COUNTRY_CODE = 'en' 22 | 23 | CONST_HEADERS = {} 24 | CONST_HEADERS['User-Agent'] = useragents.CHROME 25 | 26 | url_re = re.compile(r'(http(s)?://)?(\w{2}.)?(bongacams\.com)/([\w\d_-]+)') 27 | 28 | amf_msg_schema = validate.Schema( 29 | {'status': 'success','userData': 30 | {'username': validate.text},'localData': 31 | {'videoServerUrl': validate.text},'performerData': 32 | {'username': validate.text,'displayName': validate.text,'userId': validate.get}}) 33 | 34 | class bongacams(Plugin): 35 | @classmethod 36 | def can_handle_url(self, url): 37 | return url_re.match(url) 38 | 39 | def _get_streams(self): 40 | match = url_re.match(self.url) 41 | 42 | stream_page_scheme = 'https' 43 | stream_page_domain = match.group(4) 44 | stream_page_path = match.group(5) 45 | country_code = CONST_DEFAULT_COUNTRY_CODE 46 | 47 | # create http session and set headers 48 | http_session = http 49 | http_session.headers.update(CONST_HEADERS) 50 | 51 | # get cookies 52 | r = http_session.get(urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) 53 | 54 | # redirect to profile page means stream is offline 55 | if '/profile/' in r.url: 56 | print(colored('\n => Performer is OFFLINE <=','yellow','on_red')) 57 | print(colored('\n => END <= ', 'yellow','on_blue')) 58 | time.sleep(6) 59 | sys.exit() 60 | 61 | if not r.ok: 62 | self.logger.debug('Status code for {0}: {1}', r.url, r.status_code) 63 | raise NoStreamsError(self.url) 64 | if len(http_session.cookies) == 0: 65 | raise PluginError("Can't get a cookies") 66 | 67 | if urlparse(r.url).netloc != stream_page_domain: 68 | # then redirected to regional subdomain 69 | country_code = urlparse(r.url).netloc.split('.')[0].lower() 70 | 71 | # time to set variables 72 | baseurl = urlunparse((stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) 73 | amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) 74 | stream_page_url = urljoin(baseurl, stream_page_path) 75 | 76 | headers = { 77 | 'User-Agent': useragents.CHROME, 78 | 'Referer': stream_page_url, 79 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 80 | 'X-Requested-With': 'XMLHttpRequest' 81 | } 82 | 83 | data = 'method=getRoomData&args%5B%5D={0}&args%5B%5D=false'.format(stream_page_path) 84 | self.logger.debug('DATA: {0}'.format(str(data))) 85 | # send request and close http-session 86 | r = http_session.post(url=amf_gateway_url, 87 | headers=headers, 88 | params={CONST_AMF_GATEWAY_PARAM: country_code}, 89 | data=data) 90 | http_session.close() 91 | 92 | if r.status_code != 200: 93 | raise PluginError('unexpected status code for {0}: {1}', r.url, r.status_code) 94 | 95 | stream_source_info = amf_msg_schema.validate(json.loads(r.text)) 96 | self.logger.debug('source stream info:\n{0}', stream_source_info) 97 | 98 | if not stream_source_info: 99 | return 100 | 101 | performer = stream_source_info['performerData']['username'] 102 | real_name = stream_source_info['performerData']['displayName'] 103 | performer_id = stream_source_info['performerData']['userId'] 104 | print (colored('\n => Performer => {} <=', 'yellow', 'on_blue')).format(real_name) 105 | print (colored('\n => Performer ID => {} <=', 'yellow', 'on_blue')).format(performer_id) 106 | urlnoproto = stream_source_info['localData']['videoServerUrl'] 107 | urlnoproto = update_scheme('https://', urlnoproto) 108 | hls_url = '{0}/hls/stream_{1}/playlist.m3u8'.format(urlnoproto, performer) 109 | server = re.sub('https://', '', urlnoproto) 110 | print (colored('\n => Server => {} <=', 'yellow', 'on_blue')).format(server) 111 | 112 | if hls_url: 113 | try: 114 | for s in HLSStream.parse_variant_playlist(self.session, hls_url, headers=headers).items(): 115 | timestamp = str(time.strftime('%d%m%Y-%H%M%S')) 116 | path = config.get('folders', 'output_folder_BC') 117 | fn = real_name + '_BC_' + timestamp + '.ts' 118 | pf = (path + fn) 119 | youtube = config.get('files', 'youtube') 120 | print (colored('\n => YTDL-TS-24/7-REC => {} <=','yellow','on_red')).format(fn) 121 | print 122 | command = ('{} --hls-use-mpegts --no-part {} -q -o {}'.format(youtube,hls_url,pf)) 123 | os.system(command) 124 | print(colored(' => END <= ', 'yellow','on_blue')) 125 | sys.exit() 126 | 127 | except Exception as e: 128 | if '404' in str(e): 129 | print(colored('\n => Performer is AWAY or PRIVATE <=','yellow','on_red')) 130 | print(colored('\n => END <= ', 'yellow','on_blue')) 131 | time.sleep(6) 132 | sys.exit() 133 | 134 | __plugin__ = bongacams 135 | -------------------------------------------------------------------------------- /bongacams_ls.py: -------------------------------------------------------------------------------- 1 | # Bongacams Streamlink LIVESTREAMER Remote 24/7 Plugin v.1.0.4 by @horacio9a for Python 2.7.16 - Credits also to @sdfwv 2 | # coding: utf-8 3 | 4 | import sys, os, json, re, time, datetime, random, command 5 | from streamlink.compat import urljoin, urlparse, urlunparse 6 | from streamlink.exceptions import PluginError, NoStreamsError 7 | from streamlink.plugin.api import validate, http, useragents 8 | from streamlink.plugin import Plugin 9 | from streamlink.stream import HLSStream 10 | from streamlink.utils import update_scheme 11 | from colorama import init, Fore, Back, Style 12 | from termcolor import colored 13 | import ConfigParser 14 | config = ConfigParser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | init() 18 | 19 | CONST_AMF_GATEWAY_LOCATION = '/tools/amf.php' 20 | CONST_AMF_GATEWAY_PARAM = 'x-country' 21 | CONST_DEFAULT_COUNTRY_CODE = 'en' 22 | 23 | CONST_HEADERS = {} 24 | CONST_HEADERS['User-Agent'] = useragents.CHROME 25 | 26 | url_re = re.compile(r'(http(s)?://)?(\w{2}.)?(bongacams\.com)/([\w\d_-]+)') 27 | 28 | amf_msg_schema = validate.Schema( 29 | {'status': 'success','userData': 30 | {'username': validate.text},'localData': 31 | {'videoServerUrl': validate.text},'performerData': 32 | {'username': validate.text,'displayName': validate.text,'userId': validate.get}}) 33 | 34 | class bongacams(Plugin): 35 | @classmethod 36 | def can_handle_url(self, url): 37 | return url_re.match(url) 38 | 39 | def _get_streams(self): 40 | match = url_re.match(self.url) 41 | 42 | stream_page_scheme = 'https' 43 | stream_page_domain = match.group(4) 44 | stream_page_path = match.group(5) 45 | country_code = CONST_DEFAULT_COUNTRY_CODE 46 | 47 | # create http session and set headers 48 | http_session = http 49 | http_session.headers.update(CONST_HEADERS) 50 | 51 | # get cookies 52 | r = http_session.get(urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) 53 | 54 | # redirect to profile page means stream is offline 55 | if '/profile/' in r.url: 56 | print(colored('\n => Performer is OFFLINE <=','yellow','on_red')) 57 | print(colored('\n => END <= ', 'yellow','on_blue')) 58 | time.sleep(6) 59 | sys.exit() 60 | 61 | if not r.ok: 62 | self.logger.debug('Status code for {0}: {1}', r.url, r.status_code) 63 | raise NoStreamsError(self.url) 64 | if len(http_session.cookies) == 0: 65 | raise PluginError("Can't get a cookies") 66 | 67 | if urlparse(r.url).netloc != stream_page_domain: 68 | # then redirected to regional subdomain 69 | country_code = urlparse(r.url).netloc.split('.')[0].lower() 70 | 71 | # time to set variables 72 | baseurl = urlunparse((stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) 73 | amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) 74 | stream_page_url = urljoin(baseurl, stream_page_path) 75 | 76 | headers = { 77 | 'User-Agent': useragents.CHROME, 78 | 'Referer': stream_page_url, 79 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 80 | 'X-Requested-With': 'XMLHttpRequest' 81 | } 82 | 83 | data = 'method=getRoomData&args%5B%5D={0}&args%5B%5D=false'.format(stream_page_path) 84 | self.logger.debug('DATA: {0}'.format(str(data))) 85 | # send request and close http-session 86 | r = http_session.post(url=amf_gateway_url, 87 | headers=headers, 88 | params={CONST_AMF_GATEWAY_PARAM: country_code}, 89 | data=data) 90 | http_session.close() 91 | 92 | if r.status_code != 200: 93 | raise PluginError('unexpected status code for {0}: {1}', r.url, r.status_code) 94 | 95 | stream_source_info = amf_msg_schema.validate(json.loads(r.text)) 96 | self.logger.debug('source stream info:\n{0}', stream_source_info) 97 | 98 | if not stream_source_info: 99 | return 100 | 101 | performer = stream_source_info['performerData']['username'] 102 | real_name = stream_source_info['performerData']['displayName'] 103 | performer_id = stream_source_info['performerData']['userId'] 104 | print (colored('\n => Performer => {} <=', 'yellow', 'on_blue')).format(real_name) 105 | print (colored('\n => Performer ID => {} <=', 'yellow', 'on_blue')).format(performer_id) 106 | urlnoproto = stream_source_info['localData']['videoServerUrl'] 107 | urlnoproto = update_scheme('https://', urlnoproto) 108 | hls_url = '{0}/hls/stream_{1}/playlist.m3u8'.format(urlnoproto, performer) 109 | server = re.sub('https://', '', urlnoproto) 110 | print (colored('\n => Server => {} <=', 'yellow', 'on_blue')).format(server) 111 | 112 | if hls_url: 113 | try: 114 | for s in HLSStream.parse_variant_playlist(self.session, hls_url, headers=headers).items(): 115 | timestamp = str(time.strftime('%d%m%Y-%H%M%S')) 116 | path = config.get('folders', 'output_folder_BC') 117 | fn = real_name + '_BC_' + timestamp + '.mp4' 118 | pf = (path + fn) 119 | livestreamer = config.get('files', 'livestreamer') 120 | print (colored('\n => LS-24/7-REC => {} <=','yellow','on_red')).format(fn) 121 | print 122 | command = ('{} hlsvariant://"{}" best -Q -o "{}"'.format(livestreamer,hls_url,pf)) 123 | os.system(command) 124 | print(colored(' => END <= ', 'yellow','on_blue')) 125 | sys.exit() 126 | 127 | except Exception as e: 128 | if '404' in str(e): 129 | print(colored('\n => Performer is AWAY or PRIVATE <=','yellow','on_red')) 130 | print(colored('\n => END <= ', 'yellow','on_blue')) 131 | time.sleep(6) 132 | sys.exit() 133 | 134 | __plugin__ = bongacams 135 | -------------------------------------------------------------------------------- /bongacams_ffmpeg.py: -------------------------------------------------------------------------------- 1 | # Bongacams Streamlink FFMPEG Remote 24/7 Plugin v.1.0.4 by @horacio9a for Python 2.7.16 - Credits also to @sdfwv 2 | # coding: utf-8 3 | 4 | import sys, os, json, re, time, datetime, random, command 5 | from streamlink.compat import urljoin, urlparse, urlunparse 6 | from streamlink.exceptions import PluginError, NoStreamsError 7 | from streamlink.plugin.api import validate, http, useragents 8 | from streamlink.plugin import Plugin 9 | from streamlink.stream import HLSStream 10 | from streamlink.utils import update_scheme 11 | from colorama import init, Fore, Back, Style 12 | from termcolor import colored 13 | import ConfigParser 14 | config = ConfigParser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | init() 18 | 19 | CONST_AMF_GATEWAY_LOCATION = '/tools/amf.php' 20 | CONST_AMF_GATEWAY_PARAM = 'x-country' 21 | CONST_DEFAULT_COUNTRY_CODE = 'en' 22 | 23 | CONST_HEADERS = {} 24 | CONST_HEADERS['User-Agent'] = useragents.CHROME 25 | 26 | url_re = re.compile(r'(http(s)?://)?(\w{2}.)?(bongacams\.com)/([\w\d_-]+)') 27 | 28 | amf_msg_schema = validate.Schema( 29 | {'status': 'success','userData': 30 | {'username': validate.text},'localData': 31 | {'videoServerUrl': validate.text},'performerData': 32 | {'username': validate.text,'displayName': validate.text,'userId': validate.get}}) 33 | 34 | class bongacams(Plugin): 35 | @classmethod 36 | def can_handle_url(self, url): 37 | return url_re.match(url) 38 | 39 | def _get_streams(self): 40 | match = url_re.match(self.url) 41 | 42 | stream_page_scheme = 'https' 43 | stream_page_domain = match.group(4) 44 | stream_page_path = match.group(5) 45 | country_code = CONST_DEFAULT_COUNTRY_CODE 46 | 47 | # create http session and set headers 48 | http_session = http 49 | http_session.headers.update(CONST_HEADERS) 50 | 51 | # get cookies 52 | r = http_session.get(urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) 53 | 54 | # redirect to profile page means stream is offline 55 | if '/profile/' in r.url: 56 | print(colored('\n => Performer is OFFLINE <=','yellow','on_red')) 57 | print(colored('\n => END <= ', 'yellow','on_blue')) 58 | time.sleep(6) 59 | sys.exit() 60 | 61 | if not r.ok: 62 | self.logger.debug('Status code for {0}: {1}', r.url, r.status_code) 63 | raise NoStreamsError(self.url) 64 | if len(http_session.cookies) == 0: 65 | raise PluginError("Can't get a cookies") 66 | 67 | if urlparse(r.url).netloc != stream_page_domain: 68 | # then redirected to regional subdomain 69 | country_code = urlparse(r.url).netloc.split('.')[0].lower() 70 | 71 | # time to set variables 72 | baseurl = urlunparse((stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) 73 | amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) 74 | stream_page_url = urljoin(baseurl, stream_page_path) 75 | 76 | headers = { 77 | 'User-Agent': useragents.CHROME, 78 | 'Referer': stream_page_url, 79 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 80 | 'X-Requested-With': 'XMLHttpRequest' 81 | } 82 | 83 | data = 'method=getRoomData&args%5B%5D={0}&args%5B%5D=false'.format(stream_page_path) 84 | self.logger.debug('DATA: {0}'.format(str(data))) 85 | # send request and close http-session 86 | r = http_session.post(url=amf_gateway_url, 87 | headers=headers, 88 | params={CONST_AMF_GATEWAY_PARAM: country_code}, 89 | data=data) 90 | http_session.close() 91 | 92 | if r.status_code != 200: 93 | raise PluginError('unexpected status code for {0}: {1}', r.url, r.status_code) 94 | 95 | stream_source_info = amf_msg_schema.validate(json.loads(r.text)) 96 | self.logger.debug('source stream info:\n{0}', stream_source_info) 97 | 98 | if not stream_source_info: 99 | return 100 | 101 | performer = stream_source_info['performerData']['username'] 102 | real_name = stream_source_info['performerData']['displayName'] 103 | performer_id = stream_source_info['performerData']['userId'] 104 | print (colored('\n => Performer => {} <=', 'yellow', 'on_blue')).format(real_name) 105 | print (colored('\n => Performer ID => {} <=', 'yellow', 'on_blue')).format(performer_id) 106 | urlnoproto = stream_source_info['localData']['videoServerUrl'] 107 | urlnoproto = update_scheme('https://', urlnoproto) 108 | hls_url = '{0}/hls/stream_{1}/playlist.m3u8'.format(urlnoproto, performer) 109 | server = re.sub('https://', '', urlnoproto) 110 | print (colored('\n => Server => {} <=', 'yellow', 'on_blue')).format(server) 111 | 112 | if hls_url: 113 | try: 114 | for s in HLSStream.parse_variant_playlist(self.session, hls_url, headers=headers).items(): 115 | timestamp = str(time.strftime('%d%m%Y-%H%M%S')) 116 | path = config.get('folders', 'output_folder_BC') 117 | fn = real_name + '_BC_' + timestamp + '.flv' 118 | pf = (path + fn) 119 | ffmpeg = config.get('files', 'ffmpeg') 120 | print (colored('\n => FFMPEG-24/7-REC => {} <=','yellow','on_red')).format(fn) 121 | print 122 | command = ('{} -hide_banner -loglevel panic -i {} -c:v copy -c:a aac -b:a 160k {}'.format(ffmpeg,hls_url,pf)) 123 | os.system(command) 124 | print(colored(' => END <= ', 'yellow','on_blue')) 125 | sys.exit() 126 | 127 | except Exception as e: 128 | if '404' in str(e): 129 | print(colored('\n => Performer is AWAY or PRIVATE <=','yellow','on_red')) 130 | print(colored('\n => END <= ', 'yellow','on_blue')) 131 | time.sleep(6) 132 | sys.exit() 133 | 134 | __plugin__ = bongacams 135 | -------------------------------------------------------------------------------- /bongacams.py: -------------------------------------------------------------------------------- 1 | # Bongacams Streamlink FFPLAY/FFMPEG/LIVESTREAM/STREAMLINK/YOUTUBE-DL Plugin v.1.0.4 by @horacio9a for Python 2.7.16 - Credits also to @sdfwv 2 | # coding: utf-8 3 | 4 | import sys, os, json, re, time, datetime, random, command 5 | from streamlink.compat import urljoin, urlparse, urlunparse 6 | from streamlink.exceptions import PluginError, NoStreamsError 7 | from streamlink.plugin.api import validate, http, useragents 8 | from streamlink.plugin import Plugin 9 | from streamlink.stream import HLSStream 10 | from streamlink.utils import update_scheme 11 | from colorama import init, Fore, Back, Style 12 | from termcolor import colored 13 | import ConfigParser 14 | config = ConfigParser.ConfigParser() 15 | config.read('config.ini') 16 | 17 | init() 18 | 19 | CONST_AMF_GATEWAY_LOCATION = '/tools/amf.php' 20 | CONST_AMF_GATEWAY_PARAM = 'x-country' 21 | CONST_DEFAULT_COUNTRY_CODE = 'en' 22 | 23 | CONST_HEADERS = {} 24 | CONST_HEADERS['User-Agent'] = useragents.CHROME 25 | 26 | url_re = re.compile(r'(http(s)?://)?(\w{2}.)?(bongacams\.com)/([\w\d_-]+)') 27 | 28 | amf_msg_schema = validate.Schema( 29 | {'status': 'success','userData': 30 | {'username': validate.text},'localData': 31 | {'videoServerUrl': validate.text},'performerData': 32 | {'username': validate.text,'displayName': validate.text,'userId': validate.get}}) 33 | 34 | class bongacams(Plugin): 35 | @classmethod 36 | def can_handle_url(self, url): 37 | return url_re.match(url) 38 | 39 | def _get_streams(self): 40 | match = url_re.match(self.url) 41 | 42 | stream_page_scheme = 'https' 43 | stream_page_domain = match.group(4) 44 | stream_page_path = match.group(5) 45 | country_code = CONST_DEFAULT_COUNTRY_CODE 46 | 47 | # create http session and set headers 48 | http_session = http 49 | http_session.headers.update(CONST_HEADERS) 50 | 51 | # get cookies 52 | r = http_session.get(urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) 53 | 54 | # redirect to profile page means stream is offline 55 | if '/profile/' in r.url: 56 | print(colored('\n => Performer is OFFLINE <=','yellow','on_red')) 57 | print(colored('\n => END <= ', 'yellow','on_blue')) 58 | time.sleep(6) 59 | sys.exit() 60 | 61 | if not r.ok: 62 | self.logger.debug('Status code for {0}: {1}', r.url, r.status_code) 63 | raise NoStreamsError(self.url) 64 | if len(http_session.cookies) == 0: 65 | raise PluginError("Can't get a cookies") 66 | 67 | if urlparse(r.url).netloc != stream_page_domain: 68 | # then redirected to regional subdomain 69 | country_code = urlparse(r.url).netloc.split('.')[0].lower() 70 | 71 | # time to set variables 72 | baseurl = urlunparse((stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) 73 | amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) 74 | stream_page_url = urljoin(baseurl, stream_page_path) 75 | 76 | headers = { 77 | 'User-Agent': useragents.CHROME, 78 | 'Referer': stream_page_url, 79 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 80 | 'X-Requested-With': 'XMLHttpRequest' 81 | } 82 | 83 | data = 'method=getRoomData&args%5B%5D={0}&args%5B%5D=false'.format(stream_page_path) 84 | self.logger.debug('DATA: {0}'.format(str(data))) 85 | # send request and close http-session 86 | r = http_session.post(url=amf_gateway_url, 87 | headers=headers, 88 | params={CONST_AMF_GATEWAY_PARAM: country_code}, 89 | data=data) 90 | http_session.close() 91 | 92 | if r.status_code != 200: 93 | raise PluginError('unexpected status code for {0}: {1}', r.url, r.status_code) 94 | 95 | stream_source_info = amf_msg_schema.validate(json.loads(r.text)) 96 | self.logger.debug('source stream info:\n{0}', stream_source_info) 97 | 98 | if not stream_source_info: 99 | return 100 | 101 | performer = stream_source_info['performerData']['username'] 102 | real_name = stream_source_info['performerData']['displayName'] 103 | performer_id = stream_source_info['performerData']['userId'] 104 | print (colored('\n => Performer => {} <=', 'yellow', 'on_blue')).format(real_name) 105 | print (colored('\n => Performer ID => {} <=', 'yellow', 'on_blue')).format(performer_id) 106 | urlnoproto = stream_source_info['localData']['videoServerUrl'] 107 | urlnoproto = update_scheme('https://', urlnoproto) 108 | hls_url = '{0}/hls/stream_{1}/playlist.m3u8'.format(urlnoproto, performer) 109 | server = re.sub('https://', '', urlnoproto) 110 | print (colored('\n => Server => {} <=', 'yellow', 'on_blue')).format(server) 111 | 112 | if hls_url: 113 | try: 114 | for s in HLSStream.parse_variant_playlist(self.session, hls_url, headers=headers).items(): 115 | while True: 116 | try: 117 | print 118 | mode = int(raw_input(colored(' => Mode => EXIT(5) YTDL-TS(4) SL(3) LS(2) FFMPEG(1) FFPLAY(0) => ', 'yellow', 'on_blue'))) 119 | break 120 | except ValueError: 121 | print(colored('\n => Input must be a number <=', 'yellow', 'on_red')) 122 | if mode == 0: 123 | mod = 'FFPLAY' 124 | if mode == 1: 125 | mod = 'FFMPEG' 126 | if mode == 2: 127 | mod = 'LS' 128 | if mode == 3: 129 | mod = 'SL' 130 | if mode == 4: 131 | mod = 'YTDL-TS' 132 | if mode == 5: 133 | mod = 'EXIT' 134 | 135 | timestamp = str(time.strftime('%d%m%Y-%H%M%S')) 136 | stime = str(time.strftime('%H:%M:%S')) 137 | path = config.get('folders', 'output_folder_BC') 138 | fn = real_name + '_BC_' + timestamp 139 | fn1 = real_name + '_BC_' + timestamp + '.flv' 140 | fn2 = real_name + '_BC_' + timestamp + '.mp4' 141 | fn3 = real_name + '_BC_' + timestamp + '.ts' 142 | pf1 = (path + fn1) 143 | pf2 = (path + fn2) 144 | pf3 = (path + fn3) 145 | ffmpeg = config.get('files', 'ffmpeg') 146 | ffplay = config.get('files', 'ffplay') 147 | livestreamer = config.get('files', 'livestreamer') 148 | streamlink = config.get('files', 'streamlink') 149 | youtube = config.get('files', 'youtube') 150 | 151 | if mod == 'FFPLAY': 152 | print (colored('\n => HLS URL => {} <=', 'yellow', 'on_blue')).format(hls_url) 153 | print (colored('\n => FFPLAY => {} <=', 'yellow', 'on_magenta')).format(fn) 154 | print 155 | command = ('{} -hide_banner -loglevel panic -i {} -infbuf -autoexit -window_title "{} * {} * {}"'.format(ffplay,hls_url,real_name,stime,urlnoproto)) 156 | os.system(command) 157 | print(colored(' => END <= ', 'yellow','on_blue')) 158 | 159 | if mod == 'FFMPEG': 160 | print (colored('\n => HLS URL => {} <=', 'yellow', 'on_blue')).format(hls_url) 161 | print (colored('\n => FFMPEG-REC => {} <=','yellow','on_red')).format(fn1) 162 | print 163 | command = ('{} -hide_banner -loglevel panic -i {} -c:v copy -c:a aac -b:a 160k {}'.format(ffmpeg,hls_url,pf1)) 164 | os.system(command) 165 | print(colored(' => END <= ', 'yellow','on_blue')) 166 | 167 | if mod == 'LS': 168 | print (colored('\n => LS-REC >>> {} <<<', 'yellow', 'on_red')).format(fn2) 169 | print 170 | command = ('{} hlsvariant://"{}" best -Q -o "{}"'.format(livestreamer,hls_url,pf2)) 171 | os.system(command) 172 | print(colored(' => END <= ', 'yellow','on_blue')) 173 | 174 | if mod == 'SL': 175 | print (colored('\n => SL-REC >>> {} <<<', 'yellow', 'on_red')).format(fn2) 176 | print 177 | command = ('{} hls://"{}" best -Q -o "{}"'.format(streamlink,hls_url,pf2)) 178 | os.system(command) 179 | print(colored(' => END <= ', 'yellow','on_blue')) 180 | 181 | if mod == 'YTDL-TS': 182 | print (colored('\n => HLS URL => {} <=', 'yellow', 'on_blue')).format(hls_url) 183 | print (colored('\n => YTDL-TS-REC => {} <=', 'yellow', 'on_red')).format(fn3) 184 | command = ('{} --hls-use-mpegts --no-part {} -q -o {}'.format(youtube,hls_url,pf3)) 185 | os.system(command) 186 | print(colored('\n => END <= ', 'yellow','on_blue')) 187 | sys.exit() 188 | 189 | if mod == 'EXIT': 190 | print(colored('\n => END <= ', 'yellow','on_blue')) 191 | time.sleep(3) 192 | sys.exit() 193 | 194 | except Exception as e: 195 | if '404' in str(e): 196 | print(colored('\n => Performer is AWAY or PRIVATE <=','yellow','on_red')) 197 | print(colored('\n => END <= ', 'yellow','on_blue')) 198 | time.sleep(6) 199 | sys.exit() 200 | 201 | __plugin__ = bongacams 202 | --------------------------------------------------------------------------------