@@ -1898,51 +1898,93 @@ def _safe_get(endpoint):
18981898 if found_match :
18991899 break
19001900
1901- # === FASE 3: FDSearch multipart para a página que contém o employeeNo ===
1902- if not found_match :
1903- _log ("Registro não encontrado via FDSearch paginado" )
1904- else :
1905- _log ("Tentativa 3: FDSearch multipart na página do registro" )
1906- # Refazer a busca SEM ?format=json (retorna JPEG embutido)
1907- page_start = max (0 , pos - 30 )
1908- payload = json .dumps ({
1909- "faceLibType" : fd_type , "FDID" : fdid ,
1910- "searchResultPosition" : page_start , "maxResults" : 30
1901+ # === FASE 3: FDSearch multipart FILTRADO por employeeNo ===
1902+ # Sem ?format=json, o terminal retorna o JPEG embutido no multipart
1903+ _log ("Tentativa 3: FDSearch multipart filtrado por employeeNo" )
1904+ for fdid , fd_type in fd_configs :
1905+ # 3a: Busca com filtro faceCustomCondition (terminais mais novos)
1906+ for search_payload in [
1907+ json .dumps ({
1908+ "faceLibType" : fd_type , "FDID" : fdid ,
1909+ "searchResultPosition" : 0 , "maxResults" : 1 ,
1910+ "faceCustomCondition" : {
1911+ "searchType" : "byEmployeeNo" ,
1912+ "EmployeeNoList" : [{"employeeNo" : employee_no }]
1913+ }
1914+ }),
1915+ # 3b: Busca com FPID direto (terminais mais antigos)
1916+ json .dumps ({
1917+ "faceLibType" : fd_type , "FDID" : fdid , "FPID" : employee_no ,
1918+ "searchResultPosition" : 0 , "maxResults" : 1
1919+ }),
1920+ ]:
1921+ resp = _safe_post ("/ISAPI/Intelligent/FDLib/FDSearch" , search_payload )
1922+ status = resp .get ("status_code" , 0 )
1923+ data = resp .get ("data" , b"" )
1924+ data_len = len (data ) if isinstance (data , bytes ) else 0
1925+ if status == 200 and data_len > 500 :
1926+ _log (f" → Multipart data: { data_len } bytes" )
1927+ img = _extract_jpeg (data )
1928+ if img and len (img ) > 1024 :
1929+ _log (f" → JPEG encontrado! { len (img )// 1024 } KB" )
1930+ return img
1931+ _log (f" → Sem JPEG no multipart (data tem { data_len } B)" )
1932+ elif status == 400 :
1933+ continue # tipo errado, tentar próximo
1934+ elif status != 200 :
1935+ _log (f" → status={ status } " )
1936+
1937+ # 3c: Busca pela página que contém o registro (fallback amplo)
1938+ if found_match :
1939+ page_start = max (0 , pos - 30 )
1940+ payload = json .dumps ({
1941+ "faceLibType" : fd_type , "FDID" : fdid ,
1942+ "searchResultPosition" : page_start , "maxResults" : 30
1943+ })
1944+ resp = _safe_post ("/ISAPI/Intelligent/FDLib/FDSearch" , payload )
1945+ if resp .get ("ok" ) and resp .get ("data" ):
1946+ data = resp ["data" ]
1947+ if len (data ) > 2000 :
1948+ _log (f" → Página multipart: { len (data )} bytes" )
1949+ idx = 0
1950+ jpeg_list = []
1951+ while True :
1952+ start = data .find (b'\xff \xd8 \xff ' , idx )
1953+ if start < 0 :
1954+ break
1955+ end = data .find (b'\xff \xd9 ' , start + 3 )
1956+ if end < 0 :
1957+ break
1958+ jpeg_list .append (data [start :end + 2 ])
1959+ idx = end + 2
1960+ if jpeg_list :
1961+ _log (f" → { len (jpeg_list )} JPEG(s) na página" )
1962+ for jpg in jpeg_list :
1963+ if len (jpg ) > 1024 :
1964+ _log (f" → Retornando JPEG { len (jpg )// 1024 } KB" )
1965+ return jpg
1966+
1967+ # === FASE 4: FaceDataRecord + endpoints diretos GET ===
1968+ _log ("Tentativa 4: FaceDataRecord + GET endpoints diretos" )
1969+ for fdid , fd_type in fd_configs :
1970+ # 4a: FaceDataRecord (alguns terminais suportam GET por FPID)
1971+ fdr_payload = json .dumps ({
1972+ "faceLibType" : fd_type , "FDID" : fdid , "FPID" : employee_no
19111973 })
1912- resp = _safe_post ("/ISAPI/Intelligent/FDLib/FDSearch " , payload )
1974+ resp = _safe_post ("/ISAPI/Intelligent/FDLib/FaceDataRecord?format=json " , fdr_payload )
19131975 if resp .get ("ok" ) and resp .get ("data" ):
1914- # A resposta multipart contém TODAS as fotos da página
1915- # Extrair todos os JPEGs e retornar o correto (pela posição)
1916- data = resp ["data" ]
1917- _log (f" → Multipart data: { len (data )} bytes" )
1918- # Encontrar todos os JPEGs no multipart
1919- idx = 0
1920- jpeg_list = []
1921- while True :
1922- start = data .find (b'\xff \xd8 \xff ' , idx )
1923- if start < 0 :
1924- break
1925- end = data .find (b'\xff \xd9 ' , start + 3 )
1926- if end < 0 :
1927- break
1928- jpeg_list .append (data [start :end + 2 ])
1929- idx = end + 2
1930-
1931- if jpeg_list :
1932- _log (f" → { len (jpeg_list )} JPEG(s) encontrado(s) no multipart" )
1933- # Retornar o primeiro JPEG válido (> 1KB)
1934- for jpg in jpeg_list :
1935- if len (jpg ) > 1024 :
1936- _log (f" → Retornando JPEG { len (jpg )// 1024 } KB" )
1937- return jpg
1938-
1939- # === FASE 4: Endpoints diretos GET ===
1940- _log ("Tentativa 4: GET endpoints diretos" )
1941- for fdid , fd_type in fd_configs :
1976+ img = _extract_jpeg (resp ["data" ])
1977+ if img and len (img ) > 100 :
1978+ _log (f" → JPEG via FaceDataRecord! { len (img )// 1024 } KB" )
1979+ return img
1980+
1981+ # 4b: Endpoints diretos GET
19421982 for ep in [
19431983 f"/ISAPI/Intelligent/FDLib/{ fdid } /picture/{ employee_no } ?faceLibType={ fd_type } " ,
19441984 f"/ISAPI/Intelligent/FDLib/{ fdid } /picture/{ employee_no } " ,
19451985 f"/ISAPI/AccessControl/UserInfo/UserPic/{ employee_no } " ,
1986+ f"/ISAPI/Intelligent/FDLib/FDDownload?format=json&FDID={ fdid } &FPID={ employee_no } " ,
1987+ f"/ISAPI/AccessControl/CaptureFaceData" ,
19461988 ]:
19471989 resp = _safe_get (ep )
19481990 status = resp .get ("status_code" , 0 )
0 commit comments