├── .gitattributes
├── .gitignore
├── LICENSE.txt
├── README.md
├── addon.xml
├── changelog.txt
├── helpers.py
├── icon.png
├── lib
├── __init__.py
└── comm.py
├── pyhtml.py
├── resources
├── __init__.py
├── language
│ └── English
│ │ └── strings.po
└── settings.xml
├── service.py
├── socks.py
└── www
├── css
└── styles.css
└── index.html
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows:
2 | Thumbs.db
3 | ehthumbs.db
4 | Desktop.ini
5 |
6 | # Python:
7 | *.py[cod]
8 | *.so
9 | *.egg
10 | *.egg-info
11 | dist
12 | build
13 | /.vs
14 | service - 副本.py
15 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014, Ryan Rogers
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 115proxy-for-kodi
2 | 115原码播放服务Kodi插件,需要kodi 18以上版本,需配合 https://github.com/feelfar/115-for-kodi 使用
3 | # 安装
4 | 由于release包尚未释出,可直接下载源代码zip包安装。
5 | https://github.com/feelfar/115proxy-for-kodi/archive/master.zip
6 | # 感谢
7 | 下载链接生成参考了https://github.com/kkHAIKE/fake115 谢谢kkHAIKE大神
8 |
--------------------------------------------------------------------------------
/addon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | File115Proxy
9 | File115Proxy
10 | en
11 | all
12 |
13 |
14 |
--------------------------------------------------------------------------------
/changelog.txt:
--------------------------------------------------------------------------------
1 | v0.0.0.1
--------------------------------------------------------------------------------
/helpers.py:
--------------------------------------------------------------------------------
1 | #VERSION: 1.40
2 |
3 | # Author:
4 | # Christophe DUMEZ (chris@qbittorrent.org)
5 |
6 | # Redistribution and use in source and binary forms, with or without
7 | # modification, are permitted provided that the following conditions are met:
8 | #
9 | # * Redistributions of source code must retain the above copyright notice,
10 | # this list of conditions and the following disclaimer.
11 | # * Redistributions in binary form must reproduce the above copyright
12 | # notice, this list of conditions and the following disclaimer in the
13 | # documentation and/or other materials provided with the distribution.
14 | # * Neither the name of the author nor the names of its contributors may be
15 | # used to endorse or promote products derived from this software without
16 | # specific prior written permission.
17 | #
18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 | # POSSIBILITY OF SUCH DAMAGE.
29 | # -*- coding: utf-8 -*-
30 | import re, htmlentitydefs
31 | import tempfile
32 | import os
33 | import StringIO, gzip, urllib,urllib2
34 | import socket
35 | import socks
36 | import re
37 | import xbmc
38 |
39 | class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
40 | def http_error_301(self, req, fp, code, msg, headers):
41 | result = urllib2.HTTPRedirectHandler.http_error_301(
42 | self, req, fp, code, msg, headers)
43 | result.status = code
44 | return result
45 |
46 | def http_error_302(self, req, fp, code, msg, headers):
47 | result = urllib2.HTTPRedirectHandler.http_error_302(
48 | self, req, fp, code, msg, headers)
49 | result.status = code
50 | return result
51 |
52 | class PassRedirectHandler(urllib2.HTTPRedirectHandler):
53 | def http_error_301(self, req, fp, code, msg, headers):
54 | infourl = urllib.addinfourl(fp, headers, req.get_full_url())
55 | infourl.status = code
56 | infourl.code = code
57 | return infourl
58 |
59 | def http_error_302(self, req, fp, code, msg, headers):
60 | infourl = urllib.addinfourl(fp, headers, req.get_full_url())
61 | infourl.status = code
62 | infourl.code = code
63 | return infourl
64 |
65 |
66 | # Some sites blocks default python User-agent
67 | user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'
68 | #user_agent = 'User-Agent: Mozilla/5.0 (Linux; U; Android 6.0.1;)'
69 | headers = {'User-Agent': user_agent,'Accept-encoding': 'gzip,deflate','Accept-Language':'zh-cn','X-Requested-With': 'XMLHttpRequest'}
70 | # SOCKS5 Proxy support
71 | if os.environ.has_key("sock_proxy") and len(os.environ["sock_proxy"].strip()) > 0:
72 | proxy_str = os.environ["sock_proxy"].strip()
73 | m=re.match(r"^(?:(?P[^:]+):(?P[^@]+)@)?(?P[^:]+):(?P\w+)$", proxy_str)
74 | if m is not None:
75 | socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, m.group('host'), int(m.group('port')), True, m.group('username'), m.group('password'))
76 | socket.socket = socks.socksocket
77 |
78 | def htmlentitydecode(s):
79 | # First convert alpha entities (such as é)
80 | # (Inspired from http://mail.python.org/pipermail/python-list/2007-June/443813.html)
81 | def entity2char(m):
82 | entity = m.group(1)
83 | if entity in htmlentitydefs.name2codepoint:
84 | return unichr(htmlentitydefs.name2codepoint[entity])
85 | return u" " # Unknown entity: We replace with a space.
86 | t = re.sub(u'&(%s);' % u'|'.join(htmlentitydefs.name2codepoint), entity2char, s)
87 |
88 | # Then convert numerical entities (such as é)
89 | t = re.sub(u'(\d+);', lambda x: unichr(int(x.group(1))), t)
90 |
91 | # Then convert hexa entities (such as é)
92 | return re.sub(u'(\w+);', lambda x: unichr(int(x.group(1),16)), t)
93 |
94 | def gethead(url, data=None,referer=None,h=None):
95 | if h:
96 | headers.update(h)
97 | req = urllib2.Request(url, headers = headers)
98 | if referer:
99 | req.add_header('Referer', referer)
100 | req.get_method = lambda : 'HEAD'
101 | opener = urllib2.build_opener(PassRedirectHandler)
102 | return opener.open(req,timeout=30)
103 |
104 | def retrieve_url(url, data=None,referer=None,h=None,redirect=True,charset='auto',savecookie=False):
105 | """ Return the content of the url page as a string """
106 | if h:
107 | headers.update(h)
108 | req = urllib2.Request(url, headers = headers)
109 | if referer:
110 | req.add_header('Referer', referer)
111 | if redirect:
112 | opener = urllib2.build_opener(SmartRedirectHandler)
113 | else:
114 | opener = urllib2.build_opener(PassRedirectHandler)
115 | try:
116 | if data:
117 | response = opener.open(req, data=data,timeout=30)
118 | else:
119 | response = opener.open(req,timeout=30)
120 | if response.code==302:
121 | return response.info()['Location']
122 | except urllib2.URLError as errno:
123 | print(" ".join(("Connection error:", str(errno.reason))))
124 | return ""
125 | dat = response.read()
126 |
127 | # Check if it is gzipped
128 | if dat[:2] == '\037\213':
129 | # Data is gzip encoded, decode it
130 | compressedstream = StringIO.StringIO(dat)
131 | gzipper = gzip.GzipFile(fileobj=compressedstream)
132 | extracted_data = gzipper.read()
133 | dat = extracted_data
134 | info = response.info()
135 | if savecookie:
136 | cookie=info.getheader('Set-Cookie')
137 | if cookie:
138 | dat='feelfarcookie:'+cookie+dat
139 | if charset=='auto':
140 | charset = 'utf-8'
141 | try:
142 | ignore, charset = info['Content-Type'].split('charset=')
143 | except:
144 | pass
145 | dat = dat.decode(charset, 'replace')
146 | dat = htmlentitydecode(dat)
147 | return dat.encode('utf-8', 'replace')
148 |
149 | def download_file(url, referer=None):
150 | """ Download file at url and write it to a file, return the path to the file and the url """
151 | file, path = tempfile.mkstemp()
152 | file = os.fdopen(file, "w")
153 | # Download url
154 | req = urllib2.Request(url, headers = headers)
155 | if referer is not None:
156 | req.add_header('referer', referer)
157 | response = urllib2.urlopen(req)
158 | dat = response.read()
159 | # Check if it is gzipped
160 | if dat[:2] == '\037\213':
161 | # Data is gzip encoded, decode it
162 | compressedstream = StringIO.StringIO(dat)
163 | gzipper = gzip.GzipFile(fileobj=compressedstream)
164 | extracted_data = gzipper.read()
165 | dat = extracted_data
166 |
167 | # Write it to a file
168 | file.write(dat)
169 | file.close()
170 | # return file path
171 | return path+" "+url
172 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feelfar/115proxy-for-kodi/dd39788cbf9a43ff605519a5e651f90fcaf362b0/icon.png
--------------------------------------------------------------------------------
/lib/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/lib/comm.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # comm.py
3 | from __future__ import unicode_literals
4 | import sys
5 | import xbmc,xbmcvfs,json,gzip,time,os,re
6 |
7 | def with_metaclass(meta, *bases):
8 | """Create a base class with a metaclass."""
9 | # This requires a bit of explanation: the basic idea is to make a dummy
10 | # metaclass for one level of class instantiation that replaces itself with
11 | # the actual metaclass.
12 | class metaclass(type):
13 |
14 | def __new__(cls, name, this_bases, d):
15 | return meta(name, bases, d)
16 |
17 | @classmethod
18 | def __prepare__(cls, name, this_bases):
19 | return meta.__prepare__(name, bases)
20 | return type.__new__(metaclass, 'temporary_class', (), {})
21 |
22 | def ensure_text(s, encoding='utf-8', errors='strict'):
23 | if isinstance(s, bytes):
24 | return s.decode(encoding, errors)
25 | elif isinstance(s, str):
26 | return s
27 | else:
28 | raise TypeError("not expecting type '%s'" % type(s))
29 |
30 | def ensure_binary(s, encoding='utf-8', errors='strict'):
31 | if isinstance(s, str):
32 | return s.encode(encoding, errors)
33 | elif isinstance(s, bytes):
34 | return s
35 | else:
36 | raise TypeError("not expecting type '%s'" % type(s))
--------------------------------------------------------------------------------
/pyhtml.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 |
4 | PyHTML
5 | ======
6 |
7 | Simple HTML generator for Python.
8 |
9 |
10 | Usage:
11 |
12 | Lets create a tag.
13 |
14 | >>> t = div()
15 | >>> t
16 | div()
17 |
18 |
19 | Tags can be rendered by converting to string.
20 |
21 | >>> str(t)
22 | ''
23 |
24 |
25 | Printing an object automatically calls str() with that object.
26 | I will keep printing tags in this tutorial for clarity.
27 |
28 | >>> print(div())
29 |
30 |
31 |
32 | Parantheses can be omitted if the tag has no content.
33 |
34 | >>> print(div)
35 |
36 |
37 |
38 | Some tags are self closing.
39 | >>> print(hr)
40 |
41 |
42 |
43 | You can put some content into the tag.
44 | >>> print(div('content'))
45 |
46 | content
47 |
48 |
49 |
50 | You can set attributes of the tag.
51 |
52 | >>> print(div(lang='tr', id='content', class_="bar", data_value="foo"))
53 |
54 |
55 |
56 | Or both:
57 |
58 | >>> print(div(lang='tr')('content'))
59 |
60 | content
61 |
62 |
63 |
64 | Content can be anything which can be converted to string.
65 |
66 | If content is a callable, it will be called with a one argument
67 | that is the context you pass to render() as keyword arguments.
68 |
69 | >>> greet = lambda ctx: 'Hello %s' % ctx.get('user', 'guest')
70 | >>> greeting = div(greet)
71 | >>> print(greeting)
72 |
73 | Hello guest
74 |
75 | >>> print(greeting.render(user='Cenk'))
76 |
77 | Hello Cenk
78 |
79 |
80 |
81 | You can give list of items as content.
82 |
83 | >>> print(div(nav(), greet, hr))
84 |
85 |
86 | Hello guest
87 |
88 |
89 |
90 |
91 | You can give give a callable returning a list as content.
92 |
93 | >>> items = lambda ctx: [li('a'), li('b')]
94 | >>> print(ul(items))
95 |
96 |
97 | a
98 |
99 |
100 | b
101 |
102 |
103 |
104 |
105 | You can give give a generator as content.
106 |
107 | >>> def items(ctx):
108 | ... for i in range(3):
109 | ... yield li(i)
110 | >>> print(ul(items))
111 |
112 |
113 | 0
114 |
115 |
116 | 1
117 |
118 |
119 | 2
120 |
121 |
122 |
123 |
124 | You can nest tags.
125 |
126 | >>> print(div(div(p('a paragraph'))))
127 |