def create(users, fspath, wspath, htpasswd, **args): verbose = args.get("verbose", False) term.banner("CREATE INDEXES") progress = term.Progress(0, title="Searching:", bar=(verbose is False)) galleries = search_galleries(fspath, load_access=True, load_albums=True, progress=progress.progress) progress.finish() # create default index file create_write(fspath, galleries, path=wspath) if users: users = collect_users(galleries) for user in users: user_path = os.path.join(fspath, "~{0}".format(safe(user.name))) if not os.path.exists(user_path): os.mkdir(user_path) if os.path.isdir(user_path): user_galleries = collect_galleries_for_user(galleries, user) # create user index file create_write(user_path, user_galleries, path=wspath, title=user.name) # create htaccess file for user directory access = Access(authname=TITLE, authuserfile=htpasswd) access.users.extend([user]) access.write(user_path) else: raise IOError("CREATE INDEXES: could not write index for user '{0}'".format(str(user))) term.banner("DONE", type="INFO")
def galleries_list(fspath, stat=False, **args): term.banner("LIST OF GALLERIES") compact = not stat galleries = search_galleries(fspath) if compact: print(term.em('# '), end='') print_gallery_name('Name', term.em) print(term.em("{1}{0}".format('Albums', SYMBOL_SEPARATOR_CLEAR))) for i, gallery in enumerate(galleries): if compact: print(term.p("{:<5d}".format(i)), end='') print_gallery_name(gallery.name) print(term.p("{1}{0}".format( ', '.join(str(x) for x in gallery.albums), SYMBOL_SEPARATOR ))) else: print_gallery_name(gallery.name, term.em, end='\n') print(term.p("{0:>20}: {1}".format('Albums', ', '.join(str(x) for x in gallery.albums)))) nop, notp, sod = gallery_stat(gallery.path) print(term.p("{0:>20}: {1!s}".format("Number of pictures", nop))) print(term.p("{0:>20}: {1}".format("Size on disk", humanfriendly.format_size(sod)))) print()
def install(self, gallery): term.banner("INSTALL GALLERY") self.destination_path = os.path.join(self.fspath, gallery.name) self.__check_destination(gallery.name) self.__install(gallery) self.__restore_access()
def __check_destination(self, gallery_name): assert self.destination_path if os.path.exists(self.destination_path): if os.path.isdir(self.destination_path): term.banner("GALLERY '{0}' ALREADY EXISTS".format(gallery_name), type='WARN') if not self.interactive or not term.ask("OVERWRITE EXISTING GALLERY?"): raise GSAbortError("OVERWRITE EXISTING GALLERY") self.__backup_destination(gallery_name) else: raise GSError( "COULDN'T OVERWRITE EXISTING GALLERY\nAT '{0}'".format(self.destination_path) ) return True
def install(wspath, fspath, htpasswd, **args): """ Install indexes: Create htaccess file with rewrite rules to serve user-specific indexes. Indexes must be created by executing 'index create' command. NOTE: htaccess file will be overwritten! """ term.banner("INSTALL INDEXES") galleries = search_galleries(fspath, load_access=True) users = collect_users(galleries) access = Access(authname=TITLE, authuserfile=htpasswd) access.users.extend(users) access.conf.append(("RewriteEngine", "On")) access.conf.append(("RewriteBase", wspath)) access.conf.append(("RewriteCond", "%{REMOTE_user} ^.+$")) access.conf.append(("RewriteRule", "^$ {0}~%{{REMOTE_user}} [R,L]".format(wspath))) access.write(fspath) term.banner("DONE", type="INFO")
def setcover(gallery_name, album_name, image_name, fspath, **args): gallery = get_gallery(fspath, gallery_name) album = get_album_in_gallery(gallery, album_name) term.banner("SET ALBUM COVER") image_filename = os.path.join(gallery.path, 't', "{0}.jpg".format(image_name)) if not os.path.exists(image_filename) or not os.path.isfile(image_filename): term.banner("IMAGE '{0}' NOT FOUND IN GALLERY '{1}'\nAT '{2}'".format( image_name, gallery.name, image_filename), type='ERROR') return shutil.copyfile(image_filename, os.path.join(gallery.path, "{0}.jpg".format(album.label))) term.banner("DONE", type='INFO')
def optimize(gallery_name, fspath, **args): gallery = get_gallery(fspath, gallery_name) term.banner("OPTIMIZE IMAGES (JPEG)") dry = args.get('dry_run') try: subprocess.call(['djpeg', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) subprocess.call(['cjpeg', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except FileNotFoundError as x: term.banner("MOZJPEG MUST BE INSTALLED", type='INFO') raise GSError("OPTIMIZE: {0}".format(x)) if dry: term.banner("DRY RUN", type='INFO') for directory in ['p', 't']: path = os.path.join(gallery.path, directory) for root, directories, files in os.walk(path): for filename in sorted(fnmatch.filter(files, '*.jpg')): path = os.path.join(root, filename) size = os.path.getsize(path) if size: djpeg = subprocess.Popen(['djpeg', path], stdout=subprocess.PIPE) cjpeg = subprocess.Popen(['cjpeg', '-quality', '92', '-opt', '-sample', '1x1', '-notrellis' ], stdin=djpeg.stdout, stdout=subprocess.PIPE) new_size = 0 new_path = None if dry: with cjpeg: for chunk in iter(lambda: cjpeg.stdout.read(4096), b''): new_size = new_size + len(chunk) else: new_path = path + ".new" with cjpeg, open(new_path, 'wb') as f: for chunk in iter(lambda: cjpeg.stdout.read(4096), b''): f.write(chunk) new_size = new_size + len(chunk) ratio = 100 / size * new_size print(term.p("{0:32} {1:6.2f}% {2:8} {3:8}".format(filename, ratio, size, new_size)), end=' ') if new_path: if ratio < 95: print("... gained {0:.2f}%".format(100 - ratio), end=' ') shutil.move(new_path, path) else: print("... skipping", end=' ') os.remove(new_path) print() term.banner("DONE", type='INFO')
def __exit__(self, exception, value, traceback): if exception is None: term.banner("DONE", type='INFO') self.__backup_destination_clean() else: self.__backup_destination_restore()
def main(args=None): config = configparser.ConfigParser() config.read('galleries.ini') config_htpasswd = config['Access'].get('htpasswd') if 'Access' in config else None parser = argparse.ArgumentParser( prog=os.getenv('SCRIPT'), description=DESCRIPTION, epilog=EPILOG, formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument('--fspath', type=path_argument, default='/var/www/web/', help="Path to web galleries on filesystem. Defaults to '/var/www/web/'.") parser.add_argument('--wspath', type=path_argument, default='/', help="Path to web galleries on webserver. Defaults to '/'.") if 'Defaults' in config: parser.set_defaults(**config['Defaults']) subparsers = parser.add_subparsers(title='commands', dest='command') subparsers.required = True parser_interactive = argparse.ArgumentParser(add_help=False) parser_interactive.add_argument('-i', '--interactive', action='store_true', default=False, help="enable interactive mode and ask for input") parser_verbose = argparse.ArgumentParser(add_help=False) parser_verbose.add_argument('-v', '--verbose', action='store_true', default=False, help="enable verbose output") parser_dryrun = argparse.ArgumentParser(add_help=False) parser_dryrun.add_argument('-n', '--dry-run', action='store_true', default=False, help="simulate actions only and print expected results") # list command parser_list = subparsers.add_parser('list', help="List all web galleries.") parser_list.set_defaults(function=galleries_list) parser_list.add_argument('--stat', action='store_true', default=False, help="Print statistics for web galleries.") # install command parser_install = subparsers.add_parser('install', parents=[parser_interactive], help="Install web gallery.") parser_install.set_defaults(function=galleries_install) parser_install.add_argument('--htpasswd', default=config_htpasswd, help="Path to htpasswd file for access control.") parser_install.add_argument('--set-albums-cover', metavar='image_name', help="Set cover for albums of web gallery.") parser_install.add_argument('gallery_path', help="Path to web gallery to install.") # access command parser_access = subparsers.add_parser('access', help="Manage access to web gallery.") subparsers_access = parser_access.add_subparsers( title='access commands', dest='access_command') subparsers_access.required = True # access show command parser_access_show = subparsers_access.add_parser('show', help="Show access to web galleries.") parser_access_show.set_defaults(function=access.show) # access init command parser_access_init = subparsers_access.add_parser('init', help="Initialize access to web gallery.") parser_access_init.set_defaults(function=access.manage) parser_access_init.add_argument('--htpasswd', required=(config_htpasswd is None), default=config_htpasswd, help="Path to htpasswd file for access control.") parser_access_init.add_argument('gallery_name', help="Name of web gallery to manage.") # access list command parser_access_list = subparsers_access.add_parser('list', help="List access to web gallery.") parser_access_list.set_defaults(function=access.manage) parser_access_list.add_argument('gallery_name', help="Name of web gallery to manage.") # access adduser command parser_access_adduser = subparsers_access.add_parser('adduser', help="Add access for user to web gallery.") parser_access_adduser.set_defaults(function=access_manage) parser_access_adduser.add_argument('username', nargs='+', help="Name of user to grant access to web gallery.") parser_access_adduser.add_argument('galleries_name_pattern', help="Pattern for names of web galleries to manage.") # access deluser command parser_access_removeuser = subparsers_access.add_parser('removeuser', help="Remove access for user to web gallery.") parser_access_removeuser.set_defaults(function=access_manage) parser_access_removeuser.add_argument('username', nargs='+', help="Name of user to revoke access to web gallery.") parser_access_removeuser.add_argument('galleries_name_pattern', help="Pattern for names of web galleries to manage.") # indexes command parser_indexes = subparsers.add_parser('indexes', help="Manage indexes of web galleries.") subparsers_indexes = parser_indexes.add_subparsers( title='indexes commands', dest='indexes command') subparsers_indexes.required = True # indexes create command parser_indexes_create = subparsers_indexes.add_parser('create', parents=[parser_verbose], help="Create indexes for web galleries.") parser_indexes_create.add_argument('--users', action='store_true', default=False, help="Create indexes for users.") parser_indexes_create.add_argument('--htpasswd', default=config_htpasswd, help="Path to htpasswd file for access control.") parser_indexes_create.set_defaults(function=indexes.create) # indexes install command config_htpasswd = config['Access'].get('htpasswd') if 'Access' in config else None parser_indexes_install = subparsers_indexes.add_parser('install', help="Install indexes for web galleries.") parser_indexes_install.add_argument('--htpasswd', required=(config_htpasswd is None), default=config_htpasswd, help="Path to htpasswd file for access control.") parser_indexes_install.set_defaults(function=indexes.install) # albums command parser_albums = subparsers.add_parser('albums', help="Manage albums of web gallery.") subparsers_albums = parser_albums.add_subparsers( title='albums commands', dest='albums command') subparsers_albums.required = True # albums setcover command parser_albums_setcover = subparsers_albums.add_parser('setcover', help="Set cover for album of web gallery.") parser_albums_setcover.set_defaults(function=albums.setcover) parser_albums_setcover.add_argument('image_name', help="Name of image to be used as cover (must exist inside gallery).") parser_albums_setcover.add_argument('album_name', help="Name of album inside web gallery.") parser_albums_setcover.add_argument('gallery_name', help="Name of web gallery to manage.") # images command parser_images = subparsers.add_parser('images', help="Manage images of web gallery.") subparsers_images = parser_images.add_subparsers( title='images commands', dest='images_command') subparsers_images.required = True # images optimize command parser_images_optimize = subparsers_images.add_parser('optimize', parents=[parser_dryrun], help="Optimize images of web gallery.") parser_images_optimize.set_defaults(function=images.optimize) parser_images_optimize.add_argument('gallery_name', help="Name of web gallery to manage.") try: arguments = parser.parse_args() if args == None else parser.parse_args(args) if 'verbose' in arguments and arguments.verbose == True: logging_level = logging.INFO else: logging_level = logging.WARNING logging.basicConfig(level=logging_level, format='[%(levelname)s][%(funcName)s] %(message)s') arguments.function(**vars(arguments)) except GSError as x: term.banner(str(x), type='ERROR') except FileNotFoundError as x: term.banner("NOT FOUND '{0}'".format(x), type='ERROR')