Skip to content
This repository was archived by the owner on Nov 27, 2018. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
dd2f94d
Fixed torrenttelik playlist
AndreyPavlenko Feb 24, 2016
fc53911
Fixed HEAD requests handling.
AndreyPavlenko Feb 27, 2016
754d12c
Switched to TorrentTV API v3
AndreyPavlenko Feb 29, 2016
aa48833
Implemented streaming of the same channel to multiple clients without
AndreyPavlenko Mar 9, 2016
95a0338
Configurable playlist format
AndreyPavlenko Mar 11, 2016
d7119f7
Fixes and enhancements
AndreyPavlenko Mar 11, 2016
bd25c7e
Destroy ace if failed to stop
AndreyPavlenko Mar 12, 2016
b7947aa
Use WEB API if failed to get CID from engine
AndreyPavlenko Mar 12, 2016
c75055b
Added new handler - logos which is used to generate logomap for the
AndreyPavlenko Mar 12, 2016
1fa4500
Do not authenticate for each request - reuse the previous session.
AndreyPavlenko Mar 12, 2016
7b1ab4b
Fixed typo.
AndreyPavlenko Mar 13, 2016
0f2ed04
Use START PID if we know the content id
AndreyPavlenko Mar 13, 2016
3a8cead
Redesigned ttv api
AndreyPavlenko Mar 13, 2016
9f9d77c
Fixed torrents playback.
AndreyPavlenko Mar 14, 2016
dd66362
Redesigned the torrenttv plugin
AndreyPavlenko Mar 14, 2016
ee5134d
Prefer WEB API since it's faster and more stable
AndreyPavlenko Mar 14, 2016
6691c0c
Fixes
AndreyPavlenko Mar 14, 2016
4e313a9
Fixed VLC broadcasting
AndreyPavlenko Mar 15, 2016
94b9c71
Use gevent.spawn instead of Timer
AndreyPavlenko Mar 16, 2016
6084802
Encode vlc url
AndreyPavlenko Mar 16, 2016
dd4b767
Implemented ETag support.
AndreyPavlenko Mar 16, 2016
1594692
Stat plugin enhancements
AndreyPavlenko Mar 16, 2016
6fa734b
Fixed archive
AndreyPavlenko Mar 16, 2016
5342c09
#EXTGRP removed from the default template since it's not supported by
AndreyPavlenko Mar 17, 2016
6cb2582
Use channel name instead of sequence number.
AndreyPavlenko Mar 17, 2016
a3071d0
Added charset to the Content-type header
AndreyPavlenko Mar 17, 2016
bf51dfc
Implemented archive m3u.
AndreyPavlenko Mar 17, 2016
2c93fee
Updated logos
AndreyPavlenko Mar 18, 2016
85f6d29
Added .mp4 suffix to urls.
AndreyPavlenko Mar 18, 2016
c7b945c
Set the Content-Length header.
AndreyPavlenko Mar 18, 2016
c22da11
Update logos if the p2p plugin is configured
AndreyPavlenko Mar 18, 2016
f3f7e2c
Fixed videodestroydelay
AndreyPavlenko Mar 19, 2016
83d166d
Set the Content-Type header before writing data
AndreyPavlenko Mar 19, 2016
d2f6637
Check the ace engine and vlc processes before handling a request
AndreyPavlenko Mar 20, 2016
97a908b
Removed header duplicates
AndreyPavlenko Mar 20, 2016
63533ec
Fixes
AndreyPavlenko Mar 21, 2016
44f0afe
Added group names for the torrent-telik plugin
AndreyPavlenko Mar 22, 2016
06f4a00
Implemented transcoding
AndreyPavlenko Mar 22, 2016
853238e
Fixes
AndreyPavlenko Apr 4, 2016
dc947c0
Implemented new handler for archives - /archive/playlist
AndreyPavlenko Apr 4, 2016
71e91f1
Handle /channels.m3u requests in the same way as /channels/?type=m3u
AndreyPavlenko Apr 4, 2016
cd67576
Added support for playlist url suffix.
AndreyPavlenko Apr 5, 2016
b754d16
New playlist - /archive/dates.m3u
AndreyPavlenko Apr 6, 2016
e378999
Add new API features support for aceengine >=3.1.5
pepsik-kiev Apr 12, 2016
2086a61
Add support playlist.py
pepsik-kiev Apr 12, 2016
1f73bc2
add EPG & TIMESHIFT
pepsik-kiev Apr 12, 2016
d4041af
Added missing return
AndreyPavlenko Apr 12, 2016
54d9543
Added handler for favicon.ico
AndreyPavlenko Apr 12, 2016
d04c98d
Print VLC messages to log if DEBUG is enabled.
AndreyPavlenko Apr 12, 2016
3465cdc
Do not ignore acestream:// urls.
AndreyPavlenko Apr 23, 2016
8769e63
Changed host to allfon.org.
AndreyPavlenko Apr 23, 2016
7725ff2
Spawn the hang detector just before the proxy
AndreyPavlenko Apr 24, 2016
cb8c8f2
Show channel logo and name in /stat.
AndreyPavlenko May 4, 2016
c68d9b9
Fixed zombie connections caused by GreenletExit exception.
AndreyPavlenko May 19, 2016
894f821
Minor enhancements
AndreyPavlenko May 20, 2016
a37f88f
Fixed unicode channels renaming.
AndreyPavlenko May 21, 2016
31b6b2e
Fixed transcoding
AndreyPavlenko Jun 13, 2016
3f1e5f1
Implemented playlist sorting.
AndreyPavlenko Jun 13, 2016
9da639c
Added support for zone id.
AndreyPavlenko Jun 14, 2016
261b3a3
Added channel name to tvg name mappings
AndreyPavlenko Jun 19, 2016
5915626
Enhanced the channels group filter
AndreyPavlenko Aug 19, 2016
c47af7f
Fixed support for the fmt parameter
AndreyPavlenko Aug 19, 2016
e686825
Updated logos
AndreyPavlenko Aug 19, 2016
26e79d1
Added connection time to /stat
AndreyPavlenko Aug 21, 2016
a7ea229
Correct exit from proxy if vlcuse=True
pepsik-kiev Dec 21, 2016
29ad70d
Correct support for zone id
pepsik-kiev Dec 21, 2016
aac5f37
Add support for zone id
pepsik-kiev Dec 21, 2016
eda84b5
Correct support for zone id
pepsik-kiev Dec 21, 2016
5ed0c08
Update picon's logobase
pepsik-kiev Dec 21, 2016
a44f68b
Applied changes for Allfon.tv playlist
pepsik-kiev Dec 21, 2016
de677b8
Merge pull request #3 from pepsik-kiev/master
AndreyPavlenko Dec 21, 2016
eeaa02a
Add options for audio transcoding for HLS
pepsik-kiev Dec 24, 2016
1c98d28
Add options for audio transcoding for HLS
pepsik-kiev Dec 24, 2016
48ec9ea
Merge pull request #4 from pepsik-kiev/master
AndreyPavlenko Dec 25, 2016
291d258
Allfon TV changes
pepsik-kiev Dec 28, 2016
a84be1f
Fixed VLC broadcasting
pepsik-kiev Dec 30, 2016
be70c1b
Add gzip compression for playlist url
pepsik-kiev Jan 15, 2017
26fe0cc
Merge pull request #5 from pepsik-kiev/master
AndreyPavlenko Jan 15, 2017
569de63
universalization EOL (EndOFLine) for playlist
pepsik-kiev Jan 15, 2017
e066964
Merge pull request #7 from pepsik-kiev/master
AndreyPavlenko Jan 17, 2017
c90d5d3
Add subclass for override the log messages
pepsik-kiev Jan 22, 2017
902226e
Small changes for loggin
pepsik-kiev Jan 22, 2017
1db769a
Merge pull request #9 from pepsik-kiev/master
AndreyPavlenko Jan 23, 2017
4f71a18
Get client IP from Header
pepsik-kiev Jan 23, 2017
3d62051
Merge pull request #10 from pepsik-kiev/master
AndreyPavlenko Jan 23, 2017
61ff443
Add locale support for stat info
pepsik-kiev Jan 24, 2017
14006d7
Add duration time & GeoIP info for connections
pepsik-kiev Feb 2, 2017
c519b49
Merge pull request #11 from pepsik-kiev/master
AndreyPavlenko Feb 2, 2017
73b886f
Minor changes for stat_plugin stability
pepsik-kiev Feb 6, 2017
0f4ee9e
Merge pull request #13 from pepsik-kiev/master
AndreyPavlenko Feb 6, 2017
01d5b27
Change urllib to urllib2 for geoip
pepsik-kiev Feb 8, 2017
c939589
Merge pull request #15 from pepsik-kiev/master
AndreyPavlenko Feb 8, 2017
ee28f14
Update Picon's logobase
pepsik-kiev Feb 11, 2017
46c7b7f
Add locale & jsoncheck for geoip response
pepsik-kiev Feb 20, 2017
ee3f727
Merge pull request #16 from pepsik-kiev/master
AndreyPavlenko Feb 20, 2017
7f6570f
Add table header & change GeoIP API for stability
pepsik-kiev Feb 21, 2017
12a1d10
Merge pull request #17 from pepsik-kiev/master
AndreyPavlenko Feb 22, 2017
913668e
Remove locale & add world-flags-sprite css
pepsik-kiev Feb 23, 2017
841fbe8
Merge pull request #18 from pepsik-kiev/master
AndreyPavlenko Feb 23, 2017
c5641d4
Update torrenttv.py
zixelmike Mar 16, 2017
48f12de
Merge pull request #19 from zixelmike/patch-3
AndreyPavlenko Mar 16, 2017
37179ef
Update torrenttv.py
zixelmike Mar 16, 2017
38dffaa
Update torrenttv.py
zixelmike Mar 19, 2017
cf654dc
Merge pull request #20 from zixelmike/patch-2
AndreyPavlenko Mar 19, 2017
1fea6cb
Update torrenttv.py
zixelmike Mar 19, 2017
10d0051
Merge pull request #21 from zixelmike/patch-1
AndreyPavlenko Mar 19, 2017
1599f61
Temporary disabled CID generation WEB API
pepsik-kiev Jun 7, 2017
f1484f6
Merge pull request #24 from pepsik-kiev/master
AndreyPavlenko Jun 7, 2017
f9c6419
Returned changes after acestream CDN was repiared
pepsik-kiev Jun 9, 2017
58e25bc
Merge pull request #25 from pepsik-kiev/master
AndreyPavlenko Jun 9, 2017
08fbcbe
Add HTTP/SOCKS PROXY for AllFon,TorrentTelik & TorrentTV plugins (#27)
pepsik-kiev Sep 28, 2017
2561431
Changes & additions (#29)
pepsik-kiev Oct 12, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.pyc
.project
.pydevproject
.settings/
.idea/
168 changes: 143 additions & 25 deletions aceclient/aceclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
import telnetlib
import logging
import json
import time
import threading
import traceback
import Queue
from collections import deque
from acemessages import *

import aceconfig
from aceconfig import AceConfig

class AceException(Exception):

Expand Down Expand Up @@ -49,15 +55,24 @@ def __init__(self, host, port, connect_timeout=5, result_timeout=10):
self._authevent = Event()
# Result for getURL()
self._urlresult = AsyncResult()
# Result for GETCID()
self._cidresult = AsyncResult()
# Event for resuming from PAUSE
self._resumeevent = Event()
# Seekback seconds.
self._seekback = 0
# Did we get START command again? For seekback.
self._started_again = False

self._idleSince = time.time()
self._lock = threading.Condition(threading.Lock())
self._streamReaderConnection = None
self._streamReaderState = None
self._streamReaderQueue = deque()
self._engine_version_code = 0;

# Logger
logger = logging.getLogger('AceClient_init')
logger = logging.getLogger('AceClieimport tracebacknt_init')

try:
self._socket = telnetlib.Telnet(host, port, connect_timeout)
Expand Down Expand Up @@ -100,8 +115,15 @@ def destroy(self):
finally:
self._shuttingDown.set()

def reset(self):
self._started_again = False
self._idleSince = time.time()
self._streamReaderState = None

def _write(self, message):
try:
logger = logging.getLogger("AceClient_write")
logger.debug(message)
self._socket.write(message + "\r\n")
except EOFError as e:
raise AceException("Write error! " + repr(e))
Expand Down Expand Up @@ -136,35 +158,50 @@ def aceInit(self, gender=AceConst.SEX_MALE, age=AceConst.AGE_18_24, product_key=

def _getResult(self):
# Logger
logger = logging.getLogger("AceClient_START")

try:
result = self._result.get(timeout=self._resulttimeout)
if not result:
errmsg = "START error!"
logger.error(errmsg)
raise AceException(errmsg)
raise AceException("Result not received")
except gevent.Timeout:
errmsg = "START timeout!"
logger.error(errmsg)
raise AceException(errmsg)
raise AceException("Timeout")

return result

def START(self, datatype, value):
'''
Start video method
'''
self._result = AsyncResult()
if self._engine_version_code >= 3010500 and AceConfig.vlcuse:
stream_type = 'output_format=hls' + ' transcode_audio=' + str(AceConfig.transcode_audio) \
+ ' transcode_mp3=' + str(AceConfig.transcode_mp3) \
+ ' transcode_ac3=' + str(AceConfig.transcode_ac3)
else:
stream_type = 'output_format=http'

self._urlresult = AsyncResult()
self._write(AceMessage.request.START(datatype.upper(), value, stream_type))
self._getResult()

self._write(AceMessage.request.LOADASYNC(datatype.upper(), 0, value))
contentinfo = self._getResult()
def STOP(self):
'''
Stop video method
'''
if self._state is not None and self._state != '0':
self._result = AsyncResult()
self._write(AceMessage.request.STOP)
self._getResult()

self._write(AceMessage.request.START(datatype.upper(), value))
self._getResult()
def LOADASYNC(self, datatype, url):
self._result = AsyncResult()
self._write(AceMessage.request.LOADASYNC(datatype.upper(), 0, {'url': url}))
return self._getResult()

return contentinfo
def GETCID(self, datatype, url):
contentinfo = self.LOADASYNC(datatype, url)
self._cidresult = AsyncResult()
self._write(AceMessage.request.GETCID(contentinfo.get('checksum'), contentinfo.get('infohash'), 0, 0, 0))
cid = self._cidresult.get(True, 5)
return '' if not cid or cid == '' else cid[2:]

def getUrl(self, timeout=40):
# Logger
Expand All @@ -178,6 +215,79 @@ def getUrl(self, timeout=40):
logger.error(errmsg)
raise AceException(errmsg)

def startStreamReader(self, url, cid, counter):
logger = logging.getLogger("StreamReader")
self._streamReaderState = 1
logger.debug("Opening video stream: %s" % url)

try:
connection = self._streamReaderConnection = urllib2.urlopen(url)

if url.endswith('.m3u8'):
logger.debug("Can't stream HLS in non VLC mode: %s" % url)
return

if connection.getcode() != 200:
logger.error("Failed to open video stream %s" % connection)
return

with self._lock:
self._streamReaderState = 2
self._lock.notifyAll()

while True:
data = None
clients = counter.getClients(cid)

try:
data = connection.read(AceConfig.readchunksize)
except:
break;

if data and clients:
with self._lock:
if len(self._streamReaderQueue) == AceConfig.readcachesize:
self._streamReaderQueue.popleft()
self._streamReaderQueue.append(data)

for c in clients:
try:
c.addChunk(data, 5.0)
except Queue.Full:
if len(clients) > 1:
logger.debug("Disconnecting client: %s" % str(c))
c.destroy()
elif not clients:
logger.debug("All clients disconnected - closing video stream")
break
else:
logger.warning("No data received")
break
except urllib2.URLError:
logger.error("Failed to open video stream")
logger.error(traceback.format_exc())
except:
logger.error(traceback.format_exc())
if counter.getClients(cid):
logger.error("Failed to read video stream")
finally:
self.closeStreamReader()
with self._lock:
self._streamReaderState = 3
self._lock.notifyAll()
counter.deleteAll(cid)

def closeStreamReader(self):
logger = logging.getLogger("StreamReader")
c = self._streamReaderConnection

if c:
self._streamReaderConnection = None
c.close()
logger.debug("Video stream closed")

self._streamReaderQueue.clear()

def getPlayEvent(self, timeout=None):
'''
Blocking while in PAUSE, non-blocking while in RESUME
Expand All @@ -201,7 +311,7 @@ def _recvData(self):
try:
self._recvbuffer = self._socket.read_until("\r\n")
self._recvbuffer = self._recvbuffer.strip()
#logger.debug('<<< ' + self._recvbuffer)
# logger.debug('<<< ' + self._recvbuffer)
except:
# If something happened during read, abandon reader.
if not self._shuttingDown.isSet():
Expand All @@ -213,10 +323,14 @@ def _recvData(self):
# Parsing everything only if the string is not empty
if self._recvbuffer.startswith(AceMessage.response.HELLO):
# Parse HELLO
if 'version_code=' in self._recvbuffer:
v = self._recvbuffer.find('version_code=')
self._engine_version_code = int(self._recvbuffer[v+13:v+20])

if 'key=' in self._recvbuffer:
self._request_key_begin = self._recvbuffer.find('key=')
self._request_key = \
self._recvbuffer[self._request_key_begin+4:self._request_key_begin+14]
self._recvbuffer[self._request_key_begin + 4:self._request_key_begin + 14]
try:
self._write(AceMessage.request.READY_key(
self._request_key, self._product_key))
Expand Down Expand Up @@ -246,12 +360,11 @@ def _recvData(self):
self._result.set(False)
else:
logger.debug("Content info: %s", _contentinfo)
_filename = urllib2.unquote(_contentinfo.get('files')[0][0])
self._result.set(_filename)
self._result.set(_contentinfo)

elif self._recvbuffer.startswith(AceMessage.response.START):
# START
if not self._seekback or (self._seekback and self._started_again):
if not self._seekback or self._started_again or not self._recvbuffer.endswith(' stream=1'):
# If seekback is disabled, we use link in first START command.
# If seekback is enabled, we wait for first START command and
# ignore it, then do seeback in first EVENT position command
Expand All @@ -263,6 +376,8 @@ def _recvData(self):
self._resumeevent.set()
except IndexError as e:
self._url = None
else:
logger.debug("START received. Waiting for %s." % AceMessage.response.LIVEPOS)

elif self._recvbuffer.startswith(AceMessage.response.STOP):
pass
Expand Down Expand Up @@ -290,10 +405,7 @@ def _recvData(self):
self._position_last = self._position[2].split('=')[1]
self._position_buf = self._position[9].split('=')[1]
self._position = self._position[4].split('=')[1]
logger.debug('Current position/last/buf: %s/%s/%s' % (self._position,
self._position_last,
self._position_buf)
)

if self._seekback and not self._started_again:
self._write(AceMessage.request.SEEK(str(int(self._position_last) - \
self._seekback)))
Expand All @@ -318,6 +430,8 @@ def _recvData(self):
AceException(self._status + ' with message ' + self._recvbuffer.split(';')[2]))
elif self._status == 'main:starting':
self._result.set(True)
elif self._status == 'main:idle':
self._result.set(True)

elif self._recvbuffer.startswith(AceMessage.response.PAUSE):
logger.debug("PAUSE event")
Expand All @@ -327,3 +441,7 @@ def _recvData(self):
logger.debug("RESUME event")
gevent.sleep(self._pausedelay)
self._resumeevent.set()

elif self._recvbuffer.startswith('##') or len(self._recvbuffer) == 0:
self._cidresult.set(self._recvbuffer)
logger.debug("CID: %s" %self._recvbuffer)
10 changes: 4 additions & 6 deletions aceclient/acemessages.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
import hashlib
import platform
import urllib2



class AceConst(object):
APIVERSION = 3

Expand Down Expand Up @@ -78,7 +77,7 @@ def LOADASYNC(command, request_id, params_dict):
# End LOADASYNC

@staticmethod
def START(command, params_dict):
def START(command, params_dict, stream_type):
if command == 'TORRENT':
return 'START TORRENT ' + str(params_dict.get('url')) + ' ' + \
str(params_dict.get('file_indexes', '0')) + ' ' + \
Expand All @@ -92,12 +91,11 @@ def START(command, params_dict):
str(params_dict.get('file_indexes', '0')) + ' ' + \
str(params_dict.get('developer_id', '0')) + ' ' + \
str(params_dict.get('affiliate_id', '0')) + ' ' + \
str(params_dict.get('zone_id', '0')) + ' ' + \
str(params_dict.get('stream_id', '0'))
str(params_dict.get('zone_id', '0'))

elif command == 'PID':
return 'START PID ' + str(params_dict.get('content_id')) + ' ' + \
str(params_dict.get('file_indexes', '0'))
str(params_dict.get('file_indexes', '0')) + ' ' + stream_type

elif command == 'RAW':
return 'START RAW ' + str(params_dict.get('data')) + ' ' + \
Expand Down
Loading