55import time
66import hmac
77import hashlib
8- from typing import Optional
98from base_api import BaseCore
9+ from bs4 import BeautifulSoup
1010from urllib .parse import quote
1111from functools import cached_property
12- from base_api .base import setup_logger
12+ from typing import Optional , Generator , List
13+ from base_api .base import setup_logger , Helper
1314from base_api .modules .config import RuntimeConfig
1415from base_api .modules .progress_bars import Callback
1516from concurrent .futures import ThreadPoolExecutor , as_completed
@@ -67,46 +68,86 @@ def __getattr__(self, _):
6768 raise self ._err
6869
6970
70- class Helper :
71- def __init__ (self , core : BaseCore ):
72- super (Helper ).__init__ ()
73- self .core = core
74-
75- def _get_video (self , url : str ):
76- return Video (url , core = self .core )
77-
78- def _make_video_safe (self , url : str ):
79- try :
80- return Video (url , core = self .core )
81- except Exception as e :
82- return ErrorVideo (url , e )
83-
84-
8571class Video :
8672 def __init__ (self , url : str , core : Optional [BaseCore ] = None ) -> None :
8773 self .url = url
8874 self .core = core
8975 self .core .enable_logging (level = logging .DEBUG )
9076 self .logger = setup_logger (name = "MISSAV API - [Video]" , log_file = None , level = logging .CRITICAL )
9177 self .content = self .core .fetch (url )
78+ print (self .content )
79+ self .soup = BeautifulSoup (self .content , "lxml" )
80+ _meta_div = self .soup .find ("div" , class_ = "space-y-2" )
81+ self .meta_divs = _meta_div .find_all ("div" , class_ = "text-secondary" )
9282
9383 def enable_logging (self , level , log_file : str = None ):
9484 self .logger = setup_logger (name = "MISSAV API - [Video]" , log_file = log_file , level = level )
9585
9686 @cached_property
9787 def title (self ) -> str :
9888 """Returns the title of the video. Language depends on the URL language"""
99- return regex_title .search (self .content ).group (1 )
89+ return self .soup .find ("h1" , class_ = "text-base lg:text-lg text-nord6" ).text .strip ()
90+
91+ @cached_property
92+ def publish_date (self ) -> str :
93+ """Returns the publish date of the video"""
94+ return self .meta_divs [0 ].find ("time" , class_ = "font-medium" ).text .strip ()
10095
10196 @cached_property
10297 def video_code (self ) -> str :
10398 """Returns the specific video code"""
104- return regex_video_code . search ( self .content ). group ( 1 )
99+ return self .meta_divs [ 1 ]. find ( "span" , class_ = "font-medium" ). text . strip ( )
105100
106101 @cached_property
107- def publish_date (self ) -> str :
108- """Returns the publication date of the video"""
109- return regex_publish_date .search (self .content ).group (1 )
102+ def title_original_japanese (self ) -> str :
103+ """Returns the original title of the video"""
104+ try :
105+ return self .meta_divs [2 ].find ("span" , class_ = "font-medium" ).text .strip ()
106+
107+ except IndexError :
108+ return ""
109+
110+ @cached_property
111+ def genres (self ) -> List [str ]:
112+ """Returns the genres of the video"""
113+ try :
114+ genres = []
115+ a_tags = self .meta_divs [3 ].find_all ("a" )
116+ for a_tag in a_tags :
117+ genres .append (a_tag .text .strip ())
118+
119+ return a_tags
120+
121+ except IndexError :
122+ return []
123+
124+
125+ @cached_property
126+ def series (self ) -> str :
127+ """Returns the series of the video"""
128+ try :
129+ return self .meta_divs [4 ].find ("a" ).text .strip ()
130+
131+ except IndexError :
132+ return ""
133+
134+ @cached_property
135+ def manufacturer (self ) -> str :
136+ """Returns the manufacturer of the video"""
137+ try :
138+ return self .meta_divs [5 ].find ("a" ).text .strip ()
139+
140+ except IndexError :
141+ return ""
142+
143+ @cached_property
144+ def etiquette (self ) -> str :
145+ """Returns the etiquette of the video"""
146+ try :
147+ return self .meta_divs [6 ].find ("a" ).text .strip ()
148+
149+ except IndexError :
150+ return ""
110151
111152 @cached_property
112153 def m3u8_base_url (self ) -> str :
@@ -147,16 +188,17 @@ def download(self, quality: str, downloader: str, path: str = "./", no_title=Fal
147188
148189class Client (Helper ):
149190 def __init__ (self , core : Optional [BaseCore ] = None ):
150- super (Client , self ).__init__ (core = core )
191+ super (Client , self ).__init__ (core = core , video = Video )
151192 self .core = core or BaseCore (config = RuntimeConfig ())
152- self .core .initialize_session (headers )
193+ self .core .config .use_http2 = False # Missav doesn't support http2
194+ self .core .initialize_session ()
195+ self .core .session .headers .update (headers )
153196
154197 def get_video (self , url : str ) -> Video :
155198 """Returns the video object"""
156199 return Video (url , core = self .core )
157200
158-
159- def search (self , query : str , video_count : int = 50 , max_workers : int = 20 ):
201+ def search (self , query : str , video_count : int = 50 , max_workers : int = 20 ) -> Generator [Video , None , None ]:
160202 """
161203 Mirrors: POST /search/users/{userId}/items/
162204 Body fields follow the snippet’s Recombee client (searchQuery, count, scenario, filter, booster, logic, etc.)
@@ -183,3 +225,7 @@ def search(self, query: str, video_count: int = 50, max_workers: int = 20):
183225 futures = [executor .submit (self ._make_video_safe , url ) for url in video_urls ]
184226 for fut in as_completed (futures ):
185227 yield fut .result ()
228+
229+ client = Client ()
230+ video = client .get_video ("https://missav.ws/dm13/de/ppp-2165" )
231+ print (video .title )
0 commit comments