PK!L@g66vidl/__init__.pyfrom vidl import app def main(): return app.main()PK!==vidl/__main__.pyif __name__ == '__main__': # make the package be able to import itself (vidl) import os,sys,inspect currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) parentdir = os.path.dirname(currentdir) sys.path.insert(0,parentdir) from vidl import app app.main()PK!BcV۵ vidl/app.pyfrom colorboy import green, cyan, red from pathlib import Path package_name = 'vidl' # used for getting config location and package version package_author = 'Kasper Henningsen' # used for getting config location def log(*args, error=False, quiet=False, callback=None, quit=True, **named_args): vidl_text = cyan('[vidl]') if error: print(vidl_text, red('Error:'), *args, **named_args) elif quiet == False: print(vidl_text, *args, **named_args) script_filename = "vidl" # used to be sys.argv[0], but Poetry changes sys.argv[0] to the full path def vidl_help(): print( '') print(green( 'Download Usage:')) print( ' '+cyan(script_filename)+' [format] [options] ') print( '') print(green( 'Download Options:')) print(cyan( ' format ')+'mp3, mp4, wav or m4a. Default mp3.') print(cyan( ' --no-md ')+'Don\'t add metadata to downloaded files.') print(cyan( ' --no-smart-md ')+'Don\'t extract artist and song name from title.') print(cyan( ' --no-dl ')+'Don\'t download anything. Usually used with -v') print(cyan( ' -v, --verbose ')+'Display all logs.') print( '') print(green( 'Global Options:')) print(cyan( ' --version ')+'Display vidl version. "vidl -v" also works.') print(cyan( ' -h, --help ')+'Display this help message.') print(cyan( ' --config-path ')+'Display the location of vidl\'s configuration file.') print(cyan( '')) print(cyan( 'Update vidl:')) print(cyan( ' pip install vidl --upgrade --upgrade-strategy eager')) print( '') quit() def main(): import sys if len(sys.argv) <= 1 or '--help' in sys.argv or '-h' in sys.argv: vidl_help() elif '--version' in sys.argv or sys.argv[1:] == ['-v']: import vidl.version log("Version", vidl.version.get_package_version()) elif '--config-path' in sys.argv: import vidl.config log("Config path:", vidl.config.config_path) else: from vidl import dl dl.main() if __name__ == '__main__': main() PK!5xxvidl/config.pyimport sys, json, os, appdirs from colorboy import green from vidl.app import log, package_name, package_author user_data_dir = appdirs.user_data_dir(package_name, package_author) config_path = os.path.join(user_data_dir, 'config.json') user_md_parser_path = os.path.join(user_data_dir, 'user_md_parser.py') default_user_md_parser_path = os.path.join(os.path.dirname(__file__), 'default_user_md_parser.py') def save_file(path, content, json=False): try: file = open(path, 'w+') except FileNotFoundError: os.makedirs(user_data_dir) file = open(path, 'w+') file.write(content) file.close() def get_default_download_folder(): if sys.platform == 'darwin': return '~/Downloads' elif sys.platform == 'win32': import os path = os.path.join(os.getenv('USERPROFILE'), 'Downloads') return path else: return None default_config = { 'download_folder': get_default_download_folder(), 'output_template': '%(uploader)s - %(title)s.%(ext)s', } if not os.path.isfile(config_path): save_file(config_path, json.dumps(default_config, indent=2)) if not os.path.isfile(user_md_parser_path): default_user_md_parser = open(default_user_md_parser_path).read() save_file(user_md_parser_path, default_user_md_parser) configs = json.loads(open(config_path).read()) from importlib.machinery import SourceFileLoader user_md_parser = SourceFileLoader('user_md_parser', user_md_parser_path).load_module().user_md_parser def get_config(key): if key not in configs: log('Config does not exist:', green(key), error=True) quit() return configs[key] PK!õvidl/default_user_md_parser.pydef user_md_parser(smart_md, md, video_info, url_info): # smart_md: # Metadata object created by vidl. Metadata objects can have these properties: # - title # - artist # - album # - album_artist # - track_number # - track_count # - year # - comment # - lyrics # - composer # md: # Same as "md", except this will never include smart metadata (artist/title # parsed from title). # video_info: # Object containing metadata from youtube-dl about the current video. # playlist_info: # An object containing playlist metadata from youtube-dl. # If the URL isn't a playlist, playlist_info is the same as video_info. # If the URL is a playlist, it has an "entries" property with video_info objects. # callback: Callback function. Takes a metadata object as argument. # example: # This example cheks if vidl has detected title and artist metadata. If the # title includes "[NCS Release]", it will set the comment metadata to "NCS". # if 'title' in md and 'artist' in md: # if '[NCS Release]' in md['title']: # md['comment'] = 'NCS' return md PK!В vidl/dl.pyimport sys, os, copy import pprint; pprint = pprint.PrettyPrinter(indent=4).pprint import youtube_dl from colorboy import cyan, green from deep_filter import deep_filter from vidl import app, config from vidl.app import log def main(): options = { 'url': '', 'file_format': 'mp3', 'audio_only': True, 'no_md': False, 'no_smart_md': False, 'no_dl': False, 'verbose': False, 'download_folder': config.get_config('download_folder'), 'output_template': config.get_config('output_template'), } if options['download_folder'] == None: log('download_folder config has not been set. Add a download folder to the vidl config file.', error=True) log("Config path:", config.config_path) quit() video_formats = ['mp4'] audio_formats = ['mp3', 'wav', 'm4a'] id3_metadata_formats = ['mp3'] ytdl_output_template = os.path.join(options['download_folder'], options['output_template']) # parse arguments for arg in sys.argv[1:]: if arg in audio_formats: options['audio_only'] = True options['file_format'] = arg elif arg in video_formats: options['audio_only'] = False options['file_format'] = arg elif arg in ['--no-md']: options['no_md'] = True elif arg in ['--no-smart-md']: options['no_smart_md'] = True elif arg in ['--no-dl']: options['no_dl'] = True elif arg in ['-v', '--verbose']: options['verbose'] = True elif '.' in arg: options['url'] = arg else: log('Unknown argument:', arg, error=True) quit() if options['url'] == '': log('No URL provided', error=True) quit() # get info log('Fetching URL info') ytdl_get_info_options = { 'outtmpl': ytdl_output_template, 'quiet': False if options['verbose'] else True, } with youtube_dl.YoutubeDL(ytdl_get_info_options) as ytdl: try: info_result = ytdl.extract_info(options['url'], download=False) except: quit() if options['verbose']: pprint(info_result) # delete None properties/indexes def callback(value): return value != None cleaned_info_result = deep_filter(copy.deepcopy(info_result), callback) # restructure url_info = copy.deepcopy(cleaned_info_result) if 'entries' in cleaned_info_result: videos = cleaned_info_result['entries'] playlist_info = copy.deepcopy(cleaned_info_result) del playlist_info['entries'] else: videos = [cleaned_info_result] playlist_info = {} # generate ytdl arguments ytdl_args = [] if options['audio_only']: ytdl_args += ['-x'] ytdl_args += ['-f', 'best'] ytdl_args += ['--audio-format', options['file_format']] else: ytdl_args += ['-f', 'bestvideo+bestaudio'] ytdl_args += ['--recode-video', options['file_format']] ytdl_args += ['--audio-quality', '0'] ytdl_args += ['-o', ytdl_output_template] if options['file_format'] in ['mp3', 'm4a', 'mp4']: ytdl_args += ['--embed-thumbnail'] if not options['verbose']: ytdl_args += ['--quiet'] # ytdl_args += [options['url']] video_index = -1 for video in videos: video_index += 0 try: filename = ytdl.prepare_filename(video) except: quit() filename_split = filename.split('.') filename_split[len(filename_split)-1] = options['file_format'] filename = '.'.join(filename_split) if options['verbose']: log(green('youtube-dl command:'), 'youtube-dl', ' '.join(ytdl_args+[video['webpage_url']])) if options['no_dl']: continue log('Downloading') try: youtube_dl.main(ytdl_args+[video['webpage_url']]) except: pass log('Saved as', filename) if options['file_format'] in id3_metadata_formats and not options['no_md']: log('Adding metadata to file') # get artist/title from title parsed_title = {} if not options['no_smart_md']: if 'title' in video and video['title'].count(' - ') == 1: split_title = video['title'].split(' - ') parsed_title['artist'] = split_title[0] parsed_title['title'] = split_title[1] md = {} playlist = True if len(videos) > 1 else False # title if 'title' in parsed_title: smart_title = True else: smart_title = False if 'title' in video: md['title'] = video['title'] elif 'track' in video: md['title'] = video['track'] # artist if 'artist' in parsed_title: smart_artist = True else: smart_artist = False if 'uploader' in video: md['artist'] = video['uploader'] elif 'artist' in video: md['artist'] = video['artist'] if playlist: #album if 'title' in playlist_info: md['album'] = playlist_info['title'] elif 'playlist_title' in video: md['album'] = video['playlist_title'] elif 'playlist' in video and type(video['playlist']) == str: md['album'] = video['playlist'] #album_artist if 'uploader' in playlist_info: md['album_artist'] = playlist_info['uploader'] elif 'playlist_uploader' in video: md['album_artist'] = video['playlist_uploader'] # track_number if 'playlist_index' in video: md['track_number'] = video['playlist_index'] else: md['track_number'] = video_index+1 # track_count if 'n_entries' in video: md['track_count'] = video['n_entries'] else: md['track_count'] = len(videos) # year def is_int(number): try: int(number) return True except ValueError: return False if 'release_date' in video and is_int(video['release_date'][:4]): md['year'] = video['release_date'][:4] elif 'publish_date' in video and is_int(video['publish_date'][:4]): md['year'] = video['publish_date'][:4] elif 'upload_date' in video and is_int(video['upload_date'][:4]): md['year'] = video['upload_date'][:4] dumb_md = copy.deepcopy(md) if smart_title: md['title'] = parsed_title['title'] if smart_artist: md['artist'] = parsed_title['artist'] md = config.user_md_parser(md, dumb_md, video, url_info) from vidl import md as md_module md_module.add_metadata(filename, md) log('Done') PK!K vidl/md.pyfrom mutagen.id3 import ID3, Encoding, TIT2, TPE1, TALB, TPE2, TRCK, TCON, TDRC, COMM, USLT, TCOM from pprint import pformat from vidl.app import log def add_metadata(filename, md): tags = ID3(filename) if 'title' in md: tags["TIT2"] = TIT2(text=md['title']) if 'artist' in md: tags["TPE1"] = TPE1(text=md['artist']) if 'album' in md: tags["TALB"] = TALB(text=md['album']) if 'album_artist' in md: tags["TPE2"] = TPE2(text=md['album_artist']) if 'track_number' in md: track_number = str(md['track_number']) if 'track_count' in md: track_number += '/'+str(md['track_count']) tags["TRCK"] = TRCK(encoding=3, text=track_number) if 'genre' in md: tags["TCON"] = TCON(text=md['genre']) if 'year' in md: tags["TDRC"] = TDRC(text=md['year']) if 'comment' in md: tags["COMM"] = COMM(text=md['comment'], lang='eng') if 'lyrics' in md: tags["USLT"] = USLT(text=md['lyrics']) if 'composer' in md: tags["TCOM"] = TCOM(text=md['composer']) for key, value in md.items(): whitespace = ' ' * (10 - len(key)) log(' '+key+':'+whitespace+pformat(value)) tags.save(filename) PK!vidl/version.pyfrom vidl.app import package_name def get_package_version(): import pkg_resources my_version = pkg_resources.get_distribution(package_name).version return my_version PK!H;A%&%vidl-3.4.6.dist-info/entry_points.txtN+I/N.,()*LɱzVy\\PK!HڽTUvidl-3.4.6.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!HV v vidl-3.4.6.dist-info/METADATAYo6_ jmq[ǭk'41l'ŝD\ZUp vhpAhEr83|3oY9I''Um)gi$z# 5kQ >v&.neJaZWN8#2)s#3ZT66c1otFؚ5s5ЗPJ.ʹΦS,^T޼z7+ݘ:VʸnۍvEcu4PW/DR3AU}oW?,U"T5VvMga8:ͥz]fYˢR岁byO_:?>_oxŷOδu3!*ӵ{Ϟć㣧sR4q|m|sOXގRd:ݽs8Q4@\J뙸Tڙz;\9c )xԔNnrU>)b㖐E|?B[!HɔRe2JZowFXԦnTy6 8SHSC**ضCQLpZJO*G^s!L!~ #+)X\H&L,K֙"]Hdg"*Y'6,SG8Ix'UX;Bd )X4=~l[QfUS8:Y1,Py$&b48B6oVaRǹZZrA#mlؖ_{CUa Mj%4uL`9/1J=mX!(C*i?f(*d6safxlt?:eSҕAy# $s=.}~a[@PJ)#cqAYѸ[3\}fG3lHe-aQN`H %l'$)p fpa7d Z!lPp! mfLn%zSn2ܫZ*AVȇz䉛t)(/0~U ̘,D|SaW;ֵ@&J%#޿0H-Јq't2z󉅉2"BJA*xQmpɩ9R zf̮sV FK+8AlEeكٝ ,SiīlH+OT*i&;: O0XdiU6 45b<r[*YY!V(蘟zFXT2 ʼn#8 3ϊ˛7W*rȟƀ;Np;e#̤ 6٧86\+LUQn+*IPZ8@DEk4n>y絬- p\1duwNÍZ٤)/G_HV0f1[jMTl<竛Q·Bh{hܠAm]nt8 ec"TM}|ο\ӹ+a{%oke_kpy?^i!Qx޲ *.~o|HYE ˙Q-}}TE:f hx{#[&EBֵCʵu9z+|o[XRoE @(q:9Xg ybLIJm?@e&&Tэ\թ" Sc:!C 'DD`P'yO50 _x y\EIwTd,bwuđr$Bu"]?mSօͰ[z6C=,cXRiߍ wg2͛M2UEgJ:ErDCSZN,3IJI$إGZn,KxE9TD5]]7) w d|mL4AM_׆u0 yZ/Bgq$R5T.٬EuaR;;#a5 ʺo)oŤʰ G!b+cb.$fE??zM&_->Q,%wYo|ńY\-m\XrZ?όjՖmۿKT`? ߰L Oo8@fH.FѿPK!Ho2:_vidl-3.4.6.dist-info/RECORDmˎ@}? j1 EnP A3;Nһ1zX.VL3\HQ<%SKO_ C=r][<84n)WHUՂ#hא_2͌u]zDm*|OAIH)ǐ[>:)~Oꮝú{Ƈq1BMH/цg-r\~ۮ. VMfB ;34UGR$G6mMfkfpl(Lxk2rQSg "KS|T/PK!L@g66vidl/__init__.pyPK!==dvidl/__main__.pyPK!BcV۵ vidl/app.pyPK!5xx vidl/config.pyPK!õQvidl/default_user_md_parser.pyPK!В vidl/dl.pyPK!K 2vidl/md.pyPK!7vidl/version.pyPK!H;A%&%8vidl-3.4.6.dist-info/entry_points.txtPK!HڽTU9vidl-3.4.6.dist-info/WHEELPK!HV v 9vidl-3.4.6.dist-info/METADATAPK!Ho2:_Fvidl-3.4.6.dist-info/RECORDPK I