MP4MDL 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- MP4MDL-0.1.0/MP4MDL.egg-info/PKG-INFO +23 -0
- MP4MDL-0.1.0/MP4MDL.egg-info/SOURCES.txt +21 -0
- MP4MDL-0.1.0/MP4MDL.egg-info/dependency_links.txt +1 -0
- MP4MDL-0.1.0/MP4MDL.egg-info/entry_points.txt +2 -0
- MP4MDL-0.1.0/MP4MDL.egg-info/requires.txt +3 -0
- MP4MDL-0.1.0/MP4MDL.egg-info/top_level.txt +1 -0
- MP4MDL-0.1.0/PKG-INFO +23 -0
- MP4MDL-0.1.0/mp4mdl/__init__.py +7 -0
- MP4MDL-0.1.0/mp4mdl/download/__init__.py +2 -0
- MP4MDL-0.1.0/mp4mdl/download/mp4_downloader.py +58 -0
- MP4MDL-0.1.0/mp4mdl/download/segment_downloader.py +41 -0
- MP4MDL-0.1.0/mp4mdl/lecteur/__init__.py +4 -0
- MP4MDL-0.1.0/mp4mdl/lecteur/oneupload.py +21 -0
- MP4MDL-0.1.0/mp4mdl/lecteur/sendvid.py +18 -0
- MP4MDL-0.1.0/mp4mdl/lecteur/sibnet.py +16 -0
- MP4MDL-0.1.0/mp4mdl/lecteur/vidmoly.py +25 -0
- MP4MDL-0.1.0/mp4mdl/mp4mdl.py +89 -0
- MP4MDL-0.1.0/mp4mdl/scrap/__init__.py +4 -0
- MP4MDL-0.1.0/mp4mdl/scrap/get_best_qualite_url.py +34 -0
- MP4MDL-0.1.0/mp4mdl/scrap/get_m3u8_url.py +41 -0
- MP4MDL-0.1.0/mp4mdl/scrap/get_mp4_url.py +57 -0
- MP4MDL-0.1.0/setup.cfg +4 -0
- MP4MDL-0.1.0/setup.py +29 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: MP4MDL
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Un outil pour télécharger et gérer des vidéos MP4
|
|
5
|
+
Home-page: https://git.maddoxserv.com/maddox/MP4MDL
|
|
6
|
+
Author: Maddox
|
|
7
|
+
Author-email: maddoxtes@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# Description
|
|
15
|
+
|
|
16
|
+
This project is a library that allows you to download MP4 files with video links.
|
|
17
|
+
|
|
18
|
+
# Website Supported
|
|
19
|
+
|
|
20
|
+
- ✅ video.sibnet.ru
|
|
21
|
+
- ✅ vidmoly.to
|
|
22
|
+
- ✅ oneupload.to
|
|
23
|
+
- ✅ sendvid.com
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
setup.py
|
|
2
|
+
MP4MDL.egg-info/PKG-INFO
|
|
3
|
+
MP4MDL.egg-info/SOURCES.txt
|
|
4
|
+
MP4MDL.egg-info/dependency_links.txt
|
|
5
|
+
MP4MDL.egg-info/entry_points.txt
|
|
6
|
+
MP4MDL.egg-info/requires.txt
|
|
7
|
+
MP4MDL.egg-info/top_level.txt
|
|
8
|
+
mp4mdl/__init__.py
|
|
9
|
+
mp4mdl/mp4mdl.py
|
|
10
|
+
mp4mdl/download/__init__.py
|
|
11
|
+
mp4mdl/download/mp4_downloader.py
|
|
12
|
+
mp4mdl/download/segment_downloader.py
|
|
13
|
+
mp4mdl/lecteur/__init__.py
|
|
14
|
+
mp4mdl/lecteur/oneupload.py
|
|
15
|
+
mp4mdl/lecteur/sendvid.py
|
|
16
|
+
mp4mdl/lecteur/sibnet.py
|
|
17
|
+
mp4mdl/lecteur/vidmoly.py
|
|
18
|
+
mp4mdl/scrap/__init__.py
|
|
19
|
+
mp4mdl/scrap/get_best_qualite_url.py
|
|
20
|
+
mp4mdl/scrap/get_m3u8_url.py
|
|
21
|
+
mp4mdl/scrap/get_mp4_url.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mp4mdl
|
MP4MDL-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: MP4MDL
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Un outil pour télécharger et gérer des vidéos MP4
|
|
5
|
+
Home-page: https://git.maddoxserv.com/maddox/MP4MDL
|
|
6
|
+
Author: Maddox
|
|
7
|
+
Author-email: maddoxtes@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# Description
|
|
15
|
+
|
|
16
|
+
This project is a library that allows you to download MP4 files with video links.
|
|
17
|
+
|
|
18
|
+
# Website Supported
|
|
19
|
+
|
|
20
|
+
- ✅ video.sibnet.ru
|
|
21
|
+
- ✅ vidmoly.to
|
|
22
|
+
- ✅ oneupload.to
|
|
23
|
+
- ✅ sendvid.com
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import urllib3
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
class mp4_downloader:
|
|
6
|
+
def __init__(self, headers, url, file_name, logger):
|
|
7
|
+
self.headers = headers
|
|
8
|
+
self.url = url
|
|
9
|
+
self.file_name = file_name
|
|
10
|
+
self.logger = logger
|
|
11
|
+
|
|
12
|
+
def download(self):
|
|
13
|
+
urllib3_logger = logging.getLogger("urllib3")
|
|
14
|
+
urllib3_logger.setLevel(logging.WARNING)
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
http = urllib3.PoolManager()
|
|
18
|
+
response = http.request('GET', self.url, headers=self.headers, preload_content=False)
|
|
19
|
+
|
|
20
|
+
if response.status == 403:
|
|
21
|
+
self.logger.error(f"Erreur 403 : {self.url} <-- si url est sibnet ses normal tu est banni pendant 2-5 jours")
|
|
22
|
+
return False
|
|
23
|
+
elif response.status != 200:
|
|
24
|
+
self.logger.error(f"Erreur lors du téléchargement: {response.status}")
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
with open(self.file_name, 'wb') as f:
|
|
28
|
+
chunk_size = 512 * 1024
|
|
29
|
+
|
|
30
|
+
total_downloaded = 0
|
|
31
|
+
start_time = time.time()
|
|
32
|
+
|
|
33
|
+
for data in response.stream(chunk_size):
|
|
34
|
+
f.write(data)
|
|
35
|
+
total_downloaded += len(data)
|
|
36
|
+
|
|
37
|
+
# Ajuster la taille du chunk en fonction de la vitesse
|
|
38
|
+
elapsed = time.time() - start_time
|
|
39
|
+
if elapsed >= 1: # Ajuster toutes les secondes
|
|
40
|
+
speed = total_downloaded / elapsed # octets/seconde
|
|
41
|
+
|
|
42
|
+
# Augmenter/diminuer chunk_size selon la vitesse
|
|
43
|
+
if speed > 1024*1024: # Si > 1 MB/s
|
|
44
|
+
chunk_size = min(chunk_size * 2, 5*1024*1024) # Max 5MB
|
|
45
|
+
elif speed < 512*1024: # Si < 512 KB/s
|
|
46
|
+
chunk_size = max(chunk_size // 2, 4096) # Min 4KB
|
|
47
|
+
|
|
48
|
+
total_downloaded = 0
|
|
49
|
+
start_time = time.time()
|
|
50
|
+
|
|
51
|
+
response.release_conn()
|
|
52
|
+
self.logger.debug(f"Téléchargement réussi : {self.file_name}")
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
except Exception as e:
|
|
56
|
+
if self.logger:
|
|
57
|
+
self.logger.error(f"Erreur lors du téléchargement: {str(e)}")
|
|
58
|
+
return False
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import ffmpeg
|
|
3
|
+
|
|
4
|
+
class segment_downloader:
|
|
5
|
+
def __init__(self, best_qualite_url, headers_dict, file_name, logger):
|
|
6
|
+
self.best_qualite_url = best_qualite_url
|
|
7
|
+
self.file_name = file_name
|
|
8
|
+
self.logger = logger
|
|
9
|
+
self.headers_dict = headers_dict
|
|
10
|
+
|
|
11
|
+
def download_segments(self):
|
|
12
|
+
try:
|
|
13
|
+
if os.path.exists(self.file_name):
|
|
14
|
+
os.remove(self.file_name)
|
|
15
|
+
|
|
16
|
+
# Créer le stream d'entrée avec les headers
|
|
17
|
+
input_stream = ffmpeg.input(
|
|
18
|
+
self.best_qualite_url,
|
|
19
|
+
protocol_whitelist="file,https,http,tls,tcp",
|
|
20
|
+
headers=self.headers_dict
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Configurer le stream de sortie
|
|
24
|
+
output_stream = ffmpeg.output(
|
|
25
|
+
input_stream,
|
|
26
|
+
self.file_name,
|
|
27
|
+
c='copy' # Copier les streams sans ré-encoder
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Exécuter la commande
|
|
31
|
+
ffmpeg.run(output_stream, capture_stdout=True, capture_stderr=True)
|
|
32
|
+
|
|
33
|
+
self.logger.debug(f"Téléchargement des segments réussi : {self.file_name}")
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
except ffmpeg.Error as e:
|
|
37
|
+
self.logger.error(f"Erreur ffmpeg : {e.stderr.decode()}")
|
|
38
|
+
return False
|
|
39
|
+
except Exception as e:
|
|
40
|
+
self.logger.error(f"Erreur lors du téléchargement des segments : {e}")
|
|
41
|
+
return False
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from ..scrap import get_m3u8_url, get_best_qualite_url
|
|
2
|
+
from ..download import segment_downloader
|
|
3
|
+
|
|
4
|
+
def oneupload(url, file_name, logger):
|
|
5
|
+
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',"Referer": "https://oneupload.net/", "Origin": "https://oneupload.net"}
|
|
6
|
+
headers_dict = {
|
|
7
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',
|
|
8
|
+
'Referer': 'https://oneupload.net/',
|
|
9
|
+
'Origin': 'https://oneupload.net'
|
|
10
|
+
}
|
|
11
|
+
match = r'file:"(https?://[^"]+\.m3u8[^"]*)"'
|
|
12
|
+
m3u8_url = get_m3u8_url(url=url, headers=headers, match=match, logger=logger)
|
|
13
|
+
if not m3u8_url:
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
best_qualite_url = get_best_qualite_url(m3u8_url=m3u8_url, headers=headers, logger=logger)
|
|
17
|
+
|
|
18
|
+
downloader = segment_downloader(best_qualite_url=best_qualite_url, headers_dict=headers_dict, file_name=file_name, logger=logger)
|
|
19
|
+
status = downloader.download_segments()
|
|
20
|
+
return status
|
|
21
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from ..scrap import get_mp4_url
|
|
2
|
+
from ..download import mp4_downloader
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def sendvid(url, file_name, logger):
|
|
6
|
+
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Connection": "keep-alive", "Upgrade-Insecure-Requests": "1"}
|
|
7
|
+
meta = 'og:video'
|
|
8
|
+
video_url = get_mp4_url(url, headers, meta=meta, logger=logger)
|
|
9
|
+
if not video_url:
|
|
10
|
+
return False
|
|
11
|
+
|
|
12
|
+
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36", "Referer": f"https://video.sibnet.ru"}
|
|
13
|
+
downloader = mp4_downloader(headers, video_url, file_name, logger)
|
|
14
|
+
status = downloader.download()
|
|
15
|
+
return status
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from ..scrap import get_mp4_url
|
|
2
|
+
from ..download import mp4_downloader
|
|
3
|
+
|
|
4
|
+
def sibnet(url, file_name, logger):
|
|
5
|
+
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Connection": "keep-alive", "Upgrade-Insecure-Requests": "1"}
|
|
6
|
+
match = r'player\.src\(\[\{src: "(.*?)"'
|
|
7
|
+
video_url = get_mp4_url(url, headers, match=match, logger=logger)
|
|
8
|
+
if not video_url:
|
|
9
|
+
return False
|
|
10
|
+
|
|
11
|
+
mp4_url = f"https://video.sibnet.ru{video_url}"
|
|
12
|
+
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36", "Referer": f"https://video.sibnet.ru"}
|
|
13
|
+
downloader = mp4_downloader(headers, mp4_url, file_name, logger)
|
|
14
|
+
status = downloader.download()
|
|
15
|
+
return status
|
|
16
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from ..scrap import get_m3u8_url, get_best_qualite_url
|
|
2
|
+
from ..download import segment_downloader
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def vidmoly(url, file_name, logger):
|
|
6
|
+
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',"Referer": "https://vidmoly.to/","Origin": "https://vidmoly.to"}
|
|
7
|
+
headers_dict = {
|
|
8
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',
|
|
9
|
+
'Referer': 'https://vidmoly.to/',
|
|
10
|
+
'Origin': 'https://vidmoly.to'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
match = [r'(https?://[^\s]+/master\.m3u8\?[^\s"]+)', r'(https?://[^\s]+/master\.m3u8)']
|
|
14
|
+
|
|
15
|
+
m3u8_url = get_m3u8_url(url=url, headers=headers, match= match, list_match=True, logger=logger)
|
|
16
|
+
if not m3u8_url:
|
|
17
|
+
return False
|
|
18
|
+
|
|
19
|
+
best_qualite_url = get_best_qualite_url(m3u8_url=m3u8_url, headers=headers, logger=logger)
|
|
20
|
+
|
|
21
|
+
downloader = segment_downloader(best_qualite_url=best_qualite_url, headers_dict=headers_dict,file_name=file_name, logger=logger)
|
|
22
|
+
status = downloader.download_segments()
|
|
23
|
+
return status
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
from urllib.parse import urlparse
|
|
5
|
+
import shutil
|
|
6
|
+
|
|
7
|
+
from .lecteur import *
|
|
8
|
+
|
|
9
|
+
class mp4mdl:
|
|
10
|
+
"""
|
|
11
|
+
Classe pour télécharger des vidéos depuis différents sites.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
download_path (str): Chemin où télécharger temporairement les fichiers
|
|
15
|
+
final_path (str): Chemin final où déplacer les fichiers
|
|
16
|
+
url (str): URL de la vidéo à télécharger
|
|
17
|
+
logger (logging.Logger, optional): Logger personnalisé (par défaut, "MP4mdl" avec le titre "MP4mdl-{title})
|
|
18
|
+
title (str, optional): Titre pour le logger seulement si logger est None
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
SUPPORTED_SITES = {
|
|
22
|
+
"video.sibnet.ru": (sibnet, ["url", "file_name", "logger"]),
|
|
23
|
+
"vidmoly.to": (vidmoly, ["url", "file_name", "logger"]),
|
|
24
|
+
"oneupload.to": (oneupload, ["url", "file_name", "logger"]),
|
|
25
|
+
"sendvid.com": (sendvid, ["url", "file_name", "logger"])
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def __init__(self, download_path, final_path, url, logger=None, title=None):
|
|
29
|
+
# Validation des entrées
|
|
30
|
+
if not all([download_path, final_path, url]):
|
|
31
|
+
raise ValueError("Tous les paramètres sont requis")
|
|
32
|
+
|
|
33
|
+
# Initialisation du logger
|
|
34
|
+
if title:
|
|
35
|
+
self.logger = logger or logging.getLogger(f"MP4mdl-{title}")
|
|
36
|
+
else:
|
|
37
|
+
self.logger = logger or logging.getLogger(f"MP4mdl")
|
|
38
|
+
|
|
39
|
+
# Normalisation des chemins
|
|
40
|
+
self.download_path = os.path.normpath(download_path)
|
|
41
|
+
self.final_path = os.path.normpath(final_path)
|
|
42
|
+
self.url = url
|
|
43
|
+
|
|
44
|
+
# Création du dossier temporaire
|
|
45
|
+
self._setup_temp_directory()
|
|
46
|
+
|
|
47
|
+
def download(self):
|
|
48
|
+
site = self.SUPPORTED_SITES
|
|
49
|
+
parsed_url = urlparse(self.url)
|
|
50
|
+
base_name = parsed_url.netloc
|
|
51
|
+
|
|
52
|
+
downloaded = False
|
|
53
|
+
if base_name in site:
|
|
54
|
+
# Préparer les arguments pour la fonction
|
|
55
|
+
handler, param_names = site[base_name]
|
|
56
|
+
args = {param: getattr(self, param) for param in param_names}
|
|
57
|
+
|
|
58
|
+
self.logger.debug(f"call de la function {handler.__name__} avec les arguments {args}")
|
|
59
|
+
# Appeler la fonction avec les bons arguments
|
|
60
|
+
status = handler(**args)
|
|
61
|
+
self.logger.debug(f"status: {status}")
|
|
62
|
+
if status:
|
|
63
|
+
try:
|
|
64
|
+
if os.path.exists(self.file_name):
|
|
65
|
+
shutil.move(self.file_name, self.final_path)
|
|
66
|
+
downloaded = True
|
|
67
|
+
else:
|
|
68
|
+
self.logger.error(f"Le fichier source n'existe pas: {self.file_name}")
|
|
69
|
+
downloaded = False
|
|
70
|
+
except Exception as e:
|
|
71
|
+
self.logger.error(f"Erreur lors du déplacement du fichier: {e}")
|
|
72
|
+
downloaded = False
|
|
73
|
+
else:
|
|
74
|
+
self.logger.error(f"Le site {base_name} n'est pas supporté")
|
|
75
|
+
downloaded = False
|
|
76
|
+
try:
|
|
77
|
+
if os.path.exists(self.temp_path):
|
|
78
|
+
shutil.rmtree(self.temp_path)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
self.logger.error(f"Erreur lors de la suppression du dossier temporaire: {e}")
|
|
81
|
+
return downloaded
|
|
82
|
+
|
|
83
|
+
def _setup_temp_directory(self):
|
|
84
|
+
self.temp_name = str(uuid.uuid5(uuid.NAMESPACE_URL, self.url))
|
|
85
|
+
self.temp_path = os.path.join(self.download_path, self.temp_name)
|
|
86
|
+
os.makedirs(self.temp_path, exist_ok=True)
|
|
87
|
+
self.file_name = os.path.join(self.temp_path, f"{self.temp_name}.mp4")
|
|
88
|
+
|
|
89
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from bs4 import BeautifulSoup
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_best_qualite_url(m3u8_url, headers, logger):
|
|
6
|
+
try:
|
|
7
|
+
response = requests.get(m3u8_url, headers=headers)
|
|
8
|
+
response.raise_for_status()
|
|
9
|
+
soup = BeautifulSoup(response.content, 'html.parser')
|
|
10
|
+
except Exception as e:
|
|
11
|
+
logger.error(f"Erreur lors de la récupération de l'URL m3u8: {e}")
|
|
12
|
+
return None
|
|
13
|
+
|
|
14
|
+
lines = response.text.splitlines()
|
|
15
|
+
best_url = None
|
|
16
|
+
best_resolution = (0, 0)
|
|
17
|
+
|
|
18
|
+
for i, line in enumerate(lines):
|
|
19
|
+
if line.startswith("#EXT-X-STREAM-INF"):
|
|
20
|
+
# Extraire les informations de la ligne
|
|
21
|
+
parts = line.split(",")
|
|
22
|
+
|
|
23
|
+
# Récupérer la résolution
|
|
24
|
+
resolution_str = next((part.split("=")[1] for part in parts
|
|
25
|
+
if part.startswith("RESOLUTION=")), None)
|
|
26
|
+
|
|
27
|
+
if resolution_str and i + 1 < len(lines):
|
|
28
|
+
width, height = map(int, resolution_str.split("x"))
|
|
29
|
+
|
|
30
|
+
# Mettre à jour si meilleure résolution trouvée
|
|
31
|
+
if (width, height) > best_resolution:
|
|
32
|
+
best_url = lines[i + 1]
|
|
33
|
+
|
|
34
|
+
return best_url
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from bs4 import BeautifulSoup
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
def get_m3u8_url(url, headers, match, logger, list_match=False):
|
|
6
|
+
if not match:
|
|
7
|
+
logger.error("match non definis")
|
|
8
|
+
return None
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
response = requests.get(url, headers=headers)
|
|
12
|
+
response.raise_for_status()
|
|
13
|
+
soup = BeautifulSoup(response.content, 'html.parser')
|
|
14
|
+
except Exception as e:
|
|
15
|
+
logger.error(f"Erreur lors de la récupération de l'URL m3u8: {e}")
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
if list_match:
|
|
19
|
+
for m in match:
|
|
20
|
+
m3u8_url = find_match(soup, m, logger)
|
|
21
|
+
if m3u8_url:
|
|
22
|
+
return m3u8_url
|
|
23
|
+
return None
|
|
24
|
+
else:
|
|
25
|
+
return find_match(soup, match, logger)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def find_match(soup, match, logger):
|
|
29
|
+
if match:
|
|
30
|
+
scripts = soup.find_all('script')
|
|
31
|
+
for script in scripts:
|
|
32
|
+
script_content = script.string
|
|
33
|
+
if script_content:
|
|
34
|
+
matchs = re.search(match, script_content)
|
|
35
|
+
if matchs:
|
|
36
|
+
m3u8_url = matchs.group(1)
|
|
37
|
+
if not m3u8_url:
|
|
38
|
+
logger.error("m3u8 URL non trouvée dans le script")
|
|
39
|
+
return None
|
|
40
|
+
else:
|
|
41
|
+
return m3u8_url
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import logging
|
|
3
|
+
from bs4 import BeautifulSoup
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_mp4_url(url, headers, logger, match=None, meta=None):
|
|
8
|
+
if not match and not meta:
|
|
9
|
+
logger.error("match ou meta non definis")
|
|
10
|
+
return None
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
response = requests.get(url, headers=headers)
|
|
14
|
+
response.raise_for_status()
|
|
15
|
+
soup = BeautifulSoup(response.content, 'html.parser')
|
|
16
|
+
except requests.RequestException as e:
|
|
17
|
+
logger.error(f"Erreur lors de la requête HTTP: {e}")
|
|
18
|
+
return None
|
|
19
|
+
except Exception as e:
|
|
20
|
+
logger.error(f"Erreur inattendue: {e}")
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
if match:
|
|
24
|
+
scripts = soup.find_all('script', type="text/javascript")
|
|
25
|
+
|
|
26
|
+
if not scripts:
|
|
27
|
+
logger.error("Aucun script JavaScript trouvé dans la page")
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
for script in scripts:
|
|
31
|
+
script_content = script.string
|
|
32
|
+
if script_content:
|
|
33
|
+
matchs = re.search(match, script_content)
|
|
34
|
+
if matchs:
|
|
35
|
+
video_url = matchs.group(1)
|
|
36
|
+
if not video_url:
|
|
37
|
+
logger.error("URL de la vidéo non trouvée dans le script")
|
|
38
|
+
return None
|
|
39
|
+
else:
|
|
40
|
+
return video_url
|
|
41
|
+
elif meta:
|
|
42
|
+
meta_tag = soup.find("meta", property=meta)
|
|
43
|
+
|
|
44
|
+
if not meta_tag:
|
|
45
|
+
logger.error("Aucune vidéo trouvée dans la page")
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
video_url = meta_tag['content']
|
|
49
|
+
if not video_url:
|
|
50
|
+
logger.error("URL de la vidéo non trouvée")
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
return video_url
|
|
54
|
+
else:
|
|
55
|
+
logger.error("Aucun match ou meta trouvé")
|
|
56
|
+
return None
|
|
57
|
+
|
MP4MDL-0.1.0/setup.cfg
ADDED
MP4MDL-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="MP4MDL",
|
|
5
|
+
version="0.1.0",
|
|
6
|
+
packages=find_packages(),
|
|
7
|
+
install_requires=[
|
|
8
|
+
"requests",
|
|
9
|
+
"beautifulsoup4",
|
|
10
|
+
"ffmpeg-python",
|
|
11
|
+
],
|
|
12
|
+
author="Maddox",
|
|
13
|
+
author_email="maddoxtes@gmail.com",
|
|
14
|
+
description="Un outil pour télécharger et gérer des vidéos MP4",
|
|
15
|
+
long_description=open("readme.md").read(),
|
|
16
|
+
long_description_content_type="text/markdown",
|
|
17
|
+
url="https://git.maddoxserv.com/maddox/MP4MDL",
|
|
18
|
+
classifiers=[
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
],
|
|
23
|
+
python_requires=">=3.11",
|
|
24
|
+
entry_points={
|
|
25
|
+
"console_scripts": [
|
|
26
|
+
"mp4mdl=mp4mdl.main:main",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
)
|