├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── README.md ├── config.json ├── favicon.ico ├── requirements.txt └── web.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: build 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | 10 | tags: 11 | - 'v*.*.*' 12 | 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "build" 17 | build: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-latest 20 | 21 | # Steps represent a sequence of tasks that will be executed as part of the job 22 | steps: 23 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 24 | - uses: actions/checkout@v2 25 | 26 | # Runs a single command using the runners shell 27 | - name: Run a one-line script 28 | run: echo Hello, world! 29 | 30 | # Runs a set of commands using the runners shell 31 | - name: Run a multi-line script 32 | run: | 33 | echo Add other actions to build, 34 | echo test, and deploy your project. 35 | - name: Set env 36 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 37 | 38 | - name: Test 39 | run: | 40 | echo $RELEASE_VERSION 41 | echo ${{ env.RELEASE_VERSION }} 42 | echo ${{ github.ref }} 43 | - name: Run a multi-line script 44 | run: | 45 | echo Add other actions to build, 46 | echo test, and deploy your project. 47 | docker run -v $GITHUB_WORKSPACE:/src cdrx/pyinstaller-windows:latest 'pyinstaller -F -i favicon.ico --clean -y --dist ./ --workpath /tmp web.py' 48 | docker run -v $GITHUB_WORKSPACE:/src cdrx/pyinstaller-linux:latest 'pyinstaller -F --clean -y --dist ./ --workpath /tmp -w web.py' 49 | 50 | #zip -r $GITHUB_WORKSPACE/onedrive_share_${{ env.RELEASE_VERSION }}.zip $GITHUB_WORKSPACE/main.exe $GITHUB_WORKSPACE/linux.exe 51 | 52 | - name: Create Release 53 | id: create_release 54 | uses: actions/create-release@v1 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | with: 58 | tag_name: ${{ env.RELEASE_VERSION }} 59 | release_name: ${{ env.RELEASE_VERSION }} 60 | draft: false 61 | prerelease: false 62 | - name: Upload Release Asset 63 | id: upload-release-asset-bot-linux 64 | uses: actions/upload-release-asset@v1 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 67 | with: 68 | upload_url: ${{ steps.create_release.outputs.upload_url }} 69 | #asset_path: ${{ github.workspace }}/onedrive_share_${{ env.RELEASE_VERSION }}.zip 70 | asset_path: web 71 | asset_name: web 72 | asset_content_type: application/octet-stream 73 | #asset_content_type: application/zip 74 | - name: Upload Release Asset 75 | id: upload-release-asset-bot-win 76 | uses: actions/upload-release-asset@v1 77 | env: 78 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 79 | with: 80 | upload_url: ${{ steps.create_release.outputs.upload_url }} 81 | #asset_path: ${{ github.workspace }}/onedrive_share_${{ env.RELEASE_VERSION }}.zip 82 | asset_path: web.exe 83 | asset_name: web.exe 84 | #asset_content_type: application/octet-stream 85 | asset_content_type: application/exe -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # IPython 77 | profile_default/ 78 | ipython_config.py 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | .dmypy.json 111 | dmypy.json 112 | 113 | # Pyre type checker 114 | .pyre/ 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Emby-python 2 | 一个emby直链工具 3 | 4 | 5 | 6 | [使用教程](https://weinb.top/index.php/archives/167/) -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "main_site" : "http://127.0.0.1:8096/", 3 | "main_port": "8096", 4 | "new_port" :"2333", 5 | "api_key" : "1fb2477b5cb14d32843753xxxxxxxx", 6 | "redirects" : "True", 7 | "password_key": "True", 8 | "password_value": "xxxx", 9 | "replace_list": [ 10 | {"from": "/media/video/","to": "http://xxxx.weinb.top/shi/"}, 11 | {"from": "/media/adult/","to": "http://xxxx.weinb.top/adult/emby/"} 12 | 13 | ] 14 | 15 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666wcy/emby-python/f262d3f28f8157f8f29ee99246d46994a48edf91/favicon.ico -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==2.0.1 2 | requests==2.25.1 -------------------------------------------------------------------------------- /web.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flask import Response 3 | import flask,requests 4 | from flask import Flask,redirect 5 | import os 6 | import json 7 | 8 | data={} 9 | if os.path.isfile("config.json") == True: 10 | print("配置文件存在,直接读取") 11 | try: 12 | 13 | with open("config.json", "r", encoding='utf-8') as jsonFile: 14 | data = json.load(jsonFile) 15 | jsonFile.close() 16 | 17 | try: 18 | main_site = data['main_site'] 19 | main_port= data['main_port'] 20 | new_port = data['new_port'] 21 | api_key = data['api_key'] 22 | redirects= data['redirects'] 23 | password_key= data['password_key'] 24 | if password_key=="True": 25 | password_value = data['password_value'] 26 | replace_list = data['replace_list'] 27 | 28 | 29 | except Exception as e: 30 | print(e) 31 | 32 | except Exception as e: 33 | print(e) 34 | 35 | else: 36 | input("请先文件目录下配置config.json") 37 | 38 | with open("config.json", "a") as file: # 只需要将之前的”w"改为“a"即可,代表追加内容 39 | file.close() 40 | exit() 41 | 42 | 43 | 44 | 45 | 46 | app = Flask(__name__) 47 | 48 | 49 | @app.route('/', methods=['GET',"POST"]) 50 | def index(): 51 | 52 | #return redirect("/web/index.html") 53 | url=flask.request.url 54 | 55 | return redirect(url+"web/index.html") 56 | #return redirect(url_for('/web/index.html'),code=302) 57 | 58 | #http://176.113.81.126:8098/emby/videos/64292/stream.mp4?DeviceId=a8c06645-2729-4edc-ae73-09593a775031&MediaSourceId=2858a4cd04bfc490f714a625ecf7c537&Static=true&PlaySessionId=987cca26085644cd86fd2fbcd54ebc71&api_key=b82f726ac4be4817ac51294614674ebb 59 | #http://127.0.0.1:8096/emby/videos/64292/stream.mp4?DeviceId=a8c06645-2729-4edc-ae73-09593a775031&MediaSourceId=2858a4cd04bfc490f714a625ecf7c537&Static=true&PlaySessionId=987cca26085644cd86fd2fbcd54ebc71&api_key=b82f726ac4be4817ac51294614674ebb 60 | 61 | 62 | 63 | @app.route('/',methods=['GET',"POST"]) 64 | def proxy(path): 65 | 66 | headers=dict(flask.request.headers) 67 | 68 | '''for key, value in headers.items(): 69 | # recurse into nested dicts 70 | headers[key] = str(value).replace(domainsite, true_site)''' 71 | par=flask.request.query_string.decode() 72 | if par !="": 73 | url=f'{main_site}{path}?{par}' 74 | else: 75 | url=f'{main_site}{path}' 76 | 77 | if "stream" in url: 78 | MediaSourceId = flask.request.args.get('MediaSourceId') 79 | info_url = f"{main_site}emby/Items?Fields=Path&Ids={MediaSourceId}&api_key={api_key}" 80 | info_json = requests.get(url=info_url).json() 81 | index_url = str(info_json['Items'][0]['Path']) 82 | for a in replace_list: 83 | print(info_json['Items'][0]['Path'],a['from'], a['to']) 84 | index_url = index_url.replace(a['from'], a['to']) 85 | print(f"处理后的直链:{index_url}") 86 | if redirects=="True": 87 | 88 | 89 | true_result=requests.get(index_url,allow_redirects=False) 90 | 91 | #return redirect(index_url) 92 | if true_result.status_code==200 and password_key=="True": 93 | data = {"password1": password_value} 94 | true_result = requests.post(index_url, allow_redirects=False, data=data) 95 | 96 | true_url=dict(true_result.headers)['Location'] 97 | print(f"重定向后的直链{true_url}") 98 | return redirect(true_url) 99 | else: 100 | true_url=index_url 101 | 102 | return redirect(true_url) 103 | 104 | elif "Download" in url and "Items" in url: 105 | MediaSourceId = flask.request.args.get('mediaSourceId') 106 | info_url = f"{main_site}emby/Items?Fields=Path&Ids={MediaSourceId}&api_key={api_key}" 107 | info_json = requests.get(url=info_url).json() 108 | 109 | for a in replace_list: 110 | index_url = str(info_json['Items'][0]['Path']).replace(a['from'], a['to']) 111 | print(f"处理后的直链:{index_url}") 112 | 113 | true_result=requests.get(index_url,allow_redirects=False) 114 | 115 | if true_result.status_code==200 and password_key=="True": 116 | data = {"password1": "wcy98151"} 117 | true_result = requests.post(index_url, allow_redirects=False, data=data) 118 | 119 | #return redirect(index_url) 120 | true_url=dict(true_result.headers)['Location'] 121 | #print(true_url) 122 | return redirect(true_url) 123 | 124 | 125 | 126 | if flask.request.method == 'GET': 127 | resp = requests.get(url=url,headers=headers) 128 | 129 | excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection'] 130 | headers = [(name, value) for (name, value) in resp.raw.headers.items() if name.lower() not in excluded_headers] 131 | response = Response(resp.content, resp.status_code, headers) 132 | return response 133 | elif flask.request.method == 'POST': 134 | data=flask.request.data 135 | 136 | resp = requests.post(url=url,headers=headers,data=data) 137 | 138 | 139 | excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection'] 140 | headers = [(name, value) for (name, value) in resp.raw.headers.items() if name.lower() not in excluded_headers] 141 | response = Response(resp.content, resp.status_code, headers) 142 | return response 143 | 144 | 145 | 146 | if __name__ == '__main__': 147 | 148 | 149 | app.run(host='0.0.0.0', port=new_port) --------------------------------------------------------------------------------