def sync(ctx, localdir, remotedir, encrypt_key, encrypt_type, max_workers, no_show_progress): """同步本地目录到远端""" # Keyboard listener start keyboard_listener_start() api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) encrypt_key = encrypt_key or _encrypt_key(ctx) if encrypt_type != EncryptType.No.name and not encrypt_key: raise ValueError(f"Encrypting with {encrypt_type} must have a key") salt = _salt(ctx) _sync( api, localdir, remotedir, encrypt_key=encrypt_key, salt=salt, encrypt_type=getattr(EncryptType, encrypt_type), max_workers=max_workers, show_progress=not no_show_progress, )
def test_join_path(): a = "/foo" b = "bar" assert join_path(a, b) == "/foo/bar" a = "foo" b = "bar" assert join_path(a, b) == "foo/bar" a = "/foo" b = "../bar" assert join_path(a, b) == "/bar" a = "foo" b = "../bar" assert join_path(a, b) == "bar"
def add(ctx, task_urls, remotedir, file_type): """添加离线下载任务 \b 如果添加 magnet 连接需要指定要下载的文件类型: m:媒体文件 (默认) i:图片文件 d:文档文件 c:压缩文件 a:所有文件 比如要下载所有的媒体文件和文档文件: --file-type 'm,d' """ api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) file_types = [_cloud.FileType.from_(addr) for addr in file_type.split(",")] for url in task_urls: _cloud.add_task(api, url, remotedir, file_types=file_types)
def download( ctx, remotepaths, outdir, recursive, from_index, include, include_regex, exclude, exclude_regex, downloader, concurrency, chunk_size, no_decrypt, quiet, out_cmd, encrypt_password, ): """下载文件""" if out_cmd: assert downloader != Downloader.me.name, "输出命令只能用于第三方下载应用" api = _recent_api(ctx) if not api: return sifters = [] if include: sifters.append(IncludeSifter(include, regex=False)) if include_regex: sifters.append(IncludeSifter(include, regex=True)) if exclude: sifters.append(ExcludeSifter(exclude, regex=False)) if exclude_regex: sifters.append(ExcludeSifter(exclude_regex, regex=True)) pwd = _pwd(ctx) remotepaths = [join_path(pwd, r) for r in remotepaths] if no_decrypt: encrypt_password = b"" else: encrypt_password = encrypt_password or _encrypt_password(ctx) _download( api, remotepaths, outdir, sifters=sifters, recursive=recursive, from_index=from_index, downloader=getattr(Downloader, downloader), downloadparams=DownloadParams(concurrency=concurrency, chunk_size=chunk_size, quiet=quiet), out_cmd=out_cmd, encrypt_password=encrypt_password, )
def rename(ctx, source, dest, show): """文件重命名 \b examples: rename /path/to/far /to/here/foo """ api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) source = join_path(pwd, source) dest = join_path(pwd, dest) file_operators.rename(api, source, dest, show=show)
def ls( ctx, remotepaths, desc, name, time, size, recursive, include, include_regex, exclude, exclude_regex, is_file, is_dir, no_highlight, show_size, show_date, show_md5, show_absolute_path, ): """列出网盘路径下的文件""" api = _recent_api(ctx) if not api: return sifters = [] if include: sifters.append(IncludeSifter(include, regex=False)) if include_regex: sifters.append(IncludeSifter(include, regex=True)) if exclude: sifters.append(ExcludeSifter(exclude, regex=False)) if exclude_regex: sifters.append(ExcludeSifter(exclude_regex, regex=True)) if is_file: sifters.append(IsFileSifter()) if is_dir: sifters.append(IsDirSifter()) pwd = _pwd(ctx) remotepaths = (join_path(pwd, r) for r in list(remotepaths) or (pwd, )) list_files( api, *remotepaths, desc=desc, name=name, time=time, size=size, recursive=recursive, sifters=sifters, highlight=not no_highlight, show_size=show_size, show_date=show_date, show_md5=show_md5, show_absolute_path=show_absolute_path, )
def cat(ctx, remotepath, encoding): """显示文件内容""" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotepath = join_path(pwd, remotepath) _cat(api, remotepath, encoding=encoding)
def remove(ctx, remotepaths): """删除文件""" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotepaths = (join_path(pwd, r) for r in remotepaths) file_operators.remove(api, *remotepaths)
def mkdir(ctx, remotedirs, show): """创建目录""" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotedirs = (join_path(pwd, d) for d in remotedirs) file_operators.makedir(api, *remotedirs, show=show)
def cd(self, remotedir: str = "/"): """Change current working directory""" assert self._who, "No recent user" account = self._accounts.get(self._who) assert account pwd = join_path(account.pwd, remotedir) account = account._replace(pwd=pwd) self._accounts[self._who] = account
def add(ctx, task_urls, remotedir): """添加离线下载任务""" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) for url in task_urls: _cloud.add_task(api, url, remotedir)
def search( ctx, keyword, remotedir, recursive, include, include_regex, exclude, exclude_regex, is_file, is_dir, no_highlight, show_size, show_date, show_md5, csv, ): """搜索包含 `keyword` 的文件""" api = _recent_api(ctx) if not api: return sifters = [] if include: sifters.append(IncludeSifter(include, regex=False)) if include_regex: sifters.append(IncludeSifter(include, regex=True)) if exclude: sifters.append(ExcludeSifter(exclude, regex=False)) if exclude_regex: sifters.append(ExcludeSifter(exclude_regex, regex=True)) if is_file: sifters.append(IsFileSifter()) if is_dir: sifters.append(IsDirSifter()) pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) _search( api, keyword, remotedir, recursive=recursive, sifters=sifters, highlight=not no_highlight, show_size=show_size, show_date=show_date, show_md5=show_md5, csv=csv, )
def cat(ctx, remotepath, encoding, no_decrypt, encrypt_password): """显示文件内容""" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotepath = join_path(pwd, remotepath) if no_decrypt: encrypt_password = b"" else: encrypt_password = encrypt_password or _encrypt_password(ctx) _cat(api, remotepath, encoding=encoding, encrypt_password=encrypt_password)
def cat(ctx, remotepath, encoding, no_decrypt, encrypt_key): """显示文件内容""" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotepath = join_path(pwd, remotepath) if no_decrypt: encrypt_key = None else: encrypt_key = encrypt_key or _encrypt_key(ctx) _cat(api, remotepath, encoding=encoding, encrypt_key=encrypt_key)
def share(ctx, remotepaths, password): """分享文件 \b examples: share /path1 path2 """ assert not password or len(password) == 4, "`password` must be 4 letters" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotepaths = (join_path(pwd, r) for r in remotepaths) _share.share_files(api, *remotepaths, password=password)
def play( ctx, remotepaths, recursive, from_index, include, include_regex, exclude, exclude_regex, player, player_params, m3u8, quiet, out_cmd, ): """播放媒体文件""" api = _recent_api(ctx) if not api: return sifters = [] if include: sifters.append(IncludeSifter(include, regex=False)) if include_regex: sifters.append(IncludeSifter(include, regex=True)) if exclude: sifters.append(ExcludeSifter(exclude, regex=False)) if exclude_regex: sifters.append(ExcludeSifter(exclude_regex, regex=True)) pwd = _pwd(ctx) remotepaths = [join_path(pwd, r) for r in remotepaths] _play( api, remotepaths, sifters=sifters, recursive=recursive, from_index=from_index, player=getattr(Player, player), player_params=player_params, m3u8=m3u8, quiet=quiet, out_cmd=out_cmd, )
def sync(ctx, localdir, remotedir, max_workers, no_show_progress): """同步本地目录到远端""" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) _sync( api, localdir, remotedir, max_workers=max_workers, show_progress=not no_show_progress, )
def upload( ctx, localpaths, remotedir, encrypt_password, encrypt_type, max_workers, no_ignore_existing, no_show_progress, check_md5, ): """上传文件""" # Keyboard listener start keyboard_listener_start() api = _recent_api(ctx) if not api: return encrypt_password = encrypt_password or _encrypt_password(ctx) if encrypt_type != EncryptType.No.name and not encrypt_password: raise ValueError(f"Encrypting with {encrypt_type} must have a key") pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) rapiduploadinfo_file = _rapiduploadinfo_file(ctx) user_id, user_name = _recent_user_id_and_name(ctx) from_to_list = from_tos(localpaths, remotedir) _upload( api, from_to_list, encrypt_password=encrypt_password, encrypt_type=getattr(EncryptType, encrypt_type), max_workers=max_workers, ignore_existing=not no_ignore_existing, show_progress=not no_show_progress, rapiduploadinfo_file=rapiduploadinfo_file, user_id=user_id, user_name=user_name, check_md5=check_md5, )
def copy(ctx, remotepaths, show): """拷贝文件 \b examples: copy /file1 /file2 /to/dir """ api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotepaths = [join_path(pwd, r) for r in remotepaths] if len(remotepaths) < 2: ctx.fail("remote paths < 2") file_operators.copy(api, *remotepaths, show=show)
def save(ctx, shared_url, remotedir, password, no_show_vcode): """保存其他用户分享的链接""" assert not password or len(password) == 4, "`password` must be 4 letters" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) _share.save_shared( api, shared_url, remotedir, password=password, show_vcode=not no_show_vcode, )
def upload(ctx, localpaths, remotedir, max_workers, no_ignore_existing, no_show_progress): """上传文件""" api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) from_to_list = from_tos(localpaths, remotedir) _upload( api, from_to_list, max_workers=max_workers, ignore_existing=not no_ignore_existing, show_progress=not no_show_progress, )
def sync( ctx, localdir, remotedir, encrypt_password, encrypt_type, max_workers, no_show_progress, check_md5, ): """同步本地目录到远端""" # Keyboard listener start keyboard_listener_start() api = _recent_api(ctx) if not api: return pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) encrypt_password = encrypt_password or _encrypt_password(ctx) if encrypt_type != EncryptType.No.name and not encrypt_password: raise ValueError(f"Encrypting with {encrypt_type} must have a key") rapiduploadinfo_file = _rapiduploadinfo_file(ctx) user_id, user_name = _recent_user_id_and_name(ctx) _sync( api, localdir, remotedir, encrypt_password=encrypt_password, encrypt_type=getattr(EncryptType, encrypt_type), max_workers=max_workers, show_progress=not no_show_progress, rapiduploadinfo_file=rapiduploadinfo_file, user_id=user_id, user_name=user_name, check_md5=check_md5, )
def upload( ctx, localpaths, remotedir, encrypt_key, encrypt_type, max_workers, no_ignore_existing, no_show_progress, ): """上传文件""" # Keyboard listener start keyboard_listener_start() api = _recent_api(ctx) if not api: return encrypt_key = encrypt_key or _encrypt_key(ctx) if encrypt_type != EncryptType.No.name and not encrypt_key: raise ValueError(f"Encrypting with {encrypt_type} must have a key") salt = _salt(ctx) pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) from_to_list = from_tos(localpaths, remotedir) _upload( api, from_to_list, encrypt_key=encrypt_key, salt=salt, encrypt_type=getattr(EncryptType, encrypt_type), max_workers=max_workers, ignore_existing=not no_ignore_existing, show_progress=not no_show_progress, )
async def handle_request( request: Request, remotepath: str, order: str = "asc", # desc , asc sort: str = "name", # name, time, size ): desc = order == "desc" name = sort == "name" time = sort == "time" size = sort == "size" global _root_dir global _api assert _api remotepath = remotepath.strip("/") _rp = join_path(_root_dir, remotepath) # Anti path traversal attack if not _rp.startswith(_root_dir): raise HTTPException(status_code=404, detail="Item not found") _range = request.headers.get("range") if not _api.exists(_rp): raise HTTPException(status_code=404, detail="Item not found") is_dir = _api.is_dir(_rp) if is_dir: chunks = ["/"] + (remotepath.split("/") if remotepath != "" else []) navigation = [ (i - 1, "../" * (len(chunks) - i), name) for i, name in enumerate(chunks, 1) ] pcs_files = _api.list(_rp, desc=desc, name=name, time=time, size=size) entries = [] for f in pcs_files: p = Path(f.path) entries.append((f.is_dir, p.name, f.size, format_date(f.local_mtime or 0))) cn = _html_tempt.render( root_dir=remotepath, navigation=navigation, entries=entries ) return HTMLResponse(cn) else: try: fs = _api.file_stream(_rp, encrypt_password=_encrypt_password) except Exception as err: print("Error:", err) raise HTTPException( status_code=500, detail=f"Error: {err}, remotepath: {_rp}" ) if not fs: raise HTTPException(status_code=404, detail=f"No download link: {_rp}") length = len(fs) headers: Dict[str, str] = { "accept-ranges": "bytes", "connection": "Keep-Alive", "access-control-allow-origin": "*", } ext = os.path.splitext(remotepath)[-1] content_type = mimetypes.types_map.get(ext) if content_type: headers["content-type"] = content_type if _range and fs.seekable(): assert _range.startswith("bytes=") status_code = 206 start, end = _range[6:].split("-") _s, _e = int(start or 0), int(end or length - 1) + 1 _iter_io = fake_io(fs, _s, _e) headers["content-range"] = f"bytes {_s}-{_e-1}/{length}" headers["content-length"] = str(_e - _s) else: status_code = 200 _iter_io = fake_io(fs) headers["content-length"] = str(length) return StreamingResponse(_iter_io, status_code=status_code, headers=headers)
def sync( api: BaiduPCSApi, localdir: str, remotedir: str, encrypt_key: Any = None, salt: Any = None, encrypt_type: EncryptType = EncryptType.No, max_workers: int = CPU_NUM, slice_size: int = DEFAULT_SLICE_SIZE, show_progress: bool = True, ): localdir = Path(localdir).as_posix() remotedir = Path(remotedir).as_posix() is_file = api.is_file(remotedir) assert not is_file, "remotedir must be a directory" if not api.exists(remotedir): all_pcs_files = {} else: all_pcs_files = { pcs_file.path[len(remotedir) + 1:]: pcs_file for pcs_file in recursive_list(api, remotedir) } fts: List[FromTo] = [] check_list: List[Tuple[str, PcsFile]] = [] all_localpaths = set() for localpath in walk(localdir): path = localpath[len(localdir) + 1:] all_localpaths.add(path) if path not in all_pcs_files: fts.append(FromTo(localpath, join_path(remotedir, path))) else: check_list.append((localpath, all_pcs_files[path])) semaphore = Semaphore(max_workers) with ThreadPoolExecutor(max_workers=CPU_NUM) as executor: tasks = {} for lp, pf in check_list: semaphore.acquire() fut = executor.submit(sure_release, semaphore, check_file_md5, lp, pf) tasks[fut] = (lp, pf) for fut in as_completed(tasks): is_equal = fut.result() lp, pf = tasks[fut] if not is_equal: fts.append(FromTo(lp, pf.path)) _upload( api, fts, encrypt_key=encrypt_key, salt=salt, encrypt_type=encrypt_type, max_workers=max_workers, slice_size=slice_size, ignore_existing=False, show_progress=show_progress, ) to_deletes = [] for rp in all_pcs_files.keys(): if rp not in all_localpaths: to_deletes.append(all_pcs_files[rp].path) if to_deletes: api.remove(*to_deletes) print(f"Delete: [i]{len(to_deletes)}[/i] remote paths")
def sync( api: BaiduPCSApi, localdir: str, remotedir: str, encrypt_password: bytes = b"", encrypt_type: EncryptType = EncryptType.No, max_workers: int = CPU_NUM, slice_size: int = DEFAULT_SLICE_SIZE, show_progress: bool = True, rapiduploadinfo_file: Optional[str] = None, user_id: Optional[int] = None, user_name: Optional[str] = None, check_md5: bool = False, ): localdir = Path(localdir).as_posix() remotedir = Path(remotedir).as_posix() is_file = api.is_file(remotedir) assert not is_file, "remotedir must be a directory" if not api.exists(remotedir): all_pcs_files = {} else: all_pcs_files = { pcs_file.path[len(remotedir) + 1 :]: pcs_file for pcs_file in recursive_list(api, remotedir) } fts: List[FromTo] = [] check_list: List[Tuple[str, PcsFile]] = [] all_localpaths = set() for localpath in walk(localdir): path = localpath[len(localdir) + 1 :] all_localpaths.add(path) if path not in all_pcs_files: fts.append(FromTo(localpath, join_path(remotedir, path))) else: check_list.append((localpath, all_pcs_files[path])) for lp, pf in check_list: lstat = Path(lp).stat() if int(lstat.st_mtime) != pf.local_mtime or lstat.st_size != pf.size: fts.append(FromTo(lp, pf.path)) to_deletes = [] for rp in all_pcs_files.keys(): if rp not in all_localpaths: to_deletes.append(all_pcs_files[rp].path) logger.debug( "`sync`: all localpaths: %s, " "localpaths needed to upload: %s, " "remotepaths needed to delete: %s", len(all_localpaths), len(fts), len(to_deletes), ) # The md5 of remote file is incorrect at most time, so we don't compare md5 # # # Compare localpath content md5 with remotepath content md5 # semaphore = Semaphore(max_workers) # with ThreadPoolExecutor(max_workers=CPU_NUM) as executor: # tasks = {} # for lp, pf in check_list: # semaphore.acquire() # fut = executor.submit(sure_release, semaphore, check_file_md5, lp, pf) # tasks[fut] = (lp, pf) # # for fut in as_completed(tasks): # is_equal = fut.result() # lp, pf = tasks[fut] # if not is_equal: # fts.append(FromTo(lp, pf.path)) _upload( api, fts, encrypt_password=encrypt_password, encrypt_type=encrypt_type, max_workers=max_workers, slice_size=slice_size, ignore_existing=False, show_progress=show_progress, rapiduploadinfo_file=rapiduploadinfo_file, user_id=user_id, user_name=user_name, check_md5=check_md5, ) if to_deletes: api.remove(*to_deletes) print(f"Delete: [i]{len(to_deletes)}[/i] remote paths")
def rapid_upload( api: BaiduPCSApi, remotedir: str, link: str = "", slice_md5: str = "", content_md5: str = "", content_crc32: int = 0, content_length: int = 0, filename: str = "", no_ignore_existing: bool = False, rapiduploadinfo_file: Optional[str] = None, user_id: Optional[int] = None, user_name: Optional[str] = None, ): """Rapid upload with params If given `link` and `filename`, then filename of link will be replace by `filename` """ if link: slice_md5, content_md5, content_crc32, content_length, _filename = _parse_link( link) content_crc32 = content_crc32 or 0 filename = filename or _filename remotepath = join_path(remotedir, filename) assert all([slice_md5, content_md5, content_length ]), f"`rapid_upload`: parsing rapid upload link fails: {link}" if not no_ignore_existing: if api.exists(remotepath): return try: pcs_file = api.rapid_upload_file( slice_md5, content_md5, content_crc32, content_length, remotepath, ondup="overwrite", ) if rapiduploadinfo_file: save_rapid_upload_info( rapiduploadinfo_file, slice_md5, content_md5, content_crc32, content_length, remotepath=remotepath, user_id=user_id, user_name=user_name, ) print(f"[i blue]Save to[/i blue] {pcs_file.path}") except Exception as err: link = PcsRapidUploadInfo( slice_md5=slice_md5, content_md5=content_md5, content_crc32=content_crc32, content_length=content_length, remotepath=remotepath, ).cs3l() print( f"[i yellow]Rapid Upload fails[/i yellow]: error: {err} link: {link}", )
def play( ctx, remotepaths, recursive, from_index, include, include_regex, exclude, exclude_regex, player, player_params, m3u8, quiet, shuffle, ignore_ext, out_cmd, use_local_server, encrypt_password, ): """播放媒体文件""" api = _recent_api(ctx) if not api: return sifters = [] if include: sifters.append(IncludeSifter(include, regex=False)) if include_regex: sifters.append(IncludeSifter(include, regex=True)) if exclude: sifters.append(ExcludeSifter(exclude, regex=False)) if exclude_regex: sifters.append(ExcludeSifter(exclude_regex, regex=True)) pwd = _pwd(ctx) remotepaths = [join_path(pwd, r) for r in remotepaths] local_server = "" if use_local_server: encrypt_password = encrypt_password or _encrypt_password(ctx) host = "localhost" port = random_avail_port(49152, 65535) local_server = f"http://{host}:{port}" ps = Process( target=start_server, args=( api, "/", ), kwargs=dict( host=host, port=port, workers=CPU_NUM, encrypt_password=encrypt_password, log_level="warning", ), ) ps.start() time.sleep(1) _play( api, remotepaths, sifters=sifters, recursive=recursive, from_index=from_index, player=getattr(Player, player), player_params=player_params, m3u8=m3u8, quiet=quiet, shuffle=shuffle, ignore_ext=ignore_ext, out_cmd=out_cmd, local_server=local_server, ) if use_local_server: ps.terminate()
def rp( ctx, remotedir, link, input_file, slice_md5, content_md5, content_crc32, content_length, filename, no_ignore_existing, max_workers, ): """用秒传连接或参数上传 如果设置了 --input-file,将会忽略 `--link` 和 指定的特征参数。 如果设置了 --link,那么其他参数都不需要了。 如果设置了 --filename,将会覆盖 link 中的文件名。 """ api = _recent_api(ctx) if not api: return assert (link or input_file or all( [slice_md5, content_md5, content_length, filename])), "No params" pwd = _pwd(ctx) remotedir = join_path(pwd, remotedir) rapiduploadinfo_file = _rapiduploadinfo_file(ctx) user_id, user_name = _recent_user_id_and_name(ctx) if input_file and os.path.exists(input_file): lines = open(input_file).readlines() links = [line.strip() for line in lines if line.strip()] rapid_upload_links( api, remotedir, links=links, no_ignore_existing=no_ignore_existing, rapiduploadinfo_file=rapiduploadinfo_file, user_id=user_id, user_name=user_name, max_workers=max_workers, ) else: rapid_upload( api, remotedir, link=link, slice_md5=slice_md5, content_md5=content_md5, content_crc32=content_crc32, content_length=content_length, filename=filename, no_ignore_existing=no_ignore_existing, rapiduploadinfo_file=rapiduploadinfo_file, user_id=user_id, user_name=user_name, )
def ls( ctx, remotepaths, desc, name, time, size, recursive, include, include_regex, exclude, exclude_regex, is_file, is_dir, no_highlight, show_size, show_date, show_md5, show_absolute_path, show_dl_link, show_hash_link, hash_link_protocol, no_check_md5, csv, only_dl_link, only_hash_link, ): """列出网盘路径下的文件和对应的文件信息""" api = _recent_api(ctx) if not api: return sifters = [] if include: sifters.append(IncludeSifter(include, regex=False)) if include_regex: sifters.append(IncludeSifter(include, regex=True)) if exclude: sifters.append(ExcludeSifter(exclude, regex=False)) if exclude_regex: sifters.append(ExcludeSifter(exclude_regex, regex=True)) if is_file: sifters.append(IsFileSifter()) if is_dir: sifters.append(IsDirSifter()) pwd = _pwd(ctx) remotepaths = (join_path(pwd, r) for r in list(remotepaths) or (pwd, )) rapiduploadinfo_file = _rapiduploadinfo_file(ctx) user_id, user_name = _recent_user_id_and_name(ctx) list_files( api, *remotepaths, desc=desc, name=name, time=time, size=size, recursive=recursive, sifters=sifters, highlight=not no_highlight, rapiduploadinfo_file=rapiduploadinfo_file, user_id=user_id, user_name=user_name, show_size=show_size, show_date=show_date, show_md5=show_md5, show_absolute_path=show_absolute_path, show_dl_link=show_dl_link, show_hash_link=show_hash_link, hash_link_protocol=hash_link_protocol, check_md5=not no_check_md5, csv=csv, only_dl_link=only_dl_link, only_hash_link=only_hash_link, )