11#!/usr/bin/env python3
22
3- import argparse
43import asyncio
54import json
65import sys
6+ from argparse import ArgumentParser , Namespace
7+ from datetime import datetime
78from pathlib import Path , PurePath
89
910from reolinkfw import __version__ , get_info , get_paks
1011from reolinkfw .extract import extract_pak
1112from reolinkfw .util import sha256_pak
1213
14+ HW_FIELDS = ("board_type" , "detail_machine_type" , "board_name" )
1315
14- async def info (args : argparse .Namespace ) -> None :
15- info = await get_info (args .file_or_url , not args .no_cache )
16- print (json .dumps (info , indent = args .indent , default = str ))
1716
17+ async def info (args : Namespace ) -> None :
18+ pak_infos = await get_info (args .file_or_url , not args .no_cache )
19+ if args .json is None :
20+ width = 21
21+ for idx , info in enumerate (pak_infos ):
22+ info = Namespace (** info )
23+ fs_types = set (fs ["type" ] for fs in info .filesystems )
24+ fs_names = [fs ["name" ] for fs in info .filesystems ]
25+ version = f"{ info .firmware_version_prefix } .{ info .version_file } "
26+ hw_names = set (getattr (info , key ) for key in HW_FIELDS )
27+ build_date = datetime .strptime (info .build_date , "%y%m%d" ).date ()
28+ print (info .pak )
29+ print (f"{ 'Model:' :{width }} " , info .display_type_info )
30+ print (f"{ 'Hardware info:' :{width }} " , ', ' .join (sorted (hw_names )))
31+ print (f"{ 'Device type:' :{width }} " , info .type )
32+ print (f"{ 'Firmware version:' :{width }} " , version )
33+ print (f"{ 'Build date:' :{width }} " , build_date )
34+ print (f"{ 'Architecture:' :{width }} " , info .architecture )
35+ print (f"{ 'OS:' :{width }} " , info .os )
36+ print (f"{ 'Kernel image name:' :{width }} " , info .kernel_image_name )
37+ print (f"{ 'U-Boot version:' :{width }} " , info .uboot_version or "Unknown" )
38+ print (f"{ 'File system:' :{width }} " , ', ' .join (sorted (fs_types )))
39+ print (f"{ 'File system sections:' :{width }} " , ', ' .join (fs_names ))
40+ if idx != len (pak_infos ) - 1 :
41+ print ()
42+ else :
43+ indent = None if args .json < 0 else args .json
44+ print (json .dumps (pak_infos , indent = indent , default = str ))
1845
19- async def extract (args : argparse .Namespace ) -> None :
46+
47+ async def extract (args : Namespace ) -> None :
2048 paks = await get_paks (args .file_or_url , not args .no_cache )
2149 if not paks :
2250 raise Exception ("No PAKs found in ZIP file" )
@@ -28,16 +56,16 @@ async def extract(args: argparse.Namespace) -> None:
2856
2957
3058def main ():
31- parser = argparse . ArgumentParser (description = "Extract information and files from Reolink firmwares" )
59+ parser = ArgumentParser (description = "Extract information and files from Reolink firmwares" )
3260 parser .add_argument ("-V" , "--version" , action = "version" , version = f"%(prog)s { __version__ } " )
3361 subparsers = parser .add_subparsers (required = True )
3462
35- pcache = argparse . ArgumentParser (add_help = False )
63+ pcache = ArgumentParser (add_help = False )
3664 pcache .add_argument ("--no-cache" , action = "store_true" , help = "don't use cache for remote files (URLs)" )
3765
3866 parser_i = subparsers .add_parser ("info" , parents = [pcache ])
3967 parser_i .add_argument ("file_or_url" , help = "URL or on-disk file" )
40- parser_i .add_argument ("-i " , "--indent " , type = int , help = "indent level for pretty print" )
68+ parser_i .add_argument ("-j " , "--json " , nargs = '?' , type = int , const = - 1 , metavar = "indent" , help = "JSON output with optional indentation level for pretty print" )
4169 parser_i .set_defaults (func = info )
4270
4371 descex = "Extract the file system from a Reolink firmware"
0 commit comments