1616# to install them all, you can do:
1717# pip install requests lxml beautifulsoup4 python-dateutil pytz pyperclip wcwidth rich browser_cookie3
1818
19- # # ==================== CONSTANTS ====================
19+ # ==================== CONSTANTS ====================
2020
2121'''Twitter media name handle
2222The filename format I use, inherited from good old twMediaDownloader (RIP: here is a mirror: https://github.com/fireattack/twMediaDownloader).
@@ -479,6 +479,50 @@ def load_json(filename, encoding='utf-8'):
479479 data = json .load (f )
480480 return data
481481
482+ def dump_html (soup , filename = 'temp.html' , encoding = 'utf-8' ):
483+ """
484+ Dump the contents of a BeautifulSoup object to an HTML file.
485+
486+ Args:
487+ soup (BeautifulSoup): The BeautifulSoup object containing the HTML content.
488+ filename (str, optional): The name of the output file. Defaults to 'temp.html'.
489+ encoding (str, optional): The encoding to use when writing the file. Defaults to 'utf-8'.
490+ """
491+ filename = Path (filename )
492+ filename .parent .mkdir (parents = True , exist_ok = True )
493+ with filename .open ('w' , encoding = encoding ) as f :
494+ f .write (str (soup ))
495+
496+ def dump_tsv (data , filename = 'temp.tsv' , verbose = True , print_header = False ):
497+ """
498+ Dump data into a TSV (Tab-Separated Values) file.
499+
500+ Args:
501+ data (list): The data to be dumped. It can be a list of lists or a list of dictionaries.
502+ filename (str, optional): The name of the output file. Defaults to 'temp.tsv'.
503+ verbose (bool, optional): Whether to print the content of the TSV file. Defaults to True.
504+ print_header (bool, optional): Whether to print the header in the TSV file. Defaults to False.
505+ """
506+ s = ''
507+ if isinstance (data [0 ], dict ):
508+ headers = data [0 ].keys ()
509+ if print_header :
510+ s += '\t ' .join (headers ) + '\n '
511+ for row in data :
512+ s += '\t ' .join ([str (row [h ]) for h in headers ]) + '\n '
513+ else :
514+ if print_header :
515+ print ('[W] No header provided. Use default header [0, 1, 2, ...]' )
516+ s += '\t ' .join ([str (i ) for i in range (len (data [0 ]))]) + '\n '
517+ for row in data :
518+ s += '\t ' .join ([str (i ) for i in row ]) + '\n '
519+ if verbose :
520+ print (s )
521+ filename = Path (filename )
522+ filename .parent .mkdir (parents = True , exist_ok = True )
523+ filename .write_text (s , encoding = 'utf8' )
524+
525+
482526def get_files (directory , recursive = False , file_filter = None , path_filter = None ):
483527 '''filter(s): true means include, false means exclude'''
484528 directory = Path (directory )
@@ -582,6 +626,7 @@ def move_or_delete_duplicate(src, dst, verbose=True, conflict='error'):
582626 Returns:
583627 None
584628 """
629+
585630 if not src .exists ():
586631 raise FileNotFoundError (f"The source file { src } does not exist." )
587632 if src == dst :
@@ -593,7 +638,7 @@ def move_or_delete_duplicate(src, dst, verbose=True, conflict='error'):
593638 return
594639 else :
595640 if conflict == 'skip' :
596- print (f'[W] Destination file { src . name } already exists and hash does not match. Skip.' )
641+ print (f'[W] Destination file { dst } already exists and hash does not match. Skip.' )
597642 return
598643 elif conflict == 'error' :
599644 raise FileExistsError (f"Destination file { dst } already exists." )
0 commit comments