This commit is contained in:
尹舟 2025-03-16 14:26:18 +08:00
parent 813f1488e0
commit 8955853a68
24 changed files with 179 additions and 54 deletions

View File

@ -1,8 +1,8 @@
import m3u8_To_MP4
import m3u8_to_mp4
from utils.MySqlUtil import MySqlUtil
from apscheduler.schedulers.blocking import BlockingScheduler
import time
from utils.log import Log
from utils.Log import Log
from pathlib import Path
@ -30,7 +30,7 @@ def download_m3u8(download_path='./mp4/'):
log.info(f"任务下载中,正在下载 {name}...")
# 下载 m3u8 文件并转换为 MP4
m3u8_To_MP4.multithread_download(url, file_path=str(file_path))
m3u8_to_mp4.multithread_download(url, file_path=str(file_path))
log.info(f"成功下载并转换 {name} to {file_path}.")
except Exception as e:

View File

@ -15,7 +15,7 @@ m3u8_to_mp4.download("https://xxx.com/xxx/index.m3u8")
import logging
import subprocess
from m3u8_To_MP4.helpers import printer_helper
from m3u8_to_mp4.helpers import printer_helper
printer_helper.config_logging()
@ -34,11 +34,11 @@ def verify_ffmpey():
# define API
import m3u8_To_MP4.multithreads_processor
from m3u8_To_MP4.v2_async_processor import AsynchronousFileCrawler
from m3u8_To_MP4.v2_async_processor import AsynchronousUriCrawler
from m3u8_To_MP4.v2_multithreads_processor import MultiThreadsFileCrawler
from m3u8_To_MP4.v2_multithreads_processor import MultiThreadsUriCrawler
import m3u8_to_mp4.multithreads_processor
from m3u8_to_mp4.v2_async_processor import AsynchronousFileCrawler
from m3u8_to_mp4.v2_async_processor import AsynchronousUriCrawler
from m3u8_to_mp4.v2_multithreads_processor import MultiThreadsFileCrawler
from m3u8_to_mp4.v2_multithreads_processor import MultiThreadsUriCrawler
__all__ = (
"MultiThreadsFileCrawler",
@ -56,7 +56,7 @@ __all__ = (
# ================ Async ===================
def async_download(m3u8_uri, file_path='./m3u8_To_MP4.ts', customized_http_header=None, max_retry_times=3,
def async_download(m3u8_uri, file_path='./m3u8_to_mp4.ts', customized_http_header=None, max_retry_times=3,
num_concurrent=50, tmpdir=None):
'''
Download mp4 video from given m3u uri.
@ -69,7 +69,7 @@ def async_download(m3u8_uri, file_path='./m3u8_To_MP4.ts', customized_http_heade
:return:
'''
with m3u8_To_MP4.v2_async_processor.AsynchronousUriCrawler(m3u8_uri,
with m3u8_to_mp4.v2_async_processor.AsynchronousUriCrawler(m3u8_uri,
file_path,
customized_http_header,
max_retry_times,
@ -78,9 +78,9 @@ def async_download(m3u8_uri, file_path='./m3u8_To_MP4.ts', customized_http_heade
crawler.fetch_mp4_by_m3u8_uri('ts')
def async_uri_download(m3u8_uri, file_path='./m3u8_To_MP4.mp4', customized_http_header=None,
def async_uri_download(m3u8_uri, file_path='./m3u8_to_mp4.mp4', customized_http_header=None,
max_retry_times=3, num_concurrent=50, tmpdir=None):
with m3u8_To_MP4.v2_async_processor.AsynchronousUriCrawler(m3u8_uri,
with m3u8_to_mp4.v2_async_processor.AsynchronousUriCrawler(m3u8_uri,
file_path,
customized_http_header,
max_retry_times,
@ -89,9 +89,9 @@ def async_uri_download(m3u8_uri, file_path='./m3u8_To_MP4.mp4', customized_http_
crawler.fetch_mp4_by_m3u8_uri('ts')
def async_file_download(m3u8_uri, m3u8_file_path, file_path='./m3u8_To_MP4.ts', customized_http_header=None,
def async_file_download(m3u8_uri, m3u8_file_path, file_path='./m3u8_to_mp4.ts', customized_http_header=None,
max_retry_times=3, num_concurrent=50, tmpdir=None):
with m3u8_To_MP4.v2_async_processor.AsynchronousFileCrawler(m3u8_uri,
with m3u8_to_mp4.v2_async_processor.AsynchronousFileCrawler(m3u8_uri,
m3u8_file_path,
file_path,
customized_http_header,
@ -102,7 +102,7 @@ def async_file_download(m3u8_uri, m3u8_file_path, file_path='./m3u8_To_MP4.ts',
# ================ MultiThread ===================
def multithread_download(m3u8_uri, file_path='./m3u8_To_MP4.ts', customized_http_header=None,
def multithread_download(m3u8_uri, file_path='./m3u8_to_mp4.ts', customized_http_header=None,
max_retry_times=3, max_num_workers=100, tmpdir=None):
'''
Download mp4 video from given m3u uri.
@ -114,7 +114,7 @@ def multithread_download(m3u8_uri, file_path='./m3u8_To_MP4.ts', customized_http
:param mp4_file_name: a mp4 file name with suffix ".mp4"
:return:
'''
with m3u8_To_MP4.v2_multithreads_processor.MultiThreadsUriCrawler(m3u8_uri,
with m3u8_to_mp4.v2_multithreads_processor.MultiThreadsUriCrawler(m3u8_uri,
file_path,
customized_http_header,
max_retry_times,
@ -123,9 +123,9 @@ def multithread_download(m3u8_uri, file_path='./m3u8_To_MP4.ts', customized_http
crawler.fetch_mp4_by_m3u8_uri('ts')
def multithread_uri_download(m3u8_uri, file_path='./m3u8_To_MP4.ts', customied_http_header=None,
def multithread_uri_download(m3u8_uri, file_path='./m3u8_to_mp4.ts', customied_http_header=None,
max_retry_times=3, max_num_workers=100, tmpdir=None):
with m3u8_To_MP4.v2_multithreads_processor.MultiThreadsUriCrawler(m3u8_uri,
with m3u8_to_mp4.v2_multithreads_processor.MultiThreadsUriCrawler(m3u8_uri,
file_path,
customied_http_header,
max_retry_times,
@ -137,7 +137,7 @@ def multithread_uri_download(m3u8_uri, file_path='./m3u8_To_MP4.ts', customied_h
def multithread_file_download(m3u8_uri, m3u8_file_path, file_path,
customized_http_header=None, max_retry_times=3,
max_num_workers=100, tmpdir=None):
with m3u8_To_MP4.v2_multithreads_processor.MultiThreadsFileCrawler(
with m3u8_to_mp4.v2_multithreads_processor.MultiThreadsFileCrawler(
m3u8_uri, m3u8_file_path, file_path, customized_http_header, max_retry_times,
max_num_workers, tmpdir) as crawler:
crawler.fetch_mp4_by_m3u8_uri(True)
@ -148,7 +148,7 @@ import warnings
def download(m3u8_uri, max_retry_times=3, max_num_workers=100,
mp4_file_dir='./', mp4_file_name='m3u8_To_MP4', tmpdir=None):
mp4_file_dir='./', mp4_file_name='m3u8_to_mp4', tmpdir=None):
'''
Download mp4 video from given m3u uri.
@ -163,7 +163,7 @@ def download(m3u8_uri, max_retry_times=3, max_num_workers=100,
'download function is deprecated, and please use multithread_download.',
DeprecationWarning)
with m3u8_To_MP4.multithreads_processor.Crawler(m3u8_uri, max_retry_times,
with m3u8_to_mp4.multithreads_processor.Crawler(m3u8_uri, max_retry_times,
max_num_workers,
mp4_file_dir,
mp4_file_name,

View File

@ -11,11 +11,11 @@ import zlib
import m3u8
from m3u8_To_MP4.helpers import path_helper
from m3u8_To_MP4.helpers import printer_helper
from m3u8_To_MP4.networks.asynchronous import async_producer_consumer
from m3u8_To_MP4.networks.synchronous import sync_DNS
from m3u8_To_MP4.networks.synchronous import sync_http
from m3u8_to_mp4.helpers import path_helper
from m3u8_to_mp4.helpers import printer_helper
from m3u8_to_mp4.networks.asynchronous import async_producer_consumer
from m3u8_to_mp4.networks.synchronous import sync_DNS
from m3u8_to_mp4.networks.synchronous import sync_http
printer_helper.config_logging()

View File

@ -32,7 +32,7 @@ def random_5_char():
def random_name():
dt_str = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
return 'm3u8_To_MP4' + dt_str + random_5_char()+'.mp4'
return 'm3u8_to_mp4' + dt_str + random_5_char()+'.mp4'
def calibrate_name(name):

View File

@ -12,9 +12,9 @@ import time
import m3u8
from Crypto.Cipher import AES
from m3u8_To_MP4.helpers import path_helper
from m3u8_To_MP4.helpers import printer_helper
from m3u8_To_MP4.networks.synchronous.sync_http_requester import request_for
from m3u8_to_mp4.helpers import path_helper
from m3u8_to_mp4.helpers import printer_helper
from m3u8_to_mp4.networks.synchronous.sync_http_requester import request_for
printer_helper.config_logging()

View File

@ -3,7 +3,7 @@ import asyncio
import socket
import urllib.parse
from m3u8_To_MP4.networks.http_base import AddressInfo
from m3u8_to_mp4.networks.http_base import AddressInfo
async def available_addr_infos_of_url(url):

View File

@ -6,8 +6,8 @@ import urllib.parse
import urllib.request
import urllib.response
from m3u8_To_MP4.helpers import path_helper
from m3u8_To_MP4.networks import http_base
from m3u8_to_mp4.helpers import path_helper
from m3u8_to_mp4.networks import http_base
def http_get_header(domain_name, port, resource_path_at_server, is_keep_alive):

View File

@ -12,10 +12,10 @@ from multiprocessing import JoinableQueue, Process
from Crypto.Cipher import AES
from m3u8_To_MP4.helpers import path_helper
from m3u8_To_MP4.helpers import printer_helper
from m3u8_To_MP4.networks import http_base
from m3u8_To_MP4.networks.asynchronous import async_http
from m3u8_to_mp4.helpers import path_helper
from m3u8_to_mp4.helpers import printer_helper
from m3u8_to_mp4.networks import http_base
from m3u8_to_mp4.networks.asynchronous import async_http
async def ts_request(concurrent_condition, ssl_context, addr_info,

View File

@ -3,7 +3,7 @@ import re
import socket
import urllib.parse
from m3u8_To_MP4.networks.http_base import AddressInfo
from m3u8_to_mp4.networks.http_base import AddressInfo
def available_addr_infos_of_url(url):

View File

@ -4,8 +4,8 @@ import urllib.parse
import urllib.request
import urllib.response
from m3u8_To_MP4.helpers import path_helper
from m3u8_To_MP4.networks import http_base
from m3u8_to_mp4.helpers import path_helper
from m3u8_to_mp4.networks import http_base
def http_get_header(domain_name, port, resource_path_at_server, is_keep_alive,

View File

@ -9,10 +9,10 @@ import time
import warnings
import zlib
from m3u8_To_MP4.helpers import path_helper
from m3u8_To_MP4.helpers import printer_helper
from m3u8_To_MP4.helpers.os_helper import get_core_count
from m3u8_To_MP4.networks.synchronous import sync_DNS
from m3u8_to_mp4.helpers import path_helper
from m3u8_to_mp4.helpers import printer_helper
from m3u8_to_mp4.helpers.os_helper import get_core_count
from m3u8_to_mp4.networks.synchronous import sync_DNS
printer_helper.config_logging()
@ -20,7 +20,7 @@ printer_helper.config_logging()
class AbstractCrawler(object):
def __init__(self,
m3u8_uri,
file_path='./m3u8_To_MP4.mp4',
file_path='./m3u8_to_mp4.mp4',
customized_http_header=None,
max_retry_times=3,
num_concurrent=50,

View File

@ -5,8 +5,8 @@ import os.path
import m3u8
from m3u8_To_MP4.networks.synchronous import sync_http
from m3u8_To_MP4.v2_abstract_crawler_processor import AbstractCrawler
from m3u8_to_mp4.networks.synchronous import sync_http
from m3u8_to_mp4.v2_abstract_crawler_processor import AbstractCrawler
EncryptedKey = collections.namedtuple(typename='EncryptedKey',
field_names=['method', 'value', 'iv'])

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from m3u8_To_MP4 import v2_abstract_task_processor
from m3u8_To_MP4.networks.asynchronous import async_producer_consumer
from m3u8_to_mp4 import v2_abstract_task_processor
from m3u8_to_mp4.networks.asynchronous import async_producer_consumer
class AsynchronousFileCrawler(v2_abstract_task_processor.AbstractFileCrawler):

View File

@ -6,10 +6,10 @@ import sys
from Crypto.Cipher import AES
from m3u8_To_MP4 import v2_abstract_task_processor
from m3u8_To_MP4.helpers import path_helper
from m3u8_To_MP4.helpers import printer_helper
from m3u8_To_MP4.networks.synchronous.sync_http_requester import request_for
from m3u8_to_mp4 import v2_abstract_task_processor
from m3u8_to_mp4.helpers import path_helper
from m3u8_to_mp4.helpers import printer_helper
from m3u8_to_mp4.networks.synchronous.sync_http_requester import request_for
def download_segment(segment_url, customized_http_header):

125
utils/SQLiteDB.py Normal file
View File

@ -0,0 +1,125 @@
import sqlite3
from typing import Optional, List, Tuple, Any
class SQLiteDB:
def __init__(self, db_path: str = "./sqlite/movies.db"):
self.db_path = db_path
self.conn: Optional[sqlite3.Connection] = None
self.cursor: Optional[sqlite3.Cursor] = None
self._init_db()
def _init_db(self) -> None:
"""初始化数据库并创建表基于网页4、网页5、网页7的表结构设计[4,5,7](@ref)"""
create_table_sql = """
CREATE TABLE IF NOT EXISTS movie (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
path TEXT NOT NULL,
is_download INTEGER DEFAULT 0
)
"""
self.connect()
if self.cursor:
self.cursor.execute(create_table_sql)
self.conn.commit()
def connect(self) -> None:
"""建立数据库连接基于网页2、网页5的连接方式[2,5](@ref)"""
try:
self.conn = sqlite3.connect(self.db_path)
self.cursor = self.ursor.cursor()
self.conn.execute("PRAGMA foreign_keys = ON") # 启用外键约束
except sqlite3.Error as e:
raise ConnectionError(f"数据库连接失败: {e}")
def close(self) -> None:
"""安全关闭连接参考网页6的资源管理[6](@ref)"""
if self.cursor:
self.cursor.close()
if self.conn:
self.conn.close()
def execute(
self,
sql: str,
params: Optional[Tuple[Any, ...]] = None,
commit: bool = True
) -> int:
"""
执行单条SQL语句集成网页1网页7的参数化查询[1,7](@ref)
:param sql: 含占位符的SQL语句
:param params: 参数元组防SQL注入
:param commit: 是否自动提交事务
:return: 影响的行数
"""
try:
if params:
self.cursor.execute(sql, params)
else:
self.cursor.execute(sql)
if commit and self.conn:
self.conn.commit()
return self.cursor.rowcount
except sqlite3.Error as e:
if self.conn:
self.conn.rollback()
raise RuntimeError(f"SQL执行失败: {e}")
# ---- 高级操作方法 ----
def insert_movie(self, name: str, path: str) -> int:
"""插入电影记录(演示特定业务方法)"""
return self.execute(
"INSERT INTO movie (name, path) VALUES (?, ?)",
(name, path)
)
def mark_downloaded(self, movie_id: int) -> int:
"""标记电影为已下载(业务逻辑示例)[6](@ref)"""
return self.execute(
"UPDATE movie SET is_download = 1 WHERE id = ?",
(movie_id,)
)
def get_undownloaded(self) -> List[Tuple]:
"""查询未下载的电影(返回完整记录元组)[4,6](@ref)"""
self.execute("SELECT * FROM movie WHERE is_download = 0", commit=False)
return self.cursor.fetchall()
# ---- 上下文管理器支持 ----
def __enter__(self):
self.connect()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
# ==================== 使用示例 ====================
if __name__ == "__main__":
# 示例1自动建表并插入数据
with SQLiteDB() as db:
# 批量插入演示集成网页5的executemany方法[5](@ref)
movies = [("泰坦尼克号", "/movies/titanic"), ("阿凡达", "/movies/avatar")]
db.executemany(
"INSERT INTO movie (name, path) VALUES (?, ?)",
movies
)
# 查询未下载记录基于网页6的查询模式[6](@ref)
print("待下载电影:", db.get_undownloaded())
#
# # 示例2更新操作
# with SQLiteDB() as db:
# db.mark_downloaded(1)
# print("更新后的记录:", db.get_undownloaded())
#
# # 示例3事务回滚演示
# try:
# with SQLiteDB() as db:
# db.execute("BEGIN TRANSACTION", commit=False)
# db.insert_movie("黑客帝国", "/movies/matrix")
# raise RuntimeError("模拟业务异常") # 触发回滚
# db.execute("COMMIT", commit=True)
# except Exception as e:
# print(f"事务回滚成功: {e}")