diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b730ea7..f130f16 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,8 @@ -name: Test +name: Upload Videos on: - push + push: + branches: + - test jobs: build: @@ -11,9 +13,16 @@ jobs: uses: actions/checkout@v2 - name: Configure environment - run: echo AUTH_TOKEN=${{ secrets.AUTH_TOKEN }} >> .env + run: | + echo AUTH_TOKEN=${{ secrets.AUTH_TOKEN }} >> .env + echo CLIENT_ID=${{ secrets.CLIENT_ID }} >> .env + echo CLIENT_SECRET=${{ secrets.CLIENT_SECRET }} >> .env + echo REFRESH_TOKEN=${{ secrets.REFRESH_TOKEN }} >> .env - name: Run the script run: | + chmod +x download_ngrok.sh + ./download_ngrok.sh ${{ secrets.NGROK_TOKEN }} chmod +x ./script.sh ./script.sh + diff --git a/download_ngrok.sh b/download_ngrok.sh new file mode 100755 index 0000000..10506da --- /dev/null +++ b/download_ngrok.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +url="https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz" +wget $url -O ngrok.tgz +tar -xvf ngrok.tgz +rm -rf ngrok.tgz +chmod +x ngrok +./ngrok config add-authtoken $1 + diff --git a/index.html b/index.html new file mode 100644 index 0000000..269a5b9 --- /dev/null +++ b/index.html @@ -0,0 +1,134 @@ + + + + + + HTML Form + + + + Google ile Giriş Yap + +

+
+ + +

+
+
+ + + + +
+
+
+
+ + +

+ +
+ + + + + + + diff --git a/runserver.sh b/runserver.sh new file mode 100755 index 0000000..879a0ac --- /dev/null +++ b/runserver.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +./ngrok http 8000 > /dev/null & +pid=$! + +sleep 10 + +url=$(curl -sq http://localhost:4045/api/tunnels | jq -r '.tunnels[0].public_url') +if [ -z "$url" ]; then + url=$(curl -sq http://localhost:4040/api/tunnels | jq -r '.tunnels[0].public_url') +fi +if [ -z "$url" ]; then + echo "Failed to get URL" + exit 1 +fi + +echo "Server is live at $url" + +pip3 install -r requirements.txt +python3 server.py + +# kill ngrok +kill $pid +pkill -9 ngrok diff --git a/script.sh b/script.sh old mode 100644 new mode 100755 index ee8bdd8..35fbb7d --- a/script.sh +++ b/script.sh @@ -1,5 +1,10 @@ #!/bin/bash +chmod +x runserver.sh +./runserver.sh +exit 0 + + # This script will be executed inside the container pip3 install -r requirements.txt diff --git a/server.py b/server.py new file mode 100644 index 0000000..ef3ab95 --- /dev/null +++ b/server.py @@ -0,0 +1,75 @@ +from http import server +import json +from upload_video import DownloadVideos, GetRefreshTokenFromCode + +class MyHandler(server.BaseHTTPRequestHandler): + def do_GET(self): + # send index.html file to client + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + with open('index.html', 'rb') as f: + self.wfile.write(f.read()) + + def do_POST(self): + # request body parameters + # token (received from client) + # List of videos (received from client) + + print("POST request received") + + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length) + + print("Parsing data...") + data = json.loads(post_data.decode('utf-8')) + code = data['code'] + videos = data['videos'] + + print("videos[0]:", videos[0]) + + print("Validating input...") + # validate input + assert type(code) == str + assert type(videos) == list + for video in videos: + assert type(video) == dict + assert 'title' in video + assert 'id' in video + + # get refresh token from code + + print("Getting refresh token...", flush = True) + if not code: + self.send_response(400) + self.end_headers() + self.wfile.write(b'Error: Code not found') + return + try: + refresh_token = GetRefreshTokenFromCode(code) + except Exception as e: + print("Error getting refresh token:", e) + self.send_response(500) + # data = {'error': 'Error getting refresh token'} + self.end_headers() + self.wfile.write(b'Error getting refresh token: ' + str(e).encode('utf-8')) + return + + print("Downloading videos...", flush = True) + self.send_response(200) + self.end_headers() + self.wfile.write(b'Videos downloading...') + # send response to client + self.finish() + + DownloadVideos(refresh_token, videos) + + exit(0) + + +server_address = ('', 8000) +httpd = server.HTTPServer(server_address, MyHandler) +httpd.serve_forever() + + + diff --git a/upload_video.py b/upload_video.py index c60c11c..ddf6b93 100644 --- a/upload_video.py +++ b/upload_video.py @@ -1,11 +1,34 @@ import json import os +import re from dotenv import load_dotenv import requests +import time +import subprocess # Load environment variables load_dotenv() + +def get_access_token(refresh_token = None): + # Get access token from a refresh token + # Return a tuple of (access_token, expires_in) + refresh_token = refresh_token or os.getenv('REFRESH_TOKEN') + client_id = os.getenv('CLIENT_ID') + client_secret = os.getenv('CLIENT_SECRET') + r = requests.post('https://oauth2.googleapis.com/token', data={ + 'client_id': client_id, + 'client_secret': client_secret, + 'refresh_token': refresh_token, + 'grant_type': 'refresh_token' + }) + print(f"Getting access token: {r.status_code}") + if not r.status_code == 200: + print("Failed to get access token!") + print(r.content) + # print(r.json()) + return r.json()['access_token'], r.json()['expires_in'] + def load_videos(path): # Load videos from a path # Return a list of videos @@ -18,16 +41,42 @@ def load_videos(path): def download_video(title, url): # Download a video from a url # Return a video object - os.system(f"curl '{url}' -o '{title}.mp4'") + print(f"Downloading video: {title}", flush = True) + os.system(f"curl -s '{url}' -o '{title}.mp4'") def check_if_auth_code_valid(auth_code): r = requests.get(f'https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=Ks-_Mh1QhMc&key={auth_code}') print(f"Checking if auth code is valid: {r.status_code}") - print(r.content) + if not r.status_code == 200: + print("Auth code is not valid!") + print(r.content) + assert r.status_code == 200 + +def get_download_link(video_id): + main_url = f'https://video.sibnet.ru/shell.php?videoid={video_id}' + r = requests.get(main_url) assert r.status_code == 200 + url = re.search(b"src: \"(.*)\", type: \"video/mp4\"", r.content).group(1).decode() + url = f"https://video.sibnet.ru{url}" + user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" + + r = requests.head(url, headers={'Referer': main_url, 'User-Agent': user_agent}, allow_redirects=False) + if not r.status_code == 302: + print("Failed to get download link!") + print(r.headers) + print(r.content) + url = r.headers.get('Location')[2:] + url = f"https://{url}" + + r = requests.get(url, headers={'User-Agent': user_agent}, allow_redirects=False) + url = r.headers.get('Location') + print(f"Getting download link: {r.status_code}", flush=True) + return url + def create_resumable_upload(auth_token, video_len, title): - r = requests.post(f"https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status,contentDetails&key={auth_token}", headers={ + r = requests.post("https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status,contentDetails", headers={ + 'Authorization': f'Bearer {auth_token}', 'Content-Type': 'application/json; charset=UTF-8', 'X-Upload-Content-Length': video_len, 'X-Upload-Content-Type': 'video/mp4' @@ -40,29 +89,90 @@ def create_resumable_upload(auth_token, video_len, title): 'privacyStatus': 'private' } }) - - print(r.status_code) - print(r.headers) + print(f"Creating resumable upload: {r.status_code}", flush = True) + if not r.status_code == 200: + print("Failed to create resumable upload!", flush=True) + print(r.content, flush=True) + # print(r.headers) return r.headers['Location'] def upload_video(location, title, auth_token, video_len): + print(f"Uploading video: {title}, length: {video_len}", flush = True) video_bin = open(f'{title}.mp4', 'rb').read() r = requests.put(location, data=video_bin, headers={ - 'Authorization:': f'Bearer {auth_token}', + 'Authorization': f'Bearer {auth_token}', 'Content-Type': 'video/mp4', 'Content-Length': video_len }) + print("Uploading success, status code:", r.status_code, flush=True) + +def delete_video(title): + print(f"Deleting video from container: {title}", flush=True) + try: + name = f"{title}.mp4" + subprocess.call(["rm", name]) + except Exception as e: + print("rm failed, error:", e) + +def main(): + auth_token = os.getenv('AUTH_TOKEN') + assert auth_token != None + # check_if_auth_code_valid(auth_token) + videos = load_videos('videos.json') + + time_now = time.time() + auth_token, expires_in = get_access_token() + + for video in videos: + title = video['title'] + video_id = int(video['id']) + url = get_download_link(video_id) + download_video(title, url) + video_len = str(os.path.getsize(f'{title}.mp4')) + if time.time() - time_now > expires_in: + auth_token, expires_in = get_access_token() + location = create_resumable_upload(auth_token, video_len, title) + upload_video(location, title, auth_token, video_len) + +def DownloadVideos(refresh_token, videos): + time_now = time.time() + auth_token, expires_in = get_access_token(refresh_token) + print("Videos:", videos, flush=True) + for video in videos: + title = video['title'] + video_id = int(video['id']) + print(f"Video: {title}", flush=True) + url = get_download_link(video_id) + download_video(title, url) + video_len = str(os.path.getsize(f'{title}.mp4')) + if time.time() - time_now > expires_in: + auth_token, expires_in = get_access_token() + location = create_resumable_upload(auth_token, video_len, title) + upload_video(location, title, auth_token, video_len) + delete_video(title) + +def GetRefreshTokenFromCode(code): + client_id = os.getenv('CLIENT_ID') + if not client_id: + raise Exception("Client ID not found!") + client_secret = os.getenv('CLIENT_SECRET') + if not client_secret: + raise Exception("Client Secret not found!") + r = requests.post('https://oauth2.googleapis.com/token', data={ + 'code': code, + 'client_id': client_id, + 'client_secret': client_secret, + 'redirect_uri': 'http://127.0.0.1:8000/callback', + 'grant_type': 'authorization_code' + }) + print(f"Getting refresh token from code: {r.status_code}") + if not r.status_code == 200: + print("Failed to get refresh token!") + print(r.content) + raise Exception(r.content) + return r.json()['refresh_token'] -auth_token = os.getenv('AUTH_TOKEN') -assert auth_token != None -check_if_auth_code_valid(auth_token) -videos = load_videos('videos.json') +if __name__ == '__main__': + main() -for video in videos: - title = video['title'] - url = video['url'] - download_video(title, url) - video_len = str(os.path.getsize(f'{title}.mp4')) - location = create_resumable_upload(auth_token, video_len, title) - upload_video(location, title, auth_token, video_len) diff --git a/videos.json b/videos.json index 214ebfe..49dc2d7 100644 --- a/videos.json +++ b/videos.json @@ -1,6 +1,10 @@ [ { - "title": "Re Zero Part 11", - "url": "https://dv8-2.sibnet.ru/44/50/43/4450436.mp4?st=aigsKKNCwNbeFo_M6avH5Q&e=1701010000&stor=8&noip=1" + "title": "Jujutsu Kaisen 23. Bölüm", + "id": "4265338" + }, + { + "title": "Jujutsu Kaisen 24. Bölüm", + "id": "4772671" } ]