#!/usr/bin/python3 """Generate various reports about movie collection.""" import os import sys import time import typing import os.path def usage(retval: int) -> None: """Output a usage message.""" if retval == 0: write = sys.stdout.write else: write = sys.stderr.write write('Usage: {}\n'.format(sys.argv[0])) write(' --no-views list large files with no corresponding .views file, sorted by mtime\n') write(' --views-timestamps list large files with corresponding .views files, sorted by views timestamp\n') write(' --list-unfinished list all files with corresponding .views files, that were never finished\n') write(' --size-threshold t only consider files larger than t (units are bytes)\n') write('\n') write('Various movie reports, based on movie files and their corresponding *.views files.\n') sys.exit(retval) def find_large_files(size_threshold: int) -> typing.Iterable[str]: """Yield filenames greater then size_threshold in length.""" # find . -type f -size +"${size_threshold}"M -print for root, base_dirnames, base_filenames in os.walk('.'): for base_filename in base_filenames: filename = os.path.join(root, base_filename) length = os.path.getsize(filename) if length >= size_threshold: yield filename def tail_1(filename: str) -> typing.Optional[str]: """Return the last line of filename, sans newline.""" # This could be done a little more efficiently, but .views files tend to be small anyway with open(filename, 'r') as file_: lines = file_.readlines() if lines: return lines[-1].rstrip('\n') else: sys.stderr.write('{}: empty file: {}\n'.format(sys.argv[0], filename)) return None def return_if_start(filename: str, views_filename: str) -> typing.Optional[typing.Tuple[str, str]]: """Yield a tuple to report, if last line of views is a 'start'.""" last_line = tail_1(views_filename) if not last_line: return None fields = last_line.split() if fields[6] == 'start': return (last_line, filename) else: return None def get_unfinished_files() -> typing.Generator[typing.Tuple, None, None]: """Yield files that have been started but not finished.""" for root, base_dirnames, base_filenames in os.walk('.'): for base_filename in base_filenames: if base_filename.endswith('.views'): continue filename = os.path.join(root, base_filename) views_filename = filename + '.views' if os.path.exists(filename) and os.path.exists(views_filename): tuple_ = return_if_start(filename, views_filename) if tuple_ is None: continue yield tuple_ # def get_no_views_files(size_threshold: int) -> typing.Generator[typing.Optional[typing.Tuple], None, None]: def get_no_views_files(size_threshold: int) -> typing.Iterable[typing.Tuple[float, str, str]]: """List files with no .views files.""" for filename in find_large_files(size_threshold): views_filename = filename + '.views' if not os.path.exists(views_filename): mtime = os.path.getmtime(filename) yield (mtime, time.ctime(mtime), filename) def get_views_timestamps_files(size_threshold: int) -> typing.Generator[typing.Tuple, None, None]: """List files with corresponding .views files, along with timestamp data.""" for filename in find_large_files(size_threshold): views_filename = filename + '.views' if os.path.exists(views_filename): last_line = tail_1(views_filename) if last_line is None: continue tuple_ = (last_line, filename) yield tuple_ def to_str_list(list_: typing.Tuple) -> typing.List[str]: """Convert list of whatever to list of str's.""" new_list = [] for element in list_: if isinstance(element, str): new_list.append(element) else: new_list.append(str(element)) return new_list def main() -> None: """Generate reports about movie collection - main function.""" no_views = False views_timestamps = False list_unfinished = False size_threshold = 680 * 1024 * 1024 while sys.argv[1:]: if sys.argv[1] == '--no-views': no_views = True elif sys.argv[1] == '--views-timestamps': views_timestamps = True elif sys.argv[1] == '--list-unfinished': list_unfinished = True elif sys.argv[1] == '--size-threshold': size_threshold = int(sys.argv[2]) del sys.argv[1] elif sys.argv[1] in ('--help', '-h'): usage(0) else: sys.stderr.write('{}: Unrecognized option: {}\n'.format(sys.argv[0], sys.argv[1])) usage(1) del sys.argv[1] if views_timestamps + no_views + list_unfinished != 1: # No report requested, give a usage message sys.stderr.write('{}: You must specify exactly one of --no-views, --views-timestamps and '.format(sys.argv[0])) sys.stderr.write('--list-unfinished\n') usage(1) if views_timestamps: list_ = list(get_views_timestamps_files(size_threshold)) if no_views: list_ = list(get_no_views_files(size_threshold)) if list_unfinished: list_ = list(get_unfinished_files()) list_.sort() for element in list_: print(' '.join(to_str_list(element))) main()