diff --git a/TikTok_EN.py b/TikTok_EN.py
new file mode 100644
index 0000000..9649871
--- /dev/null
+++ b/TikTok_EN.py
@@ -0,0 +1,275 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+# @Author: https://github.com/Evil0ctal/
+# @Time: 2021/11/06
+# @Update: 2021/11/19
+"""
+@Function:
+This project uses PyWebIO, Requests, Flask as Python libraries
+to download TikTok's videos/gallery without watermark.
+It can be used to download videos/gallery that the author has forbidden to download.
+At the same time, it can be used with iOS shortcut APP
+to cooperate with this project's API to realize internal download.
+"""
+
+
+from pywebio import config, session
+from pywebio.input import *
+from pywebio.output import *
+from pywebio.platform.flask import webio_view
+from flask import Flask, request, jsonify
+from retrying import retry
+import time
+import requests
+import re
+import json
+
+
+app = Flask(__name__)
+title = "TikTok Downloader"
+description = "TikTok video/gallery analysis online"
+headers = {
+ 'user-agent': 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36 Edg/87.0.664.66'
+}
+
+
+def find_url(string):
+ # Parse the link in the Douyin share password and return to the list
+ url = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', string)
+ return url
+
+
+def valid_check(kou_ling):
+ # Verify the content of the input
+ if find_url(kou_ling):
+ return None
+ else:
+ return 'TikTok share format is not correct!'
+
+
+def error_msg():
+ # Output a useless message
+ put_html("
")
+ put_text("The input content could not be parsed. Please check the input or try again later. If multiple attempts still fail, please go issues.")
+ put_html("")
+
+
+def error_log(e):
+ # Log the error in the logs.txt
+ date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
+ with open('logs.txt', 'a') as f:
+ f.write(date + ": " + str(e) + '\n')
+
+
+def loading():
+ # Write a progress bar to pretend :)
+ set_scope('bar', position=3)
+ with use_scope('bar'):
+ put_processbar('bar')
+ for i in range(1, 4):
+ set_processbar('bar', i / 3)
+ time.sleep(0.1)
+
+
+@retry(stop_max_attempt_number=3)
+def get_video_info(original_url):
+ # Use the official interface to parse the link information
+ try:
+ # Original video link
+ r = requests.get(url=original_url)
+ key = re.findall('video/(\d+)?', str(r.url))[0]
+ api_url = f'https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids={key}'
+ js = json.loads(requests.get(url=api_url, headers=headers).text)
+ # Determine whether it is an atlas
+ try:
+ image_data = js['item_list'][0]['images']
+ # Gallery background audio
+ image_music = str(js['item_list'][0]['music']['play_url']['url_list'][0])
+ # Gallery title
+ image_title = str(js['item_list'][0]['desc'])
+ # Author's nickname
+ image_author = str(js['item_list'][0]['author']['nickname'])
+ # Atlas Author TikTok ID
+ image_author_id = str(js['item_list'][0]['author']['unique_id'])
+ if image_author_id == "":
+ # If the author has not modified the Douyin ID, this value should be used to avoid being unable to obtain its Douyin ID
+ image_author_id = str(js['item_list'][0]['author']['short_id'])
+ # De-watermarked atlas link
+ images_url = []
+ for data in image_data:
+ images_url.append(data['url_list'][0])
+ image_info = [images_url, image_music, image_title, image_author, image_author_id, original_url]
+ return image_info, 'image'
+ # Determined as a video after reporting an Exception
+ except:
+ # Video link after removing watermark
+ video_url = str(js['item_list'][0]['video']['play_addr']['url_list'][0]).replace('playwm', 'play')
+ # Video background audio
+ video_music = str(js['item_list'][0]['music']['play_url']['url_list'][0])
+ # Video title
+ video_title = str(js['item_list'][0]['desc'])
+ # Video author nickname
+ video_author = str(js['item_list'][0]['author']['nickname'])
+ # Video Author TikTok ID
+ video_author_id = str(js['item_list'][0]['author']['unique_id'])
+ if video_author_id == "":
+ # If the author has not modified the Douyin ID, this value should be used to avoid being unable to obtain its Douyin ID
+ video_author_id = str(js['item_list'][0]['author']['short_id'])
+ # Return a list containing data
+ video_info = [video_url, video_music, video_title, video_author, video_author_id, original_url]
+ return video_info, 'video'
+ except Exception as e:
+ # Exception catch
+ error_log(e)
+
+
+@app.route("/api")
+def webapi():
+ # Create a Flask application to get POST parameters and return the results
+ try:
+ post_content = request.args.get("url")
+ if post_content:
+ response_data, result_type = get_video_info(post_content)
+ if result_type == 'image':
+ # Return atlas information json
+ return jsonify(Type=result_type, image_url=response_data[0], image_music=response_data[1],
+ image_title=response_data[2], image_author=response_data[3],
+ image_author_id=response_data[4], original_url=response_data[5])
+ else:
+ # Return video information json
+ return jsonify(Type=result_type, video_url=response_data[0], video_music=response_data[1],
+ video_title=response_data[2], video_author=response_data[3],
+ video_author_id=response_data[4], original_url=response_data[5])
+ except Exception as e:
+ # Exception catch
+ error_log(e)
+ return jsonify(Message="Parsing Failed", Reason=str(e), Result=False)
+
+
+def put_result(item):
+ # Output the form to the front end according to the parsed format
+ video_info, result_type = get_video_info(item)
+ if result_type == 'video':
+ put_table([
+ ['type', 'content'],
+ ['Format:', result_type],
+ ['Video link: ', put_link('Click to open the video', video_info[0], new_window=True)],
+ ['BGM link: ', put_link('Click to open audio', video_info[1], new_window=True)],
+ ['Video title: ', video_info[2]],
+ ['Author nickname: ', video_info[3]],
+ ['Author TikTok ID: ', video_info[4]],
+ ['Original video link: ', put_link('Click to open the original video', video_info[5], new_window=True)]
+ ])
+ else:
+ put_table([
+ ['type', 'content'],
+ ['Format:', result_type],
+ ])
+ for i in video_info[0]:
+ put_table([
+ ['Picture link: ', put_link('Click to open the picture', i, new_window=True)]
+ ])
+ put_table([
+ ['BGM link: ', put_link('Click to open audio', video_info[1], new_window=True)],
+ ['Video title: ', video_info[2]],
+ ['Author nickname: ', video_info[3]],
+ ['Author TikTok ID: ', video_info[4]],
+ ['Gallery link: ', put_link('Click to open the original gallery', video_info[5], new_window=True)]
+ ])
+
+
+def github_pop_window():
+ with popup("Github"):
+ put_html('
⭐Welcome Star
')
+ put_markdown('[TikTokDownloader_PyWebIO](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO)')
+
+
+def feedback_pop_window():
+ with popup("You can give feedback in the following ways"):
+ put_html('
')
+ put_markdown('WeChat:[Evil0ctal](https://mycyberpunk.com/)')
+ put_html('')
+
+
+def api_document_pop_window():
+ with popup("API documentation"):
+ put_markdown("🛰️API usage")
+ put_markdown("The API can convert the request parameters into a non-watermarked video/picture direct link that needs to be extracted, and can be used with IOS shortcuts to achieve in-app download.")
+ put_link('[Chinese document]', 'https://github.com/Evil0ctal/TikTokDownloader_PyWebIO#%EF%B8%8Fapi%E4%BD%BF%E7%94%A8', new_window=True)
+ put_html(' ')
+ put_link('[English document]', 'https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/blob/main/README-EN.md#%EF%B8%8Fapi-usage', new_window=True)
+
+
+def about_popup_window():
+ with popup('More information'):
+ put_html('
⚠️About parsing failure
')
+ put_text('It is currently known that a large number of visits to the TikTok API in a short period of time may trigger its verification code.')
+ put_text('If the analysis fails several times, please wait for a while and then try again.')
+ put_link("Download the error log of this site", 'http://18.144.82.183:8888/down/tvoFwscxmQkE')
+ put_html('')
+ put_html('
🌐Video/Atlas batch download
')
+ put_markdown('You can use tools such as [IDM](https://www.internetdownloadmanager.com/) to sniff the links to the results page.')
+ put_html('')
+ put_html('
📣About this project
')
+ put_markdown('I have limited skills, so be welcome to [GitHub](https://github.com/Evil0ctal/TikTokDownload_PyWebIO/pulls) submit a pull request.')
+ put_html('')
+ put_html('