fix download audio

This commit is contained in:
Ghost 2024-01-06 17:52:18 +01:00
parent af55408c8a
commit fbfaeb5cac
6 changed files with 102 additions and 43 deletions

View File

@ -11,26 +11,32 @@ Script to download film from streaming community without selenium, select movie
</p>
## Requirement
* python [3.9](https://www.python.org/downloads/release/python-390/)
* ffmpeg [win](https://www.gyan.dev/ffmpeg/builds/)
## Installation library
```bash
pip install -r requirements.txt
```
## Run
```bash
python run.py
```
## Features
- Search for movies.
- Search and download TV series episode.
- Add audio if missing.
- Auto add audio if missing
## Tutorial
https://www.youtube.com/watch?v=Ok7hQCgxqLg&ab_channel=Nothing
## Authors
- [@Ghost6446](https://www.github.com/Ghost6446)

View File

@ -4,7 +4,7 @@
from Stream.util.headers import get_headers
from Stream.util.console import console, msg
from Stream.util.m3u8 import dw_m3u8, join_audio_to_video
from Stream.util.util import convert_utf8_name, check_audio_presence
from Stream.util.util import convert_utf8_name
# General import
import requests, os, re, json
@ -96,20 +96,13 @@ def main_dw_tv(tv_id, tv_name, version, domain):
m3u8_url = get_m3u8_url(json_win_video, json_win_param)
m3u8_key = get_m3u8_key_ep(json_win_video, json_win_param, tv_name, season_select, index_ep_select+1, eps[index_ep_select]['name'])
mp4_name = lower_tv_name.replace("+", "_") + "_"+str(season_select)+"__"+str(index_ep_select+1)
mp4_name = f"{lower_tv_name.replace('+', '_')}_{str(season_select)}_{str(index_ep_select+1)}"
mp4_format = mp4_name + ".mp4"
base_path_mp4 = os.path.join("videos", mp4_format)
base_audio_path = os.path.join("videos", mp4_format + "_audio.mp4")
mp4_path = os.path.join("videos", mp4_format)
dw_m3u8(m3u8_url, m3u8_key, base_path_mp4)
m3u8_url_audio = get_m3u8_audio(json_win_video, json_win_param, tv_name, season_select, index_ep_select+1, eps[index_ep_select]['name'])
if not check_audio_presence(base_path_mp4):
console.print("[red]Audio is not present, start download (Use all CPU)")
m3u8_url_audio = get_m3u8_audio(json_win_video, json_win_param, tv_name, season_select, index_ep_select+1, eps[index_ep_select]['name'])
if m3u8_url_audio != None:
console.print("[red]=> Use m3u8 audio")
dw_m3u8(m3u8_url_audio, m3u8_key, base_audio_path)
join_audio_to_video(base_audio_path, base_path_mp4, base_audio_path)
os.remove(base_audio_path)
os.remove(base_path_mp4)
dw_m3u8(m3u8_url, m3u8_url_audio, m3u8_key, mp4_path)

View File

@ -1,5 +1,5 @@
__title__ = 'Streaming_community'
__version__ = 'v0.6.1'
__version__ = 'v0.7.0'
__author__ = 'Ghost6446'
__description__ = 'A command-line program to download film'
__license__ = 'MIT License'

View File

@ -11,6 +11,7 @@ import moviepy.editor as mp
# Class import
from Stream.util.console import console
from Stream.util.headers import get_headers
from Stream.util.util import there_is_audio, merge_ts_files
# Variable
os.makedirs("videos", exist_ok=True)
@ -19,17 +20,20 @@ os.makedirs("videos", exist_ok=True)
# [ main class ]
class M3U8Downloader:
def __init__(self, m3u8_url, key=None, output_filename="output.mp4"):
def __init__(self, m3u8_url, m3u8_audio = None, key=None, output_filename="output.mp4"):
self.m3u8_url = m3u8_url
self.m3u8_audio = m3u8_audio
self.key = key
self.output_filename = output_filename
self.segments = []
self.segments_audio = []
self.iv = None
self.key = bytes.fromhex(key)
self.temp_folder = "tmp"
os.makedirs(self.temp_folder, exist_ok=True)
self.download_audio = False
def decode_ext_x_key(self, key_str):
key_str = key_str.replace('"', '').lstrip("#EXT-X-KEY:")
@ -42,6 +46,9 @@ class M3U8Downloader:
self.iv = bytes.fromhex(raw_iv.replace("0x", ""))
def parse_m3u8(self, m3u8_content):
if self.m3u8_audio != None:
m3u8_audio_line = str(requests.get(self.m3u8_audio).content).split("\\n")
m3u8_base_url = self.m3u8_url.rstrip(self.m3u8_url.split("/")[-1])
lines = m3u8_content.split('\n')
@ -57,9 +64,11 @@ class M3U8Downloader:
if not ts_url.startswith("http"):
ts_url = m3u8_base_url + ts_url
self.segments.append(ts_url)
console.print(f"[cyan]Find: {len(self.segments)} ts file to download")
self.segments.append(ts_url)
if self.m3u8_audio != None: self.segments_audio.append(m3u8_audio_line[i+1])
console.log(f"[cyan]Find: {len(self.segments)} ts file to download")
def download_m3u8(self):
response = requests.get(self.m3u8_url, headers={'user-agent': get_headers()})
@ -68,6 +77,19 @@ class M3U8Downloader:
m3u8_content = response.text
self.parse_m3u8(m3u8_content)
# Check there is audio in first ts file
path_test_ts_file = os.path.join(self.temp_folder, "ts_test.ts")
if self.key and self.iv:
open(path_test_ts_file, "wb").write(self.decrypt_ts(requests.get(self.segments[0]).content))
else:
open(path_test_ts_file, "wb").write(requests.get(self.segments[0]).content)
if not there_is_audio(path_test_ts_file):
self.download_audio = True
console.log("[cyan]=> Make req to get video and audio file")
os.remove(path_test_ts_file)
def decrypt_ts(self, encrypted_data):
cipher = Cipher(algorithms.AES(self.key), modes.CBC(self.iv), backend=default_backend())
decryptor = cipher.decryptor()
@ -75,27 +97,50 @@ class M3U8Downloader:
return decrypted_data
def decrypt_and_save(self, args):
ts_url, index = args
ts_filename = os.path.join(self.temp_folder, f"{index}.ts")
def decrypt_and_save(self, index):
video_ts_url = self.segments[index]
video_ts_filename = os.path.join(self.temp_folder, f"{index}_v.ts")
if not os.path.exists(ts_filename):
ts_response = requests.get(ts_url, headers={'user-agent': get_headers()}).content
if self.download_audio:
audio_ts_url = self.segments_audio[index]
audio_ts_filename = os.path.join(self.temp_folder, f"{index}_a.ts")
video_audio_ts_filename = os.path.join(self.temp_folder, f"{index}_v_a.ts")
# Download video or audio ts file
if not os.path.exists(video_ts_url):
ts_response = requests.get(video_ts_url, headers={'user-agent': get_headers()}).content
if self.key and self.iv:
decrypted_data = self.decrypt_ts(ts_response)
with open(ts_filename, "wb") as ts_file:
with open(video_ts_filename, "wb") as ts_file:
ts_file.write(decrypted_data)
else:
with open(ts_filename, "wb") as ts_file:
with open(video_ts_filename, "wb") as ts_file:
ts_file.write(ts_response)
# Donwload only audio ts file
if self.download_audio:
ts_response = requests.get(audio_ts_url, headers={'user-agent': get_headers()}).content
if self.key and self.iv:
decrypted_data = self.decrypt_ts(ts_response)
with open(audio_ts_filename, "wb") as ts_file:
ts_file.write(decrypted_data)
else:
with open(audio_ts_filename, "wb") as ts_file:
ts_file.write(ts_response)
# Join ts video and audio
merge_ts_files(video_ts_filename, audio_ts_filename, video_audio_ts_filename)
os.remove(video_ts_filename)
os.remove(audio_ts_filename)
def download_and_save_ts(self):
with ThreadPoolExecutor(max_workers=30) as executor:
list(tqdm(executor.map(self.decrypt_and_save, zip(self.segments, range(len(self.segments)))),
total=len(self.segments), unit="bytes", unit_scale=True, unit_divisor=1024, desc="[yellow]Download"))
list(tqdm(executor.map(self.decrypt_and_save, range(len(self.segments)) ), total=len(self.segments), unit="bytes", unit_scale=True, unit_divisor=1024, desc="[yellow]Download"))
def join_ts_files(self):
@ -110,12 +155,12 @@ class M3U8Downloader:
relative_path = os.path.relpath(os.path.join(self.temp_folder, ts_file), current_dir)
f.write(f"file '{relative_path}'\n")
console.print("[cyan]Start join all file")
console.log("[cyan]Start join all file")
try:
(
ffmpeg.input(file_list_path, format='concat', safe=0).output(self.output_filename, c="copy", loglevel="quiet").run()
ffmpeg.input(file_list_path, format='concat', safe=0).output(self.output_filename, c='copy', loglevel='quiet').run()
)
console.print(f"[cyan]Clean ...")
console.log(f"[cyan]Clean ...")
except ffmpeg.Error as e:
print(f"Errore durante il salvataggio del file MP4: {e}")
finally:
@ -124,9 +169,9 @@ class M3U8Downloader:
# [ function ]
def dw_m3u8(url, key=None, output_filename="output.mp4"):
def dw_m3u8(url, audio_url=None, key=None, output_filename="output.mp4"):
downloader = M3U8Downloader(url, key, output_filename)
downloader = M3U8Downloader(url, audio_url, key, output_filename)
downloader.download_m3u8()
downloader.download_and_save_ts()

View File

@ -1,15 +1,31 @@
# 4.01.2023
# Import
from moviepy.editor import VideoFileClip
import ffmpeg, subprocess
def convert_utf8_name(name):
return str(name).encode('utf-8').decode('latin-1')
def check_audio_presence(file_path):
try:
video_clip = VideoFileClip(file_path)
audio = video_clip.audio
return audio is not None
except Exception as e:
print(f"Si è verificato un errore: {str(e)}")
def there_is_audio(ts_file_path):
probe = ffmpeg.probe(ts_file_path)
return any(stream['codec_type'] == 'audio' for stream in probe['streams'])
def merge_ts_files(video_path, audio_path, output_path):
"""command = [
'ffmpeg',
'-i', video_path,
'-i', audio_path,
'-c', 'copy',
'-map', '0',
'-map', '1',
'-y', output_path
]
subprocess.run(command)"""
input_video = ffmpeg.input(video_path)
input_audio = ffmpeg.input(audio_path)
ffmpeg.output(input_video, input_audio, output_path, format='mpegts', acodec='copy', vcodec='copy', loglevel='quiet').run()

View File

@ -5,5 +5,4 @@ tqdm
rich
random-user-agent
ffmpeg-python
moviepy
cryptography