#!/usr/bin/python3 # Project page: https://github.com/sergei-bondarenko/picrandom import sys import time import os import threading import shutil import random import argparse import platform class BadDirException(Exception): pass class Spinner: def spinning_cursor(self): while 1: for cursor in '|/-\\': yield cursor def __init__(self, delay=None): self.busy = False self.delay = 0.2 self.spinner_generator = self.spinning_cursor() if delay and float(delay): self.delay = delay def spinner_task(self): while self.busy: sys.stdout.write(next(self.spinner_generator)) sys.stdout.flush() time.sleep(self.delay) sys.stdout.write('\b') sys.stdout.flush() def start(self): self.busy = True t = threading.Thread(target=self.spinner_task) t.daemon = True t.start() def stop(self): self.busy = False time.sleep(self.delay) class Picrandom(): def __init__(self, dir): """ Class attributes: self.files - set of all available files self.bad_files - counter for unreadable files self.bad_dirs - unreadable dirs (have no permission to read) self.good_files - files that are readable self.dir - a search path """ self.files = set() self.bad_files = 0 self.bad_dirs = 0 self.good_files = 0 self.dir = dir if not os.path.isdir(self.dir): raise BadDirException("Directory is not correct or " + "not accessible.") def scan(self): """ Gets all files from self.dir recursively """ for root, dirnames, filenames in os.walk(self.dir, followlinks=True): subdir = set() for filename in filenames: _filename = os.path.join(root, filename) if os.access(_filename, os.R_OK): subdir.update([_filename]) self.good_files += 1 else: if os.access(root, os.R_OK | os.X_OK): print("\rCan't read file {}".format(_filename)) self.bad_files += 1 self.files.update(subdir) for dirname in dirnames: _dirname = os.path.join(root, dirname) if not os.access(_dirname, os.R_OK | os.X_OK): print("\rCan't open directory {}".format(_dirname)) self.bad_dirs += 1 def copy(self, count=5): """ Choses random five files from all files and copies them to the /tmp/picrandom/ """ self.prepare_folder() chosen_files = random.sample(self.files, min(count, self.good_files)) for f in chosen_files: new_name = self.choose_new_name(f) shutil.copy2(f, "/tmp/picrandom/{}".format(new_name)) def choose_new_name(self, old_name): """ Returns a new name if there is already file with such name in the /tmp/picrandom/ """ name = os.path.basename(old_name) if not os.path.exists("/tmp/picrandom/{}".format(name)): return name i = 0 while True: new_name = "{}_{}".format(i, name) # crop filename if too big new_name = new_name[:os.pathconf("/tmp/picrandom/", 'PC_NAME_MAX')] if os.path.exists("/tmp/picrandom/{}".format(new_name)): i += 1 else: return new_name def prepare_folder(self, path="/tmp/picrandom/"): """ Creates a directory if not exists """ if os.path.isfile(path): os.remove(path) if not os.path.exists(path): os.mkdir(path) def main(): if sys.version_info < (3, 0): print("Python 2 is not supported, it will retire " + "soon, see: https://pythonclock.org/") sys.exit() if 'LINUX' not in platform.system().upper(): print("Linux only, sorry.") sys.exit() spinner = Spinner() parser = argparse.ArgumentParser(description="Recursively scans a provided" + " path and copies five random files to the /tmp/picrandom/") parser.add_argument('dir', nargs='?', default=os.getcwd(), help="The search path. It will be the current folder if not specified") args = parser.parse_args() try: picrandom = Picrandom(args.dir) spinner.start() picrandom.scan() spinner.stop() print("\rTotal number of files: {}".format(picrandom.good_files)) picrandom.copy() copy_amount = min(5, picrandom.good_files) print("{} files copied to /tmp/picrandom/".format(copy_amount)) except Exception as err: print("ERROR: {}".format(err)) sys.exit() if __name__ == '__main__': try: main() except KeyboardInterrupt: sys.stdout.write('\r') sys.stdout.flush() sys.exit()