Merge pull request #51 from arabcoders/dev

Restructured the python code.
This commit is contained in:
Abdulmohsen 2023-12-31 23:06:16 +03:00 committed by GitHub
commit 65e9f316c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 76 additions and 76 deletions

View file

@ -4,6 +4,7 @@ import os
import re import re
import sys import sys
import coloredlogs import coloredlogs
from version import APP_VERSION
class Config: class Config:
@ -33,7 +34,10 @@ class Config:
logging_level: str = 'info' logging_level: str = 'info'
_boolean_vars: tuple = ('keep_archive', 'ytdl_debug') version: str = APP_VERSION
_boolean_vars: tuple = ('keep_archive', 'ytdl_debug',)
_immutable: tuple = ('version',)
def __init__(self): def __init__(self):
baseDefualtPath: str = os.path.dirname(os.path.dirname(__file__)) baseDefualtPath: str = os.path.dirname(os.path.dirname(__file__))
@ -46,6 +50,11 @@ class Config:
if k.startswith('_'): if k.startswith('_'):
continue continue
# If the variable is not updateable, set it to the default value.
if k in self._immutable:
setattr(self, k, v)
continue
lookUpKey: str = f'YTP_{k}'.upper() lookUpKey: str = f'YTP_{k}'.upper()
setattr( setattr(
self, k, self, k,
@ -53,7 +62,7 @@ class Config:
) )
for k, v in self.__dict__.items(): for k, v in self.__dict__.items():
if k.startswith('_'): if k.startswith('_') or k in self._immutable:
continue continue
if isinstance(v, str) and '{' in v and '}' in v: if isinstance(v, str) and '{' in v and '}' in v:

View file

@ -4,10 +4,10 @@ from datetime import datetime, timezone
from email.utils import formatdate from email.utils import formatdate
import json import json
import sqlite3 import sqlite3
from src.Utils import calcDownloadPath from Utils import calcDownloadPath
from src.Config import Config from Config import Config
from src.Download import Download from Download import Download
from src.DTO.ItemDTO import ItemDTO from ItemDTO import ItemDTO
class DataStore: class DataStore:

View file

@ -7,9 +7,8 @@ import os
import re import re
import shutil import shutil
import yt_dlp import yt_dlp
from src.Utils import get_format, get_opts, jsonCookie, mergeConfig from Utils import Notifier, get_format, get_opts, jsonCookie, mergeConfig
from src.DTO.ItemDTO import ItemDTO from ItemDTO import ItemDTO
from src.Notifier import Notifier
log = logging.getLogger('download') log = logging.getLogger('download')

View file

@ -5,12 +5,11 @@ import os
import time import time
import yt_dlp import yt_dlp
from sqlite3 import Connection from sqlite3 import Connection
from src.Config import Config from Config import Config
from src.Notifier import Notifier from Download import Download
from src.Download import Download from ItemDTO import ItemDTO
from src.DTO.ItemDTO import ItemDTO from DataStore import DataStore
from src.DataStore import DataStore from Utils import Notifier, ObjectSerializer, calcDownloadPath, ExtractInfo, mergeConfig
from src.Utils import ObjectSerializer, calcDownloadPath, ExtractInfo, mergeConfig
from datetime import datetime, timezone from datetime import datetime, timezone
log = logging.getLogger('DownloadQueue') log = logging.getLogger('DownloadQueue')

View file

@ -5,6 +5,7 @@ import logging
import os import os
from typing import Any from typing import Any
import yt_dlp import yt_dlp
from socketio import AsyncServer
log = logging.getLogger('Utils') log = logging.getLogger('Utils')
@ -274,3 +275,31 @@ class ObjectSerializer(json.JSONEncoder):
def default(self, obj): def default(self, obj):
return obj.__dict__ if isinstance(obj, object) else json.JSONEncoder.default(self, obj) return obj.__dict__ if isinstance(obj, object) else json.JSONEncoder.default(self, obj)
class Notifier:
'''
This class is used to send events to the frontend.
'''
def __init__(self, sio: AsyncServer, serializer: ObjectSerializer):
self.sio = sio
self.serializer = serializer
async def added(self, dl):
await self.emit('added', dl)
async def updated(self, dl):
await self.emit('updated', dl)
async def completed(self, dl):
await self.emit('completed', dl)
async def canceled(self, id):
await self.emit('canceled', id)
async def cleared(self, id):
await self.emit('cleared', id)
async def emit(self, event: str, data):
await self.sio.emit(event, self.serializer.encode(data))

View file

@ -5,24 +5,19 @@ from datetime import datetime
import json import json
import os import os
import random import random
from src.Config import Config from Config import Config
from version import APP_VERSION from DownloadQueue import DownloadQueue
from src.Notifier import Notifier from Utils import ObjectSerializer, Notifier
from src.DownloadQueue import DownloadQueue
from src.Utils import ObjectSerializer
from aiohttp import web from aiohttp import web
from aiohttp.web import Request, Response from aiohttp.web import Request, Response
from src.M3u8 import M3u8 from player.M3u8 import M3u8
from src.Segments import Segments from player.Segments import Segments
import socketio import socketio
import logging import logging
import caribou import caribou
import sqlite3 import sqlite3
from aiocron import crontab from aiocron import crontab
log: logging.Logger = None
class Main: class Main:
config: Config = None config: Config = None
serializer: ObjectSerializer = None serializer: ObjectSerializer = None
@ -36,7 +31,6 @@ class Main:
def __init__(self): def __init__(self):
self.config = Config() self.config = Config()
self.config.version = APP_VERSION
self.logger = logging.getLogger('main') self.logger = logging.getLogger('main')
try: try:
@ -113,6 +107,7 @@ class Main:
) )
async def connect(self, sid, _): async def connect(self, sid, _):
self.logger.info(f'Config [{self.config.__dict__}].')
data: dict = { data: dict = {
**self.dqueue.get(), **self.dqueue.get(),
"config": self.config, "config": self.config,
@ -285,7 +280,10 @@ class Main:
raise web.HTTPBadRequest(reason='file is required') raise web.HTTPBadRequest(reason='file is required')
return web.Response( return web.Response(
text=M3u8(self.config).make_stream(file), text=M3u8(url=f"{self.config.url_host}{self.config.url_prefix}").make_stream(
download_path=self.config.download_path,
file=file
),
headers={ headers={
'Content-Type': 'application/x-mpegURL', 'Content-Type': 'application/x-mpegURL',
'Cache-Control': 'no-cache', 'Cache-Control': 'no-cache',
@ -308,7 +306,6 @@ class Main:
raise web.HTTPBadRequest(reason='segment is required') raise web.HTTPBadRequest(reason='segment is required')
segmenter = Segments( segmenter = Segments(
config=self.config,
segment_index=int(segment), segment_index=int(segment),
segment_duration=float('{:.6f}'.format( segment_duration=float('{:.6f}'.format(
float(sd if sd else M3u8.segment_duration))), float(sd if sd else M3u8.segment_duration))),
@ -317,7 +314,7 @@ class Main:
) )
return web.Response( return web.Response(
body=await segmenter.stream(file), body=await segmenter.stream(download_path=self.config.download_path, file=file),
headers={ headers={
'Content-Type': 'video/mpegts', 'Content-Type': 'video/mpegts',
'Connection': 'keep-alive', 'Connection': 'keep-alive',

View file

@ -1,9 +1,8 @@
import math import math
import os import os
from urllib.parse import quote from urllib.parse import quote
from src.Utils import calcDownloadPath from Utils import calcDownloadPath
from src.ffprobe import FFProbe from .ffprobe import FFProbe
from src.Config import Config
class M3u8: class M3u8:
@ -11,17 +10,15 @@ class M3u8:
ok_acodecs: tuple = ('aac', 'mp3',) ok_acodecs: tuple = ('aac', 'mp3',)
segment_duration: float = 10.000000 segment_duration: float = 10.000000
config: Config = None url: str = None
download_path: str = None
def __init__(self, config: Config, segment_duration: float = 6.000000): def __init__(self, url: str, segment_duration: float = 6.000000):
self.config = config self.url = url
self.download_path = self.config.download_path
self.segment_duration = float(segment_duration) self.segment_duration = float(segment_duration)
def make_stream(self, file: str): def make_stream(self, download_path: str, file: str):
realFile: str = calcDownloadPath( realFile: str = calcDownloadPath(
basePath=self.download_path, basePath=download_path,
folder=file, folder=file,
createPath=False createPath=False
) )
@ -68,7 +65,7 @@ class M3u8:
else: else:
m3u8 += f"#EXTINF:{segmentSize}, nodesc\n" m3u8 += f"#EXTINF:{segmentSize}, nodesc\n"
m3u8 += f"{self.config.url_host}{self.config.url_prefix}segments/{i}/{quote(file)}" m3u8 += f"{self.url}segments/{i}/{quote(file)}"
if len(segmentParams) > 0: if len(segmentParams) > 0:
m3u8 += '?'+'&'.join([f"{key}={value}" for key, m3u8 += '?'+'&'.join([f"{key}={value}" for key,
value in segmentParams.items()]) value in segmentParams.items()])

View file

@ -3,30 +3,27 @@ import logging
import os import os
import subprocess import subprocess
import tempfile import tempfile
from src.Utils import calcDownloadPath from Utils import calcDownloadPath
from src.Config import Config from Config import Config
log = logging.getLogger('segments') log = logging.getLogger('segments')
class Segments: class Segments:
config: Config = None
download_path: str = None
segment_duration: int segment_duration: int
segment_index: int segment_index: int
vconvert: bool vconvert: bool
aconvert: bool aconvert: bool
def __init__(self, config: Config, segment_index: int, segment_duration: float, vconvert: bool, aconvert: bool): def __init__(self, segment_index: int, segment_duration: float, vconvert: bool, aconvert: bool):
self.config = config
self.download_path = self.config.download_path
self.segment_duration = float(segment_duration) self.segment_duration = float(segment_duration)
self.segment_index = int(segment_index) self.segment_index = int(segment_index)
self.vconvert = bool(vconvert) self.vconvert = bool(vconvert)
self.aconvert = bool(aconvert) self.aconvert = bool(aconvert)
async def stream(self, file: str) -> bytes: async def stream(self, download_path: str, file: str) -> bytes:
realFile: str = calcDownloadPath( realFile: str = calcDownloadPath(
basePath=self.download_path, basePath=download_path,
folder=file, folder=file,
createPath=False createPath=False
) )

View file

@ -6,7 +6,6 @@ import json
import operator import operator
import os import os
import pipes import pipes
import re
import subprocess import subprocess

View file

@ -1,26 +0,0 @@
from socketio import AsyncServer
from src.Utils import ObjectSerializer
class Notifier:
def __init__(self, sio: AsyncServer, serializer: ObjectSerializer):
self.sio = sio
self.serializer = serializer
async def added(self, dl):
await self.emit('added', dl)
async def updated(self, dl):
await self.emit('updated', dl)
async def completed(self, dl):
await self.emit('completed', dl)
async def canceled(self, id):
await self.emit('canceled', id)
async def cleared(self, id):
await self.emit('cleared', id)
async def emit(self, event: str, data):
await self.sio.emit(event, self.serializer.encode(data))