├── .gitignore ├── ansible ├── .gitignore ├── Vagrantfile ├── ansible.cfg ├── dbservers.yml ├── env_vars │ ├── base.yml │ ├── production.yml │ └── vagrant.yml ├── local ├── production ├── roles │ ├── base │ │ ├── tasks │ │ │ ├── create_swap_file.yml │ │ │ └── main.yml │ │ └── vars │ │ │ └── main.yml │ ├── celery │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ ├── copy_scripts.yml │ │ │ ├── main.yml │ │ │ └── setup_supervisor.yml │ │ ├── templates │ │ │ ├── celery_start.j2 │ │ │ ├── flower_start.j2 │ │ │ ├── supervisor_celery.conf.j2 │ │ │ └── supervisor_flower.conf.j2 │ │ └── vars │ │ │ └── main.yml │ ├── db │ │ ├── handlers │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── memcached │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ ├── templates │ │ │ └── memcached.conf.j2 │ │ └── vars │ │ │ └── main.yml │ ├── rabbitmq │ │ ├── handlers │ │ │ └── main.yml │ │ └── tasks │ │ │ ├── main.yml │ │ │ ├── setup_users.yml │ │ │ └── setup_vhosts.yml │ └── web │ │ ├── handlers │ │ └── main.yml │ │ ├── tasks │ │ ├── create_users_and_groups.yml │ │ ├── main.yml │ │ ├── set_file_permissions.yml │ │ ├── setup_cron_jobs.yml │ │ ├── setup_django_app.yml │ │ ├── setup_git_repo.yml │ │ ├── setup_nginx.yml │ │ ├── setup_supervisor.yml │ │ └── setup_virtualenv.yml │ │ ├── templates │ │ ├── gunicorn_start.j2 │ │ ├── maintenance_off.html │ │ ├── nginx_site_config.j2 │ │ ├── supervisor_config.j2 │ │ └── virtualenv_postactivate.j2 │ │ └── vars │ │ └── main.yml ├── site.yml ├── vagrant.yml └── webservers.yml ├── django ├── Dockerfile └── requirements.txt ├── docker-compose.yml ├── ipython_config.py ├── manage.py ├── scripts └── deploy_youtubeadl.sh ├── tor-hidden-service ├── Dockerfile ├── get-tor-hostname ├── start-tor └── torrc └── youtubeadl ├── __init__.py ├── apps ├── __init__.py ├── core │ ├── __init__.py │ ├── admin.py │ ├── context_processors.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── utils.py │ └── views.py └── downloader │ ├── __init__.py │ ├── admin.py │ ├── migrations │ ├── 0001_initial.py │ └── __init__.py │ ├── models.py │ ├── tasks.py │ ├── tests.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── celery.py ├── settings ├── __init__.py ├── base.py ├── local.py └── production.py ├── static ├── js │ └── main.js └── vendor │ ├── bootstrap │ ├── config.json │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ └── bootstrap.min.css │ └── js │ │ ├── bootstrap.js │ │ └── bootstrap.min.js │ ├── font-awesome │ ├── css │ │ ├── font-awesome.css │ │ └── font-awesome.min.css │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── less │ │ ├── animated.less │ │ ├── bordered-pulled.less │ │ ├── core.less │ │ ├── fixed-width.less │ │ ├── font-awesome.less │ │ ├── icons.less │ │ ├── larger.less │ │ ├── list.less │ │ ├── mixins.less │ │ ├── path.less │ │ ├── rotated-flipped.less │ │ ├── stacked.less │ │ └── variables.less │ └── scss │ │ ├── _animated.scss │ │ ├── _bordered-pulled.scss │ │ ├── _core.scss │ │ ├── _fixed-width.scss │ │ ├── _icons.scss │ │ ├── _larger.scss │ │ ├── _list.scss │ │ ├── _mixins.scss │ │ ├── _path.scss │ │ ├── _rotated-flipped.scss │ │ ├── _stacked.scss │ │ ├── _variables.scss │ │ └── font-awesome.scss │ └── jquery │ └── jquery.min.js ├── templates ├── 403.html ├── 404.html ├── 500.html ├── base.html └── home.html ├── urls.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | media/ 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | # PyBuilder 58 | target/ 59 | 60 | # PyCharm IDE 61 | .idea 62 | -------------------------------------------------------------------------------- /ansible/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vagrant 3 | vagrant_ansible_inventory_default 4 | -------------------------------------------------------------------------------- /ansible/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | config.vm.box = "precise64" 9 | config.vm.box_url = "http://files.vagrantup.com/precise64.box" 10 | 11 | config.vm.network :private_network, ip: "192.168.33.15" 12 | 13 | config.vm.provider :virtualbox do |vb| 14 | vb.customize ["modifyvm", :id, "--name", "MyCoolApp", "--memory", "512"] 15 | end 16 | 17 | # Shared folder from the host machine to the guest machine. Uncomment the line 18 | # below to enable it. 19 | config.vm.synced_folder "../../youtube-audio-dl", "/webapps/youtube-audio-dl/youtubeadl" 20 | 21 | # Ansible provisioner. 22 | config.vm.provision "ansible" do |ansible| 23 | ansible.playbook = "vagrant.yml" 24 | ansible.host_key_checking = false 25 | ansible.verbose = "v" 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking=False -------------------------------------------------------------------------------- /ansible/dbservers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Provision a {{ application_name }} db server 4 | hosts: dbservers 5 | sudo: yes 6 | sudo_user: root 7 | remote_user: root 8 | vars: 9 | - update_apt_cache: yes 10 | vars_files: 11 | - env_vars/base.yml 12 | - env_vars/{{ env }}.yml 13 | 14 | roles: 15 | - base 16 | - db -------------------------------------------------------------------------------- /ansible/env_vars/base.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | git_repo: https://github.com/jcalazan/youtube-audio-dl.git 4 | 5 | project_name: youtube-audio-dl 6 | application_name: youtubeadl -------------------------------------------------------------------------------- /ansible/env_vars/production.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 64633232656537663937386564333666303265643532313731666234383635356239306463666637 3 | 6633396164393939396635633533376334623464633164640a336331343962326537656530303133 4 | 39316137623434636331333132343862626632353933613766316134313332313536313638623861 5 | 6665353762383033330a623932303837356130323633383666393335343066336365313363663461 6 | 36346566383965623262306664383466613633333932393230313534316335653435633234383264 7 | 33613833663565346637393437386564363331646136313065663065623138626163663566323362 8 | 34653062366665616364383565366562363561333335623531323362373564316531656435623237 9 | 37303337626235306534633761393238373563326432656237666233613166343137623661323662 10 | 32383239393038356130303231306132353436343566303162353966343134653265613362613763 11 | 34656639616133363734306637393261393461633334323833626637663636323238306234336663 12 | 36333735626634663962623736343530353737356564323437633632666133633461383464343366 13 | 31306565373836333732333632346239613032356333363331626537316439336431353065663961 14 | 66313561386666323261616139613734633235323966323561623066373735313531383930626633 15 | 36623662356539636561386439333034373030396130323537376235316437363562393565613232 16 | 37393662663063303063393034343135643665626466613931393637383162343237633331616463 17 | 61346530343466636634303263303637626237383164303938323133353833623263333033313630 18 | 62333835306462663365656138373031366664313661393164323230646264616262323537326438 19 | 33626662353436646563346535663662343063363835633834313165646135393462613739353132 20 | 36643165336663366439326436393435313163316366383434613962353065333232346439356132 21 | 31646330633030333439663030373034383231386631383538313931303764656664353939653834 22 | 66373937316139393061373361396437633132343864316534323665353966346365346464626134 23 | 32626138653734336436613564313739396137636530373230323836376434316631326239616563 24 | 66353465346130613938346237616462636234646436373561343661646330623237366465613864 25 | 34333831663734653131653431666264626465383463383365646161356434373637303933653731 26 | 66313562393031343336353834396638386138356335303966623363646661656239613436626662 27 | 35636237306162626135643263356530373661626538353564363165613633646530353138333462 28 | 37353033383238396164623939613235306538373164316163383536393231353763616163636439 29 | 61393336616332396436393534343630316339303131383332366531323462613962663038656662 30 | 32663038363938313235363165333965393265306461383635313334623435346333373361383766 31 | 65323638333635633836623338346235373637343731646261366331373030323637623739386537 32 | 62393932316363366433356131373038656436383536356433383863396234616164333530383165 33 | 39353232333336326630353234343266313336373335303633656335633839646335396262346366 34 | 61653433326138613263373336353564353064323964383732346261663839343761366631333938 35 | 65616132636135333332333737626463333462386664353738663038326530303164633563626634 36 | 63366133373463373866323633336533653036376166663139373535363438396334303735616138 37 | 33623830363463613637336362343462666634636438656134323264303939396632366234633830 38 | 66643534306130656338303362323565643961333630363464363366343638623763333861626364 39 | 61366637663439623138316339366566383036306633323738313166363538616536396238623830 40 | 64353835633634363330663739346464376637356332363866303364343761303737666334363366 41 | 62666362313166356238396633353561393735663361346262366631633135633737656136343661 42 | 32336233633236323132633762653939333639373032623161383632663033343262613337643236 43 | 65333530623837383964333936623731323932333634656666623935666566626365303039616539 44 | 37306631306538373363373131346139343832666161346631316438313365363438333634386636 45 | 36643564353031323132646364373239343035623464616435613164663931303164656463626530 46 | 32326465303832323961663066633938323332353961633665376133613234323430373065353231 47 | 63366333666333633165313932383465376237333365646231656438303930316231333666303065 48 | 38343764613135383261613439386435313731303565373062343630353038386235363931353661 49 | 65373537643537636638373735333563383631626137623665363765366436316137373533623836 50 | 65633063356231333730313765663462373662353938383531306161666666383636396634336663 51 | 31633262323064366539633334386564623164306564636166633736623436623533663435323363 52 | 64636662336136653933363435353033373965363038323336396130633538663039336632373035 53 | 65383465646638303739613338346134633437306464363735393961316531386534386335656632 54 | 65323435336630376234373638656139633733393464383639323737323030353836396166626163 55 | 34333462663634383734393061663061353230396131633663353963393936613534636364326532 56 | 61633739383361646531303136383437333063656334656463323362386562353161623837646232 57 | 31343464346464393562313463613161653065383663373363336462333135623464643635636336 58 | 34663234363463393935666530636463336430373031643733656230613965326165343939303937 59 | 64316539323932346536646530666463343964636136643061353538313239656239323566373833 60 | 65643463316363336635303039366530333037383131396538356230303534653838326561373361 61 | 34663563376233326433623837323835366432666539313638393630626362623866343065373265 62 | 31623664343331373733613164653663623137656333353961333636666534616638653163326162 63 | 66303937366565643633313662663139336639373231313230353531313731326230313663336237 64 | 32343362316336613738333637316236363035383330323239653037646639303331316634613736 65 | 33616136386165646634333131396564313331383032386665633233643534383466663362323234 66 | 61376436646364653832663464393037393833633033353132646139383338373563613334626133 67 | 63333765613737343630633834616462346236303264656235643663303964313438663361353231 68 | 34666261303661393533623534353631326432373263646331656662363731346136373638633138 69 | 38343164376463656638323534333734303662333865343064363663386534363866336565396234 70 | 31626265303637376564656331623363643734363936616233666361653236386438623630613562 71 | 38613439373834613730393065343139316339323463306234393336353164356263616564643730 72 | 33356633333230333761306633333466396163363035633564306131386537393035393963633934 73 | 61323730316331376534363865663438343331386434383665383033626132306163343730373666 74 | 61656265393834373339646664313238613739386537306663383731646238666463663365333462 75 | 38316664326238326135353631623438393538643833666464333039323561353766356132633633 76 | 63633463393661316661336634363534616665336530363766313661616439333334643937383466 77 | 34643064643437336335396237633337316637366562663237636466353839373865313964336539 78 | 66336239306632633931633365323239383934346461363462323739333266316361633137356534 79 | 65633935343430393131363333663364336564333838353939666361346366323561313233346439 80 | 38666539346162326536346634303233376566313762363831396437326530633031316163656433 81 | 32653531313966653539333364386431343334366637353938326662343339373233613339643166 82 | 65396335326330396163383434353934303632336532373663666463343539323435623138353961 83 | 66653735613765656537346238633530623732656439626463356334633131326535653734313938 84 | 64626432316539383430373733366130656335346636613061383536656634366231643437383339 85 | 36613436613765616564613435333833303661636663393961663839336638303039313837623564 86 | 33386263366566336565323235646330366566383237353032393131663363303032376666323731 87 | 61646630356435656565313865653464306561636139323137313561396537666332386565643861 88 | 30373639323462323464363463316532333132356364633134353364343865363464326139326532 89 | 30396336343864613161666536613362623931643763393664666561366638396233373364666130 90 | 36316437653834383862616338366264363936373836383235373434353930323232363037613964 91 | 32656666303166356335646139336435313136646531356362653139356664373233643734326466 92 | 65613363336534323538613865326165663333646137613365316263383635316561663038343966 93 | 37303637613433633863356263643537613639393533343738663266306263383565373233376437 94 | 65303662373135336239356239386135336564653535333066643030316233633038653830313565 95 | 39666661636531646638613262623833323335373065333134646466653732663964383336656365 96 | 32616466346362393935363965633862646130303561386139396665356162333863633263326233 97 | 35363233643966353034386439303262366539613331306436383261366635626535663432613730 98 | 62643431333461616533376131353933616663393633653961376263373031316433316337336637 99 | 31623163633863363462306132343862636634383530383432343432653937623436306636336562 100 | 61326163633862396132383134336666336235653962386162616363393730616134383431346466 101 | 37633736326338313733363234363838383339343236616631303833643138663464653534313835 102 | 30393665613938383962336231626666383962383638663065363834643265616262333330393136 103 | 64663136343433336265646566326637616431653636376137343338313531373363643833333733 104 | 33343430653930656431353632633737333630333235653435373462363438643935303739383330 105 | 32636432646461363066653630613437306265323031656165666663393234396236356165643064 106 | 34643164383938613134383834356331386637373066316363646336383233373031666561636337 107 | 38306536663063626639306436343033313364306536396138343061613765336536346564326130 108 | 31323230313033323532663235313833356561356265376136393537663436376533393232343164 109 | 61386235313665623964646431313234643132663730376266313632306630623932663839306135 110 | 38366333316566643161613166383638356462343036656461383961326365383065326337366539 111 | 37313032336438643961346464383763616238393936366331623561313864333363376163616336 112 | 35333362646266656235333365386165373638393866323762366339326366353066303230623036 113 | 36326161613463643338303236326236613864613664326637613563313839653135383661613133 114 | 33313038376234636630333232623133306366386466663066326466313465626562663365326334 115 | 33333933356537386338393832343733396633663236386134643163393736363030643331393932 116 | 61353864333638333433323436633766653764386332633461616362373063646335303839643233 117 | 34646436626333323834653239353031396366653238383865343933333733313663326263636130 118 | 63346438626566393834383737376464363664613130663330663566616263393832623061633436 119 | 65353431373238623532323465333462666165626435393462663963353761636634336430653762 120 | 63366439366261333432393830636461356465313035303135333333353065303062363761663439 121 | 30616261396363393732353161396564306238316632306131633534326461316366363566333662 122 | 34303833323464366336326536623866396163336565363436633838353235663837386632326136 123 | 30356637313839303131353065626533656332636534386437643434333332386138343837643864 124 | 66313634306636636331626532396163343361633434663034303561323361386436633639636439 125 | 32653464303065393963373862396236373232343336306332643837653939323634663039323831 126 | 31623131383361323963336339383564353336643837373539653939373130376430613765303831 127 | 65333736636434626663323935366333356161363132366435303765346333633536666334303930 128 | 66343537383139376137626630633531663436386363363539306536633638333865363636643364 129 | 36313937363633623964653330646136383132333933333961333131636361633161306436653936 130 | 61353535306234633938653462316233383434623430316138303964343732663432303035363735 131 | 66623266343336386365386563393734636461343666326635613737333130303764376338653635 132 | 31616637376465646461316138653530313536633262306166316636633837663632376234653866 133 | 30363832663763353435623430633131353538333731353835303239373363316465353136323962 134 | 61376535613036303938653236393338613737326564333339356134653962316563323333623164 135 | 64643333633030313838353839343632323631663339376135386338316531363931356262393362 136 | 33616434323030303436313035373834313565363637326266363461646435373032353765653638 137 | 34646136323765636436353762636138306361373461396663353766643465333461383738326437 138 | 38386561626562363462346365643833393265643439653861393662643366316332353032633839 139 | 36386262333036656132623038333364653932653034663332356436363637653636353764623136 140 | 35386366343638346466316437663762396331393539613136396635363862333037663132623765 141 | 30323261306633633463376135303861396430656662616339373364653666646130633065636162 142 | 65316236643635386262663431363266633936663733313231386431653436393330616538316333 143 | 63396266653730653335393836663064363838346632376636383233383261366638346532633264 144 | 33663635343662633030396534383761623463353936623165663361633939636336366334303532 145 | 35386435643939396233643837363363353066373633396464636231656264623938623134393666 146 | 30343739643536343363393536626135613733343061666539643262326237326631653736633939 147 | 34383861393364656461356237376530653830396666663764306630633035333337343561343531 148 | 64383538393738303865366466383834356263323537646364306633646161636536366235306465 149 | 33613066653365376430383136396232636332303034376235656161636434353836623363633033 150 | 61316334306631623866643730376565336262333535656364393430313838306163653561396337 151 | 61343665333931306136663635356239633836333061376361316230346232366434303665663839 152 | 32613636383333653336366563633231613935383737383837613031396530663636313736666666 153 | 31363738396634393731656434366664323666323439393865323130653430313532363065663537 154 | 33376139353231653438393565393037626631313539653237313338336161633034643638333363 155 | 37313866393064366435373734663636666561333734663138373164333637313632346666366630 156 | 39373139323466316531666464373065343339376532353931396364353134343035316165643435 157 | 37373936393065646136613037353961623131333835643438383537613366363034623638653635 158 | 61323637393366613665623066613531393630316262386366343262393061353564643732393066 159 | 31323239616231663730636231353334326263346163373833396263323838613438323430396264 160 | 65313433623638306634363038353365626263626335643436656462373631386135643735396236 161 | 36663966633634386339393036373435313636353031333566393861353230336235303666663539 162 | 66613862366332653338643964363233326633363738353930363563663130373936343866306135 163 | 32663335653764326433633637636333386535623065336561306330313536623365366431613235 164 | 63323733343839313033636564343635393935393366363661373866366639373365363636643030 165 | 64656534633762646661313938363534316535356564383465383761373435363762323261363338 166 | 33373233383339373466643633653730386233363064336531336363343165643962633336376337 167 | 64356337393762376236323239393730373864356237666533353366306361313330363938373361 168 | 30313330383931666562666337643535613764356366306430363634313435653066313233616436 169 | 33373238376266303537396463316430646261666466393964356331636133303934383264656633 170 | 30346433343266346333393264633266396439616433303931643536643361643130343761366163 171 | 34356362646136313537336662623666353762396238396432646162386634353161396330666533 172 | 38393839633861343966623731393938306163393665643138386463373061393236626233616563 173 | 64303237643934313436326663663130386465396163393931653836383332396364303464396265 174 | 66313536633661326439373938373464613838336537313533353637316266393362643764353966 175 | 34396266386161303830333339363933373438633538626638646636343963373330313635643336 176 | 38386637363464656537383139376633653439353831366238333138373161306533653039666464 177 | 64643832613232356430376438383763346339366263623164656538353534376137323636623164 178 | 37633239646566623431653237653338383636343331343736386332626633383962366439653361 179 | 63333565313436626365616466386637353039396338323331343664393031656235383336636138 180 | 38353439316564323437396563326363623061333531393061346463306433373530396630343536 181 | 31313437663938386537303031366233346162653462303563613865653532386464366666636265 182 | 32666263373262373235663964656464666463626132663038343461336230393161376133616663 183 | 61323236393764663133653064323865306564633066326430323738616539316539363964343764 184 | 37353931393062363764343732666631636535373462333262363663653563383064306462336438 185 | 32616263623835633937373966393538393734353866366362636630376538613766343032353661 186 | 34383365323434666532313062343334626139393762656631353566336161363066643839363565 187 | 39656634363439653233633130613434646536353634633135363634376363356265383163356530 188 | 34323366313664663634363261613836616239383962396532316139303330333634343364623431 189 | 32643865333863393362313566303961383337383835663666303165386436373461613038336138 190 | 35383632346139646631336162306664323932636466653333353938393438336563353962353566 191 | 63326461316563633866653562316365316239326461386566336430323962303966636632636130 192 | 37643939373030343035326134373662363235623436393839653263636137333531653630363334 193 | 38653430643731346363613632393330396336663763376431343833376561306136643465316533 194 | 34376639656538636535316362323634366634303630313464383062323766666465663066316363 195 | 66343961356161663864373362316135306133306332323236333966396536623630353163646333 196 | 39633339626665313930626464316338633935316535393937356339636435386465363838386331 197 | 35623933626239636435656533323532643230623634393762666434303761616562326165626331 198 | 36356537326264373662313039633463626534626432333436653532336462623365633565633233 199 | 66363835623766663838663365643738393533663038323461383838323636376433386135643966 200 | 31336462353162646432333735383237623734333134386663616234316466353837343336386233 201 | 65356239353639626631653637313461316432666665616364656466376638313964393364363164 202 | 38376134326666636635633661383232313862386435663464356434386536633966376139643634 203 | 66623665633932346661636637323831633665396537646163383938626363333263623063323162 204 | 66376139356665313462393563626532363065343238306432613131663739643236336337653932 205 | 63306133353166633762356534313233616664383230316234393137363230326337666561643830 206 | 66643761633934326164653066633332633935616238643238306139333635313433333265666464 207 | 64396432653732333963326339653766313834376637373930313461636663393966613133326237 208 | 66646231393737646336366439366634663235303365626461316532353766653566323066626239 209 | 38356330363062336662373166633261373534626534303961623933666634323064343364663936 210 | 31313265393730323536343036303164393265343062643331386363386661666662343166343932 211 | 30653633393262643537376366643638613039343936353764613132333132346165323662356135 212 | 65653831623536663238616561313661393562383836343237653637633930303063333331313933 213 | 30306435316139386663383064323138303862613431313731313462633263613531356134323331 214 | 63393634653334393966373137653033613232383333383262393731393137643937636235353137 215 | 38353437353434363337623231333132373939323537633033393161333135393133653931613237 216 | 39623434346663346131623361366538313433353036643664633466386238393536316638623365 217 | 66353332313265386137636130316430363038366335383761363330663036663534623435396166 218 | 39393234333762353066613363393230656465613231313563623466366631333039366233343865 219 | 35636136383730336663633137386264323363383937366130643765396233393761623232663739 220 | 61656130393061636161663037303061366561373864353963303265333339643035323433386334 221 | 38386534313037333665396563663864373831356361353038393735623161643563666534643835 222 | 36643432653635666263616439666234656664343966653033343863316462666234643161373938 223 | 37383332386463373737353865333963636130346136633466623634343565346536336634356534 224 | 38316363316164393261663133653232306530323566623365333361626535303930353461393235 225 | 64366465633132636233343530323935303933623062643331383832316335613863653665396336 226 | 62663138653931653165363935633630343834393261633065313538366332353838366335316337 227 | 66393632346437653066636263633630613963316331656630653334393638653664366265643530 228 | 63653463666533613863393632393163353533616364643166353762656630303335633563336133 229 | 63386434373731366463306361613163396465626566303935643165656330363137303664616538 230 | 37663133613561656333363436343039313131323439346231373638383239636534353430336661 231 | 34656436333336336362306263383037653337643530653330616337633235323636616135383865 232 | 65666264373966613536323864653330396561653661623330613731373230613061636534323466 233 | 64336465663636396464383634323964363966356465616664613066353730626466383534383864 234 | 31363663386262316165626338343365343565303339386630386261656166613336346139326635 235 | 36383939323162376432326135323230373335316139623432376434363737633238613734623831 236 | 36396431343864396536626163393733616366333364346638376139383364646235313665613963 237 | 37326531333663353065643135333336343831336361353933666230396162633337343865663337 238 | 33323265613737613364643432313961353364386636333961653636386463316133666439393331 239 | 35663333306661343361323737353638626539353963616330373036353433393334363437623261 240 | 64653838396263636166623431383964633634353433383637613565393739336131636532636535 241 | 32316436363633353261303963626632653332383233626639393362303337643638306231633861 242 | 38393832643561623938666566623436373736346565663038313261343534623766393835623230 243 | 33333637366133636639636339383930323137333630633431363365343062646566336435643830 244 | 613331383632356166626332366431663664 245 | -------------------------------------------------------------------------------- /ansible/env_vars/vagrant.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Git settings. 4 | setup_git_repo: no 5 | git_branch: master 6 | 7 | 8 | # Database settings. 9 | db_user: "{{ application_name }}" 10 | db_name: "{{ application_name }}" 11 | db_password: password 12 | 13 | 14 | # Gunicorn settings. For the number of workers, a good rule to follow is 15 | # 2 x number of CPUs + 1 16 | gunicorn_num_workers: 3 17 | 18 | # Setting this to 1 will restart the Gunicorn process each time 19 | # you make a request, basically reloading the code. Very handy 20 | # when developing. Set to 0 for unlimited requests (default). 21 | gunicorn_max_requests: 0 22 | 23 | 24 | # Nginx settings. 25 | nginx_server_name: "{{ inventory_hostname }}" 26 | ssl_src_dir: ssl_self_signed 27 | ssl_dest_dir: /etc/ssl 28 | ssl_key_password: password 29 | 30 | 31 | # RabbitMQ settings. 32 | rabbitmq_server_name: "{{ inventory_hostname }}" 33 | 34 | rabbitmq_admin_user: admin 35 | rabbitmq_admin_password: password 36 | 37 | rabbitmq_application_vhost: "{{ application_name }}" 38 | rabbitmq_application_user: "{{ application_name }}" 39 | rabbitmq_application_password: password 40 | 41 | 42 | # Celery settings. 43 | celery_num_workers: 2 44 | flower_admin_password: password 45 | 46 | 47 | # Application settings. 48 | django_settings_file: "{{ application_name }}.settings.local" 49 | django_secret_key: "akr2icmg1n8%z^3fe3c+)5d0(t^cy-2_25rrl35a7@!scna^1#" 50 | 51 | broker_url: "amqp://{{ rabbitmq_application_user }}:{{ rabbitmq_application_password }}@localhost/{{ rabbitmq_application_vhost }}" 52 | 53 | run_django_db_migrations: yes 54 | run_django_collectstatic: yes 55 | 56 | 57 | # SSL certs settings. 58 | ssl_dest_dir: /etc/ssl 59 | 60 | ssl_crt: | 61 | -----BEGIN CERTIFICATE----- 62 | MIIDjTCCAnWgAwIBAgIJAMazeCBLPAeCMA0GCSqGSIb3DQEBCwUAMF0xCzAJBgNV 63 | BAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 64 | aWRnaXRzIFB0eSBMdGQxFjAUBgNVBAMMDSouZXhhbXBsZS5jb20wHhcNMTQwNjE4 65 | MjI0MjU0WhcNMjQwNjE1MjI0MjU0WjBdMQswCQYDVQQGEwJVUzETMBEGA1UECAwK 66 | U29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRYw 67 | FAYDVQQDDA0qLmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 68 | CgKCAQEAuTyvpgw5iC7vHGr9cpCa1yVW3rTc/81PWqMPL3lKmV4IzHd7+50QKOFC 69 | QE6nfhYS+jVM/3dk8DQgaaTdo1BVF9kT/p1SQE2aE4AFHfPKXP1M+MFBoqK6uuje 70 | jns4sItZg5yj6QTyTBNOHkaXeCObYpAnp+HokqT5Nmrr/uzPZc7jNZ41ehM2mL3n 71 | J7Zgl40nQj4UqFLsXmlG4g6DKEKilRIpJClLdRm6lydgdVi2HApE4adkDOcIXXY6 72 | 87Y/LFoPFrgpztWv/O0/oJguV7LHttjS9b6LzjF8k7rWqCT4muHy1fYcJXLIOaUr 73 | WEru9FQegppwIo6tQjoZuy09lLOztQIDAQABo1AwTjAdBgNVHQ4EFgQUbYkM8ZEK 74 | +5GbEw1Bx7DHBqbJ6G0wHwYDVR0jBBgwFoAUbYkM8ZEK+5GbEw1Bx7DHBqbJ6G0w 75 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAuBfBeTXulSb9dNCMvxzN 76 | XAEIxw1s+Rdlup8v25Otj+5mUAq0LNWDmNID7gA4NcJ/GiCzA7+ojE6g6vFj4weU 77 | LPHnAKN+XbyGyG/Jeqk30qWdWw/3E3bPrJBt4dTT9QwojasHbkIP0/qtzy3UFW9V 78 | 32wScQV1aMXQtip5o5ANidXuTOgz2u+DCWWZmSMb0r1vn8/XV/X39n4iC/7H9TrQ 79 | DFrdr8kCgVJ7UxXDFvx7g6HxDfh0UudkjSxH5L4aQEmdXQAZjDY015lq2mp58LjE 80 | /2gucKfZufEhaPL159OVt5fctnRrFySyDmzQX5z2oVWTrTE83huTRc87XQK1QcYH 81 | +w== 82 | -----END CERTIFICATE----- 83 | 84 | ssl_key: | 85 | -----BEGIN PRIVATE KEY----- 86 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5PK+mDDmILu8c 87 | av1ykJrXJVbetNz/zU9aow8veUqZXgjMd3v7nRAo4UJATqd+FhL6NUz/d2TwNCBp 88 | pN2jUFUX2RP+nVJATZoTgAUd88pc/Uz4wUGiorq66N6Oeziwi1mDnKPpBPJME04e 89 | Rpd4I5tikCen4eiSpPk2auv+7M9lzuM1njV6EzaYvecntmCXjSdCPhSoUuxeaUbi 90 | DoMoQqKVEikkKUt1GbqXJ2B1WLYcCkThp2QM5whddjrztj8sWg8WuCnO1a/87T+g 91 | mC5Xsse22NL1vovOMXyTutaoJPia4fLV9hwlcsg5pStYSu70VB6CmnAijq1COhm7 92 | LT2Us7O1AgMBAAECggEAHjQU9+A6aUgt2NZhKRMHDFmcRof7GQKjE8ZOrZD7ZvJ8 93 | QMqivq4nemLwaIfqq5Zx1bZnLaiMHtaBCnjFYuGwXkkZB4UjajS9ELzpGK8tqefr 94 | awwn5ZrfE6bw0w6oebDfEaSy3UXfNCRZsnoULJSxu2qB7M/bGj4oHIVmoZR/ZLwG 95 | LRItoK0wB2Bok5bmF9mAfW9EkoCOwkQP6uEynJ7z1f03teGJhSWu8xfjJv7XkLxG 96 | vIFbSSeGKZdEHK5fz5nHyr8RCkF5DNPagrs5gz0o1clDeG3VcQhCUgE1y8Ly8iSR 97 | LvDhk6KcfTscvdvxXNKPYPtqpFZfnJTt4qVM5rGaAQKBgQDd94EiefLVPxei/gqB 98 | cfhmtsDAdeQRqdJedg/PE4KDzlAqzOY/Bd4klVASwA0yxXepqf2ZRT1vhMkjFmSM 99 | kproc+gzKFQxxUSmJ//Jl2csaR3+UTAiBiv3b5BuCptG9WaHxRwx/jwHyslANrHP 100 | D+33ybLTOjlMbktrwIkqR1JzwQKBgQDVo31EYOQ7fDt4DYLRVHMoKJpErbwDH9jt 101 | hqfloKrV+UfrAWtn4mMcrCb6LKWTiv5T52gPbV/sSM5+7U97W3goffJShCPKobd3 102 | 6vhn2rowPjQd3IkZEygUuw4xTOHWUl1MB6Z9zyUd3hM1/wlO1CiHtaCpnEVgX66b 103 | yEF7IVXs9QKBgBIrscmVvBhS6udv7oI8Rz55VXwr6ni7szoCZjbofPW3TP7D+VFN 104 | dKsAAicWy73NRoeAH/+NGINplmGl8qNDWSUQYADYG1Rbtsv3WEwzdcG/9TGdidgv 105 | Myg1XNh1S9LaQgN5Ul6RVm643hLAp3uw7SUswNPj307vdIMkptXsMsbBAoGAdfuI 106 | 9ZdQ0+0i5oUHptUtl5L8x0rvFwaihWKlHHJjhjHZ3tX02/UxaSdFi0nW0ymilPGq 107 | DUMJA3Od3ojuKSD1td8AUUO6hHBU4zv3nVs1Eel4XLlrWVaz/ubiyqU732GzNobP 108 | EpGwXNNE5sAHAuq1y2Sp6qFryvJseonYZ8icLHUCgYBHSWGgjhOA7KyZdcTdmpu+ 109 | fGAwN2Coj2l+PKvQEJkX96IOboHDkd7N8mwyfjb30FcD1uVkEwjp55PpHmYph8xD 110 | GhN+yDgmf5OjDia0mFhv1nA/Nh16rQZd2NV7qYx0npPabOfud/8imdgOFr78HCjS 111 | MoqV6aeRY/7uwLiviaIUJg== 112 | -----END PRIVATE KEY----- -------------------------------------------------------------------------------- /ansible/local: -------------------------------------------------------------------------------- 1 | [webservers] 2 | 192.168.33.15 3 | 4 | [dbservers] 5 | 192.168.33.15 6 | 7 | [memcachedservers] 8 | 192.168.33.15 9 | -------------------------------------------------------------------------------- /ansible/production: -------------------------------------------------------------------------------- 1 | [all:vars] 2 | env=production 3 | 4 | [webservers] 5 | www.youtubeadl.com 6 | 7 | [dbservers] 8 | www.youtubeadl.com -------------------------------------------------------------------------------- /ansible/roles/base/tasks/create_swap_file.yml: -------------------------------------------------------------------------------- 1 | - name: Create swap file 2 | command: dd if=/dev/zero of={{ swap_file_path }} bs=1024 count={{ swap_file_size_kb }}k 3 | creates="{{ swap_file_path }}" 4 | tags: swap.file.create 5 | 6 | - name: Change swap file permissions 7 | file: path="{{ swap_file_path }}" 8 | owner=root 9 | group=root 10 | mode=0600 11 | tags: swap.file.permissions 12 | 13 | - name: Check swap file type 14 | command: file {{ swap_file_path }} 15 | register: swapfile 16 | tags: swap.file.mkswap 17 | 18 | - name: Make swap file 19 | command: "sudo mkswap {{ swap_file_path }}" 20 | when: swapfile.stdout.find('swap file') == -1 21 | tags: swap.file.mkswap 22 | 23 | - name: Write swap entry in fstab 24 | mount: name=none 25 | src={{ swap_file_path }} 26 | fstype=swap 27 | opts=sw 28 | passno=0 29 | dump=0 30 | state=present 31 | tags: swap.fstab 32 | 33 | - name: Mount swap 34 | command: "swapon {{ swap_file_path }}" 35 | when: ansible_swaptotal_mb < 1 36 | tags: swap.file.swapon -------------------------------------------------------------------------------- /ansible/roles/base/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - include: create_swap_file.yml 4 | when: create_swap_file 5 | tags: swap 6 | 7 | - name: Ensure bash, OpenSSl, and libssl are the latest versions 8 | apt: name={{ item }} update_cache={{ update_apt_cache }} state=latest 9 | with_items: 10 | - bash 11 | - openssl 12 | - libssl-dev 13 | - libssl-doc 14 | tags: packages 15 | 16 | - name: Install base packages 17 | apt: name={{ item }} update_cache={{ update_apt_cache }} force=yes state=installed 18 | with_items: 19 | - build-essential 20 | - ntp 21 | - htop 22 | - git 23 | - libpq-dev 24 | - python-dev 25 | - python-pip 26 | - python-pycurl 27 | - supervisor 28 | tags: packages 29 | 30 | - name: Install newrelic 31 | pip: name=newrelic 32 | tags: packages 33 | 34 | - name: Install virtualenv 35 | pip: name=virtualenv 36 | tags: packages 37 | -------------------------------------------------------------------------------- /ansible/roles/base/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | create_swap_file: yes 4 | swap_file_path: /swapfile 5 | swap_file_size_kb: 512 -------------------------------------------------------------------------------- /ansible/roles/celery/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: restart {{ celery_application_name }} 4 | supervisorctl: name={{ celery_application_name }} state=restarted 5 | 6 | - name: restart flower 7 | supervisorctl: name=flower state=restarted -------------------------------------------------------------------------------- /ansible/roles/celery/tasks/copy_scripts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Create the folder for the celery scripts 4 | file: path={{ celery_scripts_dir }} 5 | owner={{ celery_user }} 6 | group={{ celery_group }} 7 | mode=0774 8 | state=directory 9 | tags: 10 | - celery 11 | 12 | - name: Create the {{ celery_application_name }} script file 13 | template: src={{ celery_template_file }} 14 | dest={{ celery_scripts_dir }}/{{ celery_application_name }}_start 15 | owner={{ celery_user }} 16 | group={{ celery_group }} 17 | mode=0755 18 | tags: 19 | - celery 20 | 21 | - name: Create the flower script file 22 | template: src=flower_start.j2 23 | dest={{ celery_scripts_dir }}/flower_start 24 | owner={{ celery_user }} 25 | group={{ celery_group }} 26 | mode=0755 27 | tags: 28 | - celery 29 | - flower -------------------------------------------------------------------------------- /ansible/roles/celery/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - include: copy_scripts.yml 4 | 5 | - include: setup_supervisor.yml 6 | 7 | - name: Check running applications managed by supervisor 8 | shell: supervisorctl status 9 | register: supervisor_applications 10 | tags: 11 | - celery 12 | - deploy 13 | 14 | - name: Restart the {{ celery_application_name }} app 15 | supervisorctl: name={{ celery_application_name }} state=restarted 16 | when: supervisor_applications.stdout.find('{{ celery_application_name }}') != -1 17 | tags: 18 | - celery 19 | - deploy 20 | 21 | - name: Restart the flower app 22 | supervisorctl: name=flower state=restarted 23 | when: supervisor_applications.stdout.find('flower') != -1 24 | tags: 25 | - flower 26 | - deploy -------------------------------------------------------------------------------- /ansible/roles/celery/tasks/setup_supervisor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Install Supervisor 4 | apt: name=supervisor update_cache={{ update_apt_cache }} state=installed 5 | tags: 6 | - celery 7 | - flower 8 | 9 | - name: Ensure supervisor service is started 10 | service: name=supervisor state=started enabled=yes 11 | tags: 12 | - celery 13 | - flower 14 | 15 | - name: Create the Supervisor config file for {{ celery_application_name }} 16 | template: src=supervisor_{{ celery_application_name }}.conf.j2 17 | dest=/etc/supervisor/conf.d/{{ celery_application_name }}.conf 18 | tags: 19 | - celery 20 | 21 | - name: Create the Supervisor config file for flower 22 | template: src=supervisor_flower.conf.j2 23 | dest=/etc/supervisor/conf.d/flower.conf 24 | tags: 25 | - flower 26 | 27 | - name: Create the {{ celery_application_name }} log directory 28 | file: path={{ celery_log_dir }} 29 | owner={{ celery_user }} 30 | group={{ celery_group }} 31 | state=directory 32 | tags: 33 | - celery 34 | - flower 35 | 36 | - name: Create the {{ celery_application_name }} log file 37 | file: path={{ celery_log_file }} 38 | owner={{ celery_user }} 39 | group={{ celery_group }} 40 | state=touch 41 | tags: 42 | - celery 43 | 44 | - name: Create the flower log file 45 | file: path={{ flower_log_file }} 46 | owner={{ celery_user }} 47 | group={{ celery_group }} 48 | state=touch 49 | tags: 50 | - flower 51 | 52 | - name: Re-read the Supervisor config files 53 | command: supervisorctl reread 54 | tags: 55 | - celery 56 | - flower 57 | 58 | - name: Update Supervisor to add the apps in the process group 59 | command: supervisorctl update 60 | tags: 61 | - celery 62 | - flower -------------------------------------------------------------------------------- /ansible/roles/celery/templates/celery_start.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DJANGODIR={{ project_path }} 4 | 5 | # Activate the virtual environment. 6 | cd $DJANGODIR 7 | . {{ virtualenv_path }}/bin/activate 8 | . {{ virtualenv_path }}/bin/postactivate 9 | 10 | # Programs meant to be run under supervisor should not daemonize themselves 11 | # (do not use --daemon). 12 | exec celery -A {{ application_name }} worker -E -l info --concurrency={{ celery_num_workers }} 13 | -------------------------------------------------------------------------------- /ansible/roles/celery/templates/flower_start.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DJANGODIR={{ project_path }} 4 | 5 | # Activate the virtual environment. 6 | cd $DJANGODIR 7 | . {{ virtualenv_path }}/bin/activate 8 | . {{ virtualenv_path }}/bin/postactivate 9 | 10 | # Programs meant to be run under supervisor should not daemonize themselves 11 | # (do not use --daemon). 12 | exec celery -A youtubeadl flower --port=5555 --basic_auth=admin:{{ flower_admin_password }} -------------------------------------------------------------------------------- /ansible/roles/celery/templates/supervisor_celery.conf.j2: -------------------------------------------------------------------------------- 1 | [program:{{ celery_application_name }}] 2 | command={{ celery_scripts_dir }}/{{ celery_application_name }}_start 3 | 4 | autostart=true 5 | autorestart=true 6 | 7 | user={{ celery_user }} 8 | 9 | stdout_logfile={{ celery_log_file }} 10 | redirect_stderr = true -------------------------------------------------------------------------------- /ansible/roles/celery/templates/supervisor_flower.conf.j2: -------------------------------------------------------------------------------- 1 | [program:flower] 2 | command={{ celery_scripts_dir }}/flower_start 3 | 4 | autostart=true 5 | autorestart=true 6 | 7 | user={{ celery_user }} 8 | 9 | stdout_logfile={{ flower_log_file }} 10 | redirect_stderr = true -------------------------------------------------------------------------------- /ansible/roles/celery/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | server_root_dir: /webapps 4 | 5 | virtualenv_path: "/webapps/{{ application_name }}" 6 | project_path: "{{ virtualenv_path }}/{{ project_name }}" 7 | 8 | celery_user: "{{ application_name }}" 9 | celery_group: webapps 10 | 11 | celery_application_name: celery 12 | celery_scripts_dir: "{{ virtualenv_path }}/scripts/celery" 13 | celery_template_file: "{{ celery_application_name }}_start.j2" 14 | 15 | celery_log_dir: "{{ virtualenv_path }}/logs" 16 | celery_log_file: "{{ celery_log_dir }}/{{ celery_application_name }}.log" 17 | flower_log_file: "{{ celery_log_dir }}/flower.log" -------------------------------------------------------------------------------- /ansible/roles/db/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: restart postgresql 4 | service: name=postgresql state=restarted enabled=yes 5 | -------------------------------------------------------------------------------- /ansible/roles/db/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Install PostgreSQL 4 | apt: name={{ item }} update_cache={{ update_apt_cache }} state=installed 5 | with_items: 6 | - postgresql 7 | - postgresql-contrib 8 | - python-psycopg2 9 | tags: packages 10 | 11 | - name: Ensure the PostgreSQL service is running 12 | service: name=postgresql state=started enabled=yes 13 | 14 | - name: Ensure database is created 15 | sudo_user: postgres 16 | postgresql_db: name={{ db_name }} 17 | encoding='UTF-8' 18 | lc_collate='en_US.UTF-8' 19 | lc_ctype='en_US.UTF-8' 20 | template='template0' 21 | state=present 22 | 23 | - name: Ensure user has access to the database 24 | sudo_user: postgres 25 | postgresql_user: db={{ db_name }} 26 | name={{ db_user }} 27 | password={{ db_password }} 28 | priv=ALL 29 | state=present 30 | 31 | - name: Ensure user does not have unnecessary privileges 32 | sudo_user: postgres 33 | postgresql_user: name={{ db_user }} 34 | role_attr_flags=NOSUPERUSER,NOCREATEDB 35 | state=present -------------------------------------------------------------------------------- /ansible/roles/memcached/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: restart memcached 4 | service: name=memcached state=restarted enabled=yes 5 | -------------------------------------------------------------------------------- /ansible/roles/memcached/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Install Memcached 4 | apt: name=memcached update_cache={{ update_apt_cache }} state=installed 5 | tags: packages 6 | 7 | - name: Create the Memcached configuration file 8 | template: src=memcached.conf.j2 9 | dest=/etc/memcached.conf 10 | mode=0644 11 | backup=yes 12 | notify: 13 | - restart memcached 14 | 15 | - name: Ensure the Memcached service is running 16 | service: name=memcached state=started enabled=yes -------------------------------------------------------------------------------- /ansible/roles/memcached/templates/memcached.conf.j2: -------------------------------------------------------------------------------- 1 | # 2003 - Jay Bonci 2 | # This configuration file is read by the start-memcached script provided as 3 | # part of the Debian GNU/Linux distribution. 4 | 5 | # Run memcached as a daemon. This command is implied, and is not needed for the 6 | # daemon to run. See the README.Debian that comes with this package for more 7 | # information. 8 | -d 9 | 10 | # Log memcached's output to /var/log/memcached 11 | logfile /var/log/memcached.log 12 | 13 | # Be verbose 14 | # -v 15 | 16 | # Be even more verbose (print client commands as well) 17 | # -vv 18 | 19 | # Start with a cap of 64 megs of memory. It's reasonable, and the daemon default 20 | # Note that the daemon will grow to this size, but does not start out holding this much 21 | # memory 22 | -m {{ memcached_max_memory_mb }} 23 | 24 | # Default connection port is 11211 25 | -p {{ memcached_port }} 26 | 27 | # Run the daemon as root. The start-memcached will default to running as root if no 28 | # -u command is present in this config file 29 | -u {{ memcached_user }} 30 | 31 | # Specify which IP address to listen on. The default is to listen on all IP addresses 32 | # This parameter is one of the only security measures that memcached has, so make sure 33 | # it's listening on a firewalled interface. 34 | -l {{ memcached_listen }} 35 | 36 | # Limit the number of simultaneous incoming connections. The daemon default is 1024 37 | -c {{ memcached_max_connections }} 38 | 39 | # Lock down all paged memory. Consult with the README and homepage before you do this 40 | # -k 41 | 42 | # Return error when memory is exhausted (rather than removing items) 43 | # -M 44 | 45 | # Maximize core file limit 46 | # -r -------------------------------------------------------------------------------- /ansible/roles/memcached/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | memcached_listen: 127.0.0.1 4 | memcached_port: 11211 5 | memcached_user: nobody 6 | memcached_max_memory_mb: 64 7 | memcached_max_connections: 1024 8 | -------------------------------------------------------------------------------- /ansible/roles/rabbitmq/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: restart rabbitmq-server 4 | service: name=rabbitmq-server state=restarted -------------------------------------------------------------------------------- /ansible/roles/rabbitmq/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Add the RabbitMQ public GPG key to the apt repo 4 | apt_key: url=http://www.rabbitmq.com/rabbitmq-signing-key-public.asc 5 | state=present 6 | 7 | - name: Add RabbitMQ to the sources list 8 | apt_repository: repo='deb http://www.rabbitmq.com/debian/ testing main' 9 | update_cache={{ update_apt_cache }} 10 | state=present 11 | 12 | - name: Install RabbitMQ server 13 | apt: name={{ item }} update_cache={{ update_apt_cache }} force=yes state=installed 14 | with_items: 15 | - rabbitmq-server 16 | 17 | - name: Enable the RabbitMQ Management Console 18 | rabbitmq_plugin: names=rabbitmq_management state=enabled 19 | notify: restart rabbitmq-server 20 | 21 | - include: setup_vhosts.yml 22 | 23 | - include: setup_users.yml 24 | 25 | - name: Ensure that the RabbitMQ service is running 26 | service: name=rabbitmq-server state=started enabled=yes -------------------------------------------------------------------------------- /ansible/roles/rabbitmq/tasks/setup_users.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Create default admin user 4 | rabbitmq_user: user={{ rabbitmq_admin_user }} 5 | password={{ rabbitmq_admin_password }} 6 | vhost=/ 7 | tags=administrator 8 | state=present 9 | 10 | - name: Create application user 11 | rabbitmq_user: user={{ rabbitmq_application_user }} 12 | password={{ rabbitmq_application_password }} 13 | vhost={{ rabbitmq_application_vhost }} 14 | configure_priv=.* 15 | read_priv=.* 16 | write_priv=.* 17 | state=present 18 | 19 | - name: Ensure the default 'guest' user doesn't exist 20 | rabbitmq_user: user=guest 21 | state=absent 22 | 23 | -------------------------------------------------------------------------------- /ansible/roles/rabbitmq/tasks/setup_vhosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Create a vhost for the application 4 | rabbitmq_vhost: name={{ rabbitmq_application_vhost }} state=present -------------------------------------------------------------------------------- /ansible/roles/web/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: restart application 4 | supervisorctl: name={{ application_name }} state=restarted 5 | 6 | - name: restart nginx 7 | service: name=nginx state=restarted enabled=yes 8 | 9 | - name: reload nginx 10 | service: name=nginx state=reloaded 11 | -------------------------------------------------------------------------------- /ansible/roles/web/tasks/create_users_and_groups.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Create the application user 4 | user: name={{ gunicorn_user }} state=present 5 | 6 | - name: Create the application group 7 | group: name={{ gunicorn_group }} system=yes state=present 8 | 9 | - name: Add the application user to the application group 10 | user: name={{ gunicorn_user }} group={{ gunicorn_group }} state=present -------------------------------------------------------------------------------- /ansible/roles/web/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Install additional packages 4 | apt: name={{ item }} update_cache={{ update_apt_cache }} force=yes state=installed 5 | with_items: 6 | - ffmpeg 7 | - libavcodec-extra-53 8 | tags: packages 9 | 10 | - include: create_users_and_groups.yml 11 | 12 | - include: setup_virtualenv.yml 13 | tags: virtualenv 14 | 15 | - include: setup_git_repo.yml 16 | tags: deploy 17 | 18 | - include: setup_django_app.yml 19 | tags: deploy 20 | 21 | - include: setup_supervisor.yml 22 | tags: supervisor 23 | 24 | - include: set_file_permissions.yml 25 | tags: deploy 26 | 27 | - include: setup_nginx.yml 28 | tags: nginx 29 | 30 | - include: setup_cron_jobs.yml 31 | tags: cron -------------------------------------------------------------------------------- /ansible/roles/web/tasks/set_file_permissions.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Ensure that the application file permissions are set properly 4 | file: path={{ virtualenv_path }} 5 | recurse=yes 6 | owner={{ gunicorn_user }} 7 | group={{ gunicorn_group }} 8 | state=directory 9 | notify: restart application 10 | -------------------------------------------------------------------------------- /ansible/roles/web/tasks/setup_cron_jobs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Ensure that a daily youtube-dl package update job exists 4 | cron: name="daily youtube-dl update" 5 | special_time=daily 6 | user=root 7 | job=". /webapps/youtubeadl/bin/activate && pip install -U youtube-dl && supervisorctl restart all" 8 | state=present 9 | 10 | - name: Ensure that a daily file cleanup job exists 11 | cron: name="daily file cleanup" 12 | special_time=daily 13 | user=root 14 | job="cd /webapps/youtubeadl/media && rm -rf *.mp3" 15 | state=present -------------------------------------------------------------------------------- /ansible/roles/web/tasks/setup_django_app.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Install packages required by the Django app inside virtualenv 4 | pip: virtualenv={{ virtualenv_path }} requirements={{ requirements_file }} 5 | 6 | - name: Run the Django syncdb command 7 | django_manage: 8 | command: syncdb 9 | app_path: "{{ project_path }}" 10 | virtualenv: "{{ virtualenv_path }}" 11 | settings: "{{ django_settings_file }}" 12 | environment: django_environment 13 | when: run_django_syncdb is defined and run_django_sync_db 14 | tags: django.syncdb 15 | 16 | - name: Run Django database migrations 17 | django_manage: 18 | command: migrate 19 | app_path: "{{ project_path }}" 20 | virtualenv: "{{ virtualenv_path }}" 21 | settings: "{{ django_settings_file }}" 22 | environment: django_environment 23 | when: run_django_db_migrations is defined and run_django_db_migrations 24 | tags: django.migrate 25 | 26 | - name: Run Django collectstatic 27 | django_manage: 28 | command: collectstatic 29 | app_path: "{{ project_path }}" 30 | virtualenv: "{{ virtualenv_path }}" 31 | settings: "{{ django_settings_file }}" 32 | environment: django_environment 33 | when: run_django_collectstatic is defined and run_django_collectstatic 34 | tags: django.collectstatic -------------------------------------------------------------------------------- /ansible/roles/web/tasks/setup_git_repo.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Setup the Git repo 4 | git: repo={{ git_repo }} 5 | version="{{ git_branch }}" 6 | dest={{ project_path }} 7 | accept_hostkey=yes 8 | when: setup_git_repo 9 | tags: git 10 | 11 | - name: Delete all .pyc files 12 | command: find . -name '*.pyc' -delete 13 | args: 14 | chdir: "{{ project_path }}" 15 | tags: git -------------------------------------------------------------------------------- /ansible/roles/web/tasks/setup_nginx.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Install Nginx 4 | apt: name=nginx update_cache={{ update_apt_cache }} state=installed 5 | tags: packages 6 | 7 | - name: Copy the SSL certificate to the remote server 8 | copy: content="{{ ssl_crt }}" dest={{ ssl_dest_dir }}/{{ application_name }}.crt 9 | notify: restart nginx 10 | 11 | - name: Copy the SSL private key to the remote server 12 | copy: content="{{ ssl_key }}" dest={{ ssl_dest_dir }}/{{ application_name }}.key 13 | notify: restart nginx 14 | 15 | - name: Create the Nginx configuration file 16 | template: src=nginx_site_config.j2 17 | dest=/etc/nginx/sites-available/{{ application_name }} 18 | backup=yes 19 | notify: reload nginx 20 | 21 | - name: Ensure that the default site is disabled 22 | command: rm /etc/nginx/sites-enabled/default 23 | removes=/etc/nginx/sites-enabled/default 24 | notify: reload nginx 25 | 26 | - name: Ensure that the application site is enabled 27 | command: ln -s /etc/nginx/sites-available/{{ application_name }} 28 | /etc/nginx/sites-enabled/{{ application_name }} 29 | creates=/etc/nginx/sites-enabled/{{ application_name }} 30 | notify: reload nginx 31 | 32 | - name: Ensure Nginx service is started 33 | service: name=nginx state=started enabled=yes -------------------------------------------------------------------------------- /ansible/roles/web/tasks/setup_supervisor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Create the Supervisor config file 4 | template: src=supervisor_config.j2 5 | dest=/etc/supervisor/conf.d/{{ application_name }}.conf 6 | backup=yes 7 | 8 | - name: Re-read the Supervisor config files 9 | supervisorctl: name={{ application_name }} state=present 10 | 11 | - name: Restart Supervisor 12 | supervisorctl: name={{ application_name }} state=restarted 13 | -------------------------------------------------------------------------------- /ansible/roles/web/tasks/setup_virtualenv.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Create the virtualenv 4 | command: virtualenv {{ virtualenv_path }} --no-site-packages 5 | creates={{ virtualenv_path }}/bin/activate 6 | 7 | - name: Ensure gunicorn is installed 8 | pip: virtualenv={{ virtualenv_path }} name=gunicorn 9 | 10 | - name: Create the Gunicorn script file 11 | template: src=gunicorn_start.j2 12 | dest={{ virtualenv_path }}/bin/gunicorn_start 13 | owner={{ gunicorn_user }} 14 | group={{ gunicorn_group }} 15 | mode=0755 16 | backup=yes 17 | tags: deploy 18 | 19 | - name: Create the application log folder 20 | file: path={{ application_log_dir }} 21 | owner={{ gunicorn_user }} 22 | group={{ gunicorn_group }} 23 | mode=0774 24 | state=directory 25 | 26 | - name: Create the application log file 27 | command: touch {{ application_log_file }} 28 | creates={{ application_log_file }} 29 | 30 | - name: Create the virtualenv postactivate script to set environment variables 31 | template: src=virtualenv_postactivate.j2 32 | dest={{ virtualenv_path }}/bin/postactivate 33 | owner={{ gunicorn_user }} 34 | group={{ gunicorn_group }} 35 | mode=0640 36 | backup=yes 37 | tags: deploy 38 | 39 | - name: Set permission to the application log file 40 | file: path={{ application_log_file }} 41 | owner={{ gunicorn_user }} 42 | group={{ gunicorn_group }} 43 | mode=0664 44 | state=file 45 | 46 | - name: Create the maintenance page 47 | template: src=maintenance_off.html 48 | dest={{ virtualenv_path }}/maintenance_off.html 49 | mode=0664 50 | -------------------------------------------------------------------------------- /ansible/roles/web/templates/gunicorn_start.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | NAME="{{ application_name }}" 4 | DJANGODIR="{{ project_path }}" 5 | SOCKFILE={{ virtualenv_path }}/run/gunicorn.sock 6 | USER={{ gunicorn_user }} 7 | GROUP={{ gunicorn_group }} 8 | NUM_WORKERS={{ gunicorn_num_workers }} 9 | 10 | # Set this to 0 for unlimited requests. During development, you might want to 11 | # set this to 1 to automatically restart the process on each request (i.e. your 12 | # code will be reloaded on every request). 13 | MAX_REQUESTS={{ gunicorn_max_requests }} 14 | 15 | echo "Starting $NAME as `whoami`" 16 | 17 | # Activate the virtual environment. 18 | cd $DJANGODIR 19 | . {{ virtualenv_path }}/bin/activate 20 | 21 | # Set additional environment variables. 22 | . {{ virtualenv_path }}/bin/postactivate 23 | 24 | # Create the run directory if it doesn't exist. 25 | RUNDIR=$(dirname $SOCKFILE) 26 | test -d $RUNDIR || mkdir -p $RUNDIR 27 | 28 | # Programs meant to be run under supervisor should not daemonize themselves 29 | # (do not use --daemon). Also note that the timeout is set to 5 minutes as we're 30 | # currently waiting for the celery task to finish before returning a response, which 31 | # could sometimes take a while. We should remove this later once we fixed the Ajax call 32 | # to check for the task status as this could tie up all the Gunicorn workers if the 33 | # site gets busy. 34 | exec gunicorn \ 35 | --name $NAME \ 36 | --workers $NUM_WORKERS \ 37 | --max-requests $MAX_REQUESTS \ 38 | --timeout 300 \ 39 | --user $USER --group $GROUP \ 40 | --log-level debug \ 41 | --bind unix:$SOCKFILE \ 42 | {{ application_name }}.wsgi 43 | -------------------------------------------------------------------------------- /ansible/roles/web/templates/maintenance_off.html: -------------------------------------------------------------------------------- 1 | 2 | Site Maintenance 3 | 11 | 12 |
13 |

We’ll be back soon!

14 |
15 |

Sorry for the inconvenience but we’re performing some maintenance at the moment. If you need to you can always contact us, otherwise we’ll be back online shortly!

16 |

— The Team

17 |
18 |
19 | -------------------------------------------------------------------------------- /ansible/roles/web/templates/nginx_site_config.j2: -------------------------------------------------------------------------------- 1 | upstream wsgi_server { 2 | # fail_timeout=0 means we always retry an upstream even if it failed 3 | # to return a good HTTP response (in case the Unicorn master nukes a 4 | # single worker for timing out). 5 | 6 | server unix:{{ virtualenv_path }}/run/gunicorn.sock fail_timeout=0; 7 | } 8 | 9 | server { 10 | listen 80; 11 | server_name {{ nginx_server_name }}; 12 | rewrite ^/admin/ https://$server_name$request_uri? permanent; 13 | 14 | client_max_body_size 4G; 15 | 16 | access_log {{ nginx_access_log_file }}; 17 | error_log {{ nginx_error_log_file }}; 18 | 19 | location /static/ { 20 | alias {{ nginx_static_dir }}; 21 | } 22 | 23 | location /media/ { 24 | alias {{ nginx_media_dir }}; 25 | } 26 | 27 | location / { 28 | if (-f {{ virtualenv_path }}/maintenance_on.html) { 29 | return 503; 30 | } 31 | 32 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 33 | proxy_set_header Host $http_host; 34 | proxy_redirect off; 35 | proxy_read_timeout 300; 36 | 37 | # Try to serve static files from nginx, no point in making an 38 | # *application* server like Unicorn/Rainbows! serve static files. 39 | if (!-f $request_filename) { 40 | proxy_pass http://wsgi_server; 41 | break; 42 | } 43 | } 44 | 45 | # Error pages 46 | error_page 500 502 504 /500.html; 47 | location = /500.html { 48 | root {{ project_path }}/{{ application_name }}/templates/; 49 | } 50 | 51 | error_page 503 /maintenance_on.html; 52 | location = /maintenance_on.html { 53 | root {{ virtualenv_path }}/; 54 | } 55 | } 56 | 57 | server { 58 | listen 443; 59 | server_name {{ nginx_server_name }}; 60 | ssl on; 61 | ssl_certificate {{ ssl_dest_dir }}/{{ application_name }}.crt; 62 | ssl_certificate_key {{ ssl_dest_dir }}/{{ application_name }}.key; 63 | 64 | client_max_body_size 4G; 65 | 66 | access_log {{ nginx_access_log_file }}; 67 | error_log {{ nginx_error_log_file }}; 68 | 69 | location /static/ { 70 | alias {{ nginx_static_dir }}; 71 | } 72 | 73 | location /media/ { 74 | alias {{ nginx_media_dir }}; 75 | } 76 | 77 | location / { 78 | if (-f {{ virtualenv_path }}/maintenance_on.html) { 79 | return 503; 80 | } 81 | 82 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 83 | proxy_set_header X-Forwarded-Proto https; 84 | proxy_set_header Host $http_host; 85 | proxy_redirect off; 86 | proxy_read_timeout 300; 87 | 88 | # Try to serve static files from nginx, no point in making an 89 | # *application* server like Unicorn/Rainbows! serve static files. 90 | if (!-f $request_filename) { 91 | proxy_pass http://wsgi_server; 92 | break; 93 | } 94 | } 95 | 96 | # Error pages 97 | error_page 500 502 504 /500.html; 98 | location = /500.html { 99 | root {{ project_path }}/{{ application_name }}/templates/; 100 | } 101 | 102 | error_page 503 /maintenance_on.html; 103 | location = /maintenance_on.html { 104 | root {{ virtualenv_path }}/; 105 | } 106 | } 107 | 108 | 109 | ### DOMAIN REDIRECTS ### 110 | 111 | # Redirect non-www top-level domain to www 112 | server { 113 | listen 80; 114 | listen 443; 115 | server_name youtubeadl.com; 116 | return 301 http://www.youtubeadl.com$request_uri; 117 | } -------------------------------------------------------------------------------- /ansible/roles/web/templates/supervisor_config.j2: -------------------------------------------------------------------------------- 1 | [program:{{ application_name }}] 2 | command = {{ virtualenv_path }}/bin/gunicorn_start 3 | user = {{ gunicorn_user }} 4 | stdout_logfile = {{ application_log_file }} 5 | redirect_stderr = true -------------------------------------------------------------------------------- /ansible/roles/web/templates/virtualenv_postactivate.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | {% for variable_name, value in django_environment.iteritems() %} 4 | export {{ variable_name }}="{{ value }}" 5 | {% endfor %} 6 | -------------------------------------------------------------------------------- /ansible/roles/web/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Application settings. 4 | virtualenv_path: "/webapps/{{ application_name }}" 5 | project_path: "{{ virtualenv_path }}/{{ project_name }}" 6 | application_log_dir: "{{ virtualenv_path }}/logs" 7 | application_log_file: "{{ application_log_dir }}/gunicorn_supervisor.log" 8 | requirements_file: "{{ project_path }}/requirements.txt" 9 | 10 | 11 | # Gunicorn settings 12 | gunicorn_user: "{{ application_name }}" 13 | gunicorn_group: webapps 14 | 15 | 16 | # Nginx settings 17 | nginx_http_port: 80 18 | nginx_https_port: 443 19 | nginx_access_log_file: "{{ application_log_dir }}/nginx_access.log" 20 | nginx_error_log_file: "{{ application_log_dir }}/nginx_error.log" 21 | nginx_static_dir: "{{ virtualenv_path }}/static/" 22 | nginx_media_dir: "{{ virtualenv_path }}/media/" 23 | 24 | 25 | # Django Environment variables 26 | django_environment: 27 | DJANGO_SETTINGS_MODULE: "{{ django_settings_file }}" 28 | DJANGO_SECRET_KEY: "{{ django_secret_key }}" 29 | MEDIA_ROOT: "{{ nginx_media_dir }}" 30 | STATIC_ROOT: "{{ nginx_static_dir }}" 31 | DATABASE_USER: "{{ db_user }}" 32 | DATABASE_PASSWORD: "{{ db_password }}" 33 | BROKER_URL: "{{ broker_url }}" 34 | EMAIL_HOST: "{{ email_host }}" 35 | EMAIL_HOST_USER: "{{ email_host_user }}" 36 | EMAIL_HOST_PASSWORD: "{{ email_host_password }}" 37 | GOOGLE_ANALYTICS_TRACKING_ID: "{{ google_analytics_tracking_id }}" 38 | ADDTHIS_PUBLISHER_ID: "{{ addthis_publisher_id }}" 39 | -------------------------------------------------------------------------------- /ansible/site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - include: dbservers.yml 4 | 5 | - include: webservers.yml -------------------------------------------------------------------------------- /ansible/vagrant.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Create a {{ application_name }} virtual machine via vagrant 4 | hosts: all 5 | sudo: yes 6 | sudo_user: root 7 | remote_user: vagrant 8 | vars: 9 | - update_apt_cache: yes 10 | vars_files: 11 | - env_vars/base.yml 12 | - env_vars/vagrant.yml 13 | 14 | roles: 15 | - base 16 | - db 17 | - rabbitmq 18 | - web 19 | - celery -------------------------------------------------------------------------------- /ansible/webservers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Provision a {{ application_name }} web server 4 | hosts: webservers 5 | sudo: yes 6 | sudo_user: root 7 | remote_user: root 8 | vars: 9 | - setup_git_repo: yes 10 | - update_apt_cache: yes 11 | vars_files: 12 | - env_vars/base.yml 13 | - env_vars/{{ env }}.yml 14 | 15 | roles: 16 | - base 17 | - rabbitmq 18 | - web 19 | - celery -------------------------------------------------------------------------------- /django/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jcalazan/django 2 | 3 | ADD ./requirements.txt ./requirements.txt 4 | 5 | RUN pip install -r requirements.txt 6 | -------------------------------------------------------------------------------- /django/requirements.txt: -------------------------------------------------------------------------------- 1 | django ~> 1.11.18 2 | 3 | django-braces==1.4.0 4 | django-compressor==1.4 5 | django-debug-toolbar>=1.11.1 6 | django-extensions==1.5.5 7 | django-grappelli==2.6.4 8 | celery==3.1.18 9 | flower ~> 0.9.2 10 | gunicorn ~> 19.5.0 11 | ipdb==0.8 12 | ipython[notebook]==3.2.1 13 | psycopg2==2.6 14 | werkzeug>=0.15.3 15 | youtube-dl>=2014.06.26 16 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | postgresql: 2 | image: sameersbn/postgresql:9.4-11 3 | environment: 4 | - DB_NAME=youtubeadl 5 | - DB_USER=youtubeadl 6 | - DB_PASS=password 7 | volumes: 8 | - ~/dockerfiles/youtube-audio-dl/postgresql:/var/lib/postgresql 9 | expose: 10 | - "5432" 11 | 12 | rabbitmq: 13 | image: jcalazan/rabbitmq 14 | expose: 15 | - "15672" 16 | 17 | # NOTES: 18 | # - The C_FORCE_ROOT variable allows celery to run as the root user. 19 | celery: 20 | build: django/ 21 | environment: 22 | - C_FORCE_ROOT=true 23 | - DATABASE_HOST=postgresql 24 | - BROKER_URL=amqp://guest:guest@rabbitmq// 25 | working_dir: /youtube-audio-dl 26 | command: bash -c "sleep 3 && celery -A youtubeadl worker -E -l info --concurrency=3" 27 | volumes: 28 | - .:/youtube-audio-dl 29 | links: 30 | - postgresql 31 | - rabbitmq 32 | 33 | # NOTES: 34 | # - The C_FORCE_ROOT variable allows celery to run as the root user. 35 | flower: 36 | build: django/ 37 | environment: 38 | - C_FORCE_ROOT=true 39 | - DATABASE_HOST=postgresql 40 | - BROKER_URL=amqp://guest:guest@rabbitmq// 41 | working_dir: /youtube-audio-dl 42 | command: bash -c "sleep 3 && celery -A youtubeadl flower --port=5555" 43 | volumes: 44 | - .:/youtube-audio-dl 45 | expose: 46 | - "5555" 47 | links: 48 | - postgresql 49 | - rabbitmq 50 | 51 | django: 52 | build: django/ 53 | environment: 54 | - DATABASE_HOST=postgresql 55 | - BROKER_URL=amqp://guest:guest@rabbitmq// 56 | working_dir: /youtube-audio-dl 57 | command: bash -c "sleep 3 && python manage.py migrate --noinput && python manage.py runserver_plus 0.0.0.0:80" 58 | volumes: 59 | - .:/youtube-audio-dl 60 | expose: 61 | - "80" 62 | links: 63 | - postgresql 64 | - rabbitmq 65 | 66 | tor: 67 | build: tor-hidden-service/ 68 | links: 69 | - django:web 70 | -------------------------------------------------------------------------------- /ipython_config.py: -------------------------------------------------------------------------------- 1 | c = get_config() 2 | 3 | # Allow all IP addresses to use the service and run it on port 80. 4 | c.NotebookApp.ip = '0.0.0.0' 5 | c.NotebookApp.port = 80 6 | 7 | # Don't load the browser on startup. 8 | c.NotebookApp.open_browser = False 9 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | 6 | if __name__ == '__main__': 7 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 8 | 'youtubeadl.settings.local') 9 | 10 | from django.core.management import execute_from_command_line 11 | 12 | execute_from_command_line(sys.argv) 13 | -------------------------------------------------------------------------------- /scripts/deploy_youtubeadl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ../ansible 4 | 5 | ansible-playbook -i ../ansible/production ../ansible/site.yml --tags="deploy" --ask-vault-pass 6 | -------------------------------------------------------------------------------- /tor-hidden-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM patrickod/tor-hidden-service 2 | 3 | ADD ./start-tor /bin/start-tor 4 | 5 | RUN chmod +x /bin/start-tor 6 | 7 | ADD ./get-tor-hostname /bin/get-tor-hostname 8 | 9 | RUN chmod +x /bin/get-tor-hostname 10 | 11 | -------------------------------------------------------------------------------- /tor-hidden-service/get-tor-hostname: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | HOSTNAME='/var/lib/tor/hidden-service/hostname' 4 | 5 | while [ ! -f $HOSTNAME ]; do sleep 1; done 6 | 7 | echo 'Your onion address is' $(cat $HOSTNAME) 8 | -------------------------------------------------------------------------------- /tor-hidden-service/start-tor: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | REGEX='\([0-9]*\)_TCP=tcp://\([0-9]\{1,3\}\.\)\{3\}\([0-9]\)\{1,3\}\:\([0-9]*\)' 5 | 6 | env | grep -o $REGEX | sed -e "s/_TCP=tcp:\/\// /" -e "s/:/ /" | awk '{ printf "HiddenServicePort %s %s:%s\n", $1, $2, $3 }' >> /etc/torrc 7 | 8 | /bin/get-tor-hostname & 9 | 10 | /usr/local/bin/tor -f /etc/torrc 11 | -------------------------------------------------------------------------------- /tor-hidden-service/torrc: -------------------------------------------------------------------------------- 1 | ## Configuration file for a typical Tor user 2 | ## Last updated 22 April 2012 for Tor 0.2.3.14-alpha. 3 | ## (may or may not work for much older or much newer versions of Tor.) 4 | ## 5 | ## Lines that begin with "## " try to explain what's going on. Lines 6 | ## that begin with just "#" are disabled commands: you can enable them 7 | ## by removing the "#" symbol. 8 | ## 9 | ## See 'man tor', or https://www.torproject.org/docs/tor-manual.html, 10 | ## for more options you can use in this file. 11 | ## 12 | ## Tor will look for this file in various places based on your platform: 13 | ## https://www.torproject.org/docs/faq#torrc 14 | 15 | ## Tor opens a socks proxy on port 9050 by default -- even if you don't 16 | ## configure one below. Set "SocksPort 0" if you plan to run Tor only 17 | ## as a relay, and not make any local application connections yourself. 18 | #SocksPort 9050 # Default: Bind to localhost:9050 for local connections. 19 | #SocksPort 192.168.0.1:9100 # Bind to this adddress:port too. 20 | 21 | ## Logs go to stdout at level "notice" unless redirected by something 22 | ## else, like one of the below lines. You can have as many Log lines as 23 | ## you want. 24 | ## 25 | ## We advise using "notice" in most cases, since anything more verbose 26 | ## may provide sensitive information to an attacker who obtains the logs. 27 | ## 28 | ## Send all messages of level 'notice' or higher to /var/log/tor/notices.log 29 | #Log notice file /var/log/tor/notices.log 30 | ## Send every possible message to /var/log/tor/debug.log 31 | #Log debug file /var/log/tor/debug.log 32 | ## Use the system log instead of Tor's logfiles 33 | Log notice syslog 34 | ## To send all messages to stderr: 35 | #Log debug stderr 36 | 37 | ## Uncomment this to start the process in the background... or use 38 | ## --runasdaemon 1 on the command line. This is ignored on Windows; 39 | ## see the FAQ entry if you want Tor to run as an NT service. 40 | #RunAsDaemon 1 41 | 42 | ## The directory for keeping all the keys/etc. By default, we store 43 | ## things in $HOME/.tor on Unix, and in Application Data\tor on Windows. 44 | DataDirectory /var/lib/tor 45 | 46 | ############### This section is just for location-hidden services ### 47 | 48 | ## Once you have configured a hidden service, you can look at the 49 | ## contents of the file ".../hidden_service/hostname" for the address 50 | ## to tell people. 51 | ## 52 | ## HiddenServicePort x y:z says to redirect requests on port x to the 53 | ## address y:z. 54 | 55 | #HiddenServiceDir /var/lib/tor/hidden_service/ 56 | #HiddenServicePort 80 127.0.0.1:80 57 | 58 | #HiddenServiceDir /var/lib/tor/other_hidden_service/ 59 | #HiddenServicePort 80 127.0.0.1:80 60 | #HiddenServicePort 22 127.0.0.1:22 61 | 62 | HiddenServiceDir /var/lib/tor/hidden-service 63 | -------------------------------------------------------------------------------- /youtubeadl/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | # This will make sure the app is always imported when 4 | # Django starts so that shared_task will use this app. 5 | from youtubeadl.celery import app as celery_app -------------------------------------------------------------------------------- /youtubeadl/apps/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'jonathan' 2 | -------------------------------------------------------------------------------- /youtubeadl/apps/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwestergren/docker-compose-tor-demo/5f0795e74b534b33a14a86c98f29a34b7ca0227d/youtubeadl/apps/core/__init__.py -------------------------------------------------------------------------------- /youtubeadl/apps/core/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from youtubeadl.apps.core.models import Ad 4 | 5 | 6 | @admin.register(Ad) 7 | class AdAdmin(admin.ModelAdmin): 8 | list_display = ('description', 'position', 'created', 'modified') -------------------------------------------------------------------------------- /youtubeadl/apps/core/context_processors.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def third_party_tracking_ids(request): 5 | """ 6 | Retrieve 3rd-party tracking IDs from the settings file and add them to the 7 | request context. 8 | """ 9 | return { 10 | 'google_analytics_tracking_id': settings.GOOGLE_ANALYTICS_TRACKING_ID, 11 | 'addthis_publisher_id': settings.ADDTHIS_PUBLISHER_ID, 12 | } -------------------------------------------------------------------------------- /youtubeadl/apps/core/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import django.utils.timezone 6 | import django_extensions.db.fields 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Ad', 17 | fields=[ 18 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 19 | ('created', django_extensions.db.fields.CreationDateTimeField(default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), 20 | ('modified', django_extensions.db.fields.ModificationDateTimeField(default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), 21 | ('description', models.CharField(max_length=255, null=True, blank=True)), 22 | ('code', models.TextField()), 23 | ('position', models.CharField(blank=True, max_length=10, null=True, choices=[(b'top', b'Top'), (b'bottom', b'Bottom')])), 24 | ], 25 | options={ 26 | 'ordering': ('-modified', '-created'), 27 | 'abstract': False, 28 | 'get_latest_by': 'modified', 29 | }, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /youtubeadl/apps/core/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwestergren/docker-compose-tor-demo/5f0795e74b534b33a14a86c98f29a34b7ca0227d/youtubeadl/apps/core/migrations/__init__.py -------------------------------------------------------------------------------- /youtubeadl/apps/core/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from django_extensions.db.models import TimeStampedModel 4 | 5 | 6 | class Ad(TimeStampedModel): 7 | TOP = 'top' 8 | BOTTOM = 'bottom' 9 | POSITION_CHOICES = ( 10 | (TOP, 'Top'), 11 | (BOTTOM, 'Bottom'), 12 | ) 13 | 14 | description = models.CharField(max_length=255, null=True, blank=True) 15 | code = models.TextField() 16 | position = models.CharField(max_length=10, 17 | null=True, 18 | blank=True, 19 | choices=POSITION_CHOICES) 20 | 21 | def __unicode__(self): 22 | return self.description -------------------------------------------------------------------------------- /youtubeadl/apps/core/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /youtubeadl/apps/core/utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | from unicodedata import normalize 3 | 4 | 5 | def slugify(text, delim=u'-'): 6 | """ 7 | Slugifies a string. 8 | 9 | This is slightly different from the built-in one in Django. 10 | 11 | Source: http://stackoverflow.com/questions/9042515/normalizing-unicode-\ 12 | text-to-filenames-etc-in-python 13 | """ 14 | result = [] 15 | 16 | re_obj = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.:]+') 17 | for word in re_obj.split(text): 18 | word = normalize('NFKD', word).encode('ascii', 'ignore') 19 | if word: 20 | result.append(word) 21 | return unicode(delim.join(result)) 22 | 23 | 24 | def get_client_ip(request): 25 | """ 26 | Retrieve the client's IPv4 address from the request object. 27 | """ 28 | x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') 29 | 30 | if x_forwarded_for: 31 | ip = x_forwarded_for.split(',')[0] 32 | else: 33 | ip = request.META.get('REMOTE_ADDR') 34 | return ip 35 | -------------------------------------------------------------------------------- /youtubeadl/apps/core/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwestergren/docker-compose-tor-demo/5f0795e74b534b33a14a86c98f29a34b7ca0227d/youtubeadl/apps/downloader/__init__.py -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.utils.text import Truncator 3 | 4 | from youtubeadl.apps.downloader import models 5 | 6 | 7 | class ActivityLogInline(admin.TabularInline): 8 | model = models.ActivityLog 9 | readonly_fields = ('created',) 10 | 11 | 12 | @admin.register(models.ActivityLog) 13 | class ActivityLogAdmin(admin.ModelAdmin): 14 | list_display = ( 15 | 'video', 16 | 'video_title', 17 | 'client_ip', 18 | 'action', 19 | 'created', 20 | ) 21 | list_filter = ('action', 'client_ip') 22 | 23 | def video_title(self, obj): 24 | return Truncator(obj.video.title).chars(50) 25 | video_title.admin_order_field = 'video__title' 26 | 27 | 28 | @admin.register(models.Video) 29 | class VideoAdmin(admin.ModelAdmin): 30 | list_display = ( 31 | 'youtube_id', 32 | 'title', 33 | 'audio_filename', 34 | 'duration', 35 | 'download_count', 36 | 'last_download_date', 37 | ) 38 | list_filter = ('last_download_date',) 39 | search_fields = ( 40 | 'youtube_id', 41 | 'title', 42 | 'audio_filename', 43 | ) 44 | 45 | inlines = [ActivityLogInline] 46 | -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import django.utils.timezone 6 | import django_extensions.db.fields 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='ActivityLog', 17 | fields=[ 18 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 19 | ('created', django_extensions.db.fields.CreationDateTimeField(default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), 20 | ('modified', django_extensions.db.fields.ModificationDateTimeField(default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), 21 | ('client_ip', models.GenericIPAddressField(null=True)), 22 | ('action', models.CharField(max_length=50, choices=[(b'convert', b'Convert'), (b'download', b'Download')])), 23 | ], 24 | options={ 25 | 'ordering': ('-modified', '-created'), 26 | 'abstract': False, 27 | 'get_latest_by': 'modified', 28 | }, 29 | ), 30 | migrations.CreateModel( 31 | name='Video', 32 | fields=[ 33 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 34 | ('created', django_extensions.db.fields.CreationDateTimeField(default=django.utils.timezone.now, verbose_name='created', editable=False, blank=True)), 35 | ('modified', django_extensions.db.fields.ModificationDateTimeField(default=django.utils.timezone.now, verbose_name='modified', editable=False, blank=True)), 36 | ('youtube_id', models.CharField(unique=True, max_length=100)), 37 | ('url', models.URLField(max_length=255)), 38 | ('title', models.TextField()), 39 | ('duration', models.IntegerField(null=True)), 40 | ('audio_filename', models.CharField(max_length=255, null=True, blank=True)), 41 | ('audio_filesize', models.IntegerField(null=True)), 42 | ('download_count', models.IntegerField(default=0, null=True)), 43 | ('last_download_date', models.DateTimeField(null=True)), 44 | ], 45 | options={ 46 | 'ordering': ('-modified', '-created'), 47 | 'abstract': False, 48 | 'get_latest_by': 'modified', 49 | }, 50 | ), 51 | migrations.AddField( 52 | model_name='activitylog', 53 | name='video', 54 | field=models.ForeignKey(to='downloader.Video'), 55 | ), 56 | ] 57 | -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwestergren/docker-compose-tor-demo/5f0795e74b534b33a14a86c98f29a34b7ca0227d/youtubeadl/apps/downloader/migrations/__init__.py -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/models.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from django.db import models 4 | 5 | from django_extensions.db.models import TimeStampedModel 6 | 7 | 8 | class Video(TimeStampedModel): 9 | youtube_id = models.CharField(max_length=100, unique=True) 10 | url = models.URLField(max_length=255) 11 | title = models.TextField() 12 | duration = models.IntegerField(null=True) 13 | audio_filename = models.CharField(max_length=255, null=True, blank=True) 14 | audio_filesize = models.IntegerField(null=True) 15 | download_count = models.IntegerField(null=True, default=0) 16 | last_download_date = models.DateTimeField(null=True) 17 | 18 | def __unicode__(self): 19 | return self.youtube_id 20 | 21 | 22 | class ActivityLogManager(models.Manager): 23 | 24 | def get_current_day_convert_count_by_ip(self, client_ip): 25 | today = datetime.datetime.today() 26 | return self.select_related().filter( 27 | client_ip=client_ip, 28 | action=ActivityLog.CONVERT, 29 | created__year=today.year, 30 | created__month=today.month, 31 | created__day=today.day).count() 32 | 33 | 34 | class ActivityLog(TimeStampedModel): 35 | """ 36 | Store user activity. 37 | """ 38 | CONVERT = 'convert' 39 | DOWNLOAD = 'download' 40 | ACTION_CHOICES = ( 41 | (CONVERT, 'Convert'), 42 | (DOWNLOAD, 'Download'), 43 | ) 44 | 45 | video = models.ForeignKey(Video) 46 | client_ip = models.GenericIPAddressField(null=True) 47 | action = models.CharField(max_length=50, choices=ACTION_CHOICES) 48 | 49 | objects = ActivityLogManager() -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/tasks.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | import shutil 5 | import uuid 6 | 7 | from django.conf import settings 8 | 9 | import youtube_dl 10 | from celery import shared_task 11 | 12 | from youtubeadl.apps.downloader.models import Video, ActivityLog 13 | from youtubeadl.apps.downloader.utils import create_filename, get_video_info 14 | 15 | 16 | @shared_task 17 | def convert(url, client_ip=None): 18 | """ 19 | Convert the YouTube video to MP3 audio. 20 | 21 | Steps: 22 | 1. Get the video's information to make sure the provided url is valid. 23 | 2. If info is returned and the duration is no more than 24 | MAX_DURATION_SECONDS, log the request and start the conversion. 25 | 3. Return the download link if conversion completes successfully. 26 | """ 27 | info = get_video_info(url) 28 | 29 | result = None 30 | if info and info['duration'] <= settings.MAX_DURATION_SECONDS: 31 | youtube_id = info['id'] 32 | title = info['title'] 33 | 34 | audio_filename = create_filename(info['title']) 35 | 36 | video, created = Video.objects.get_or_create(youtube_id=youtube_id) 37 | video.url = url 38 | video.title = title 39 | video.duration = int(info['duration']) 40 | video.save() 41 | 42 | ActivityLog.objects.create( 43 | video=video, 44 | client_ip=client_ip, 45 | action=ActivityLog.CONVERT, 46 | ) 47 | 48 | result = { 49 | 'youtube_id': youtube_id, 50 | 'title': title 51 | } 52 | 53 | # Simply return the filename and update the video object if the file 54 | # already exists, otherwise, start the conversion. 55 | output_filepath = os.path.join(settings.MEDIA_ROOT, audio_filename) 56 | if os.path.exists(output_filepath): 57 | result['filename'] = audio_filename 58 | 59 | # Update the video object. 60 | video.audio_filename = audio_filename 61 | video.audio_filesize = os.path.getsize(output_filepath) 62 | video.save() 63 | else: 64 | conversion_result = start_conversion(url, audio_filename, video) 65 | 66 | # If extraction result is 0, extraction is successful. 67 | if conversion_result == 0: 68 | result['filename'] = audio_filename 69 | 70 | return result 71 | 72 | 73 | def start_conversion(url, audio_filename, video): 74 | # We're creating a temporary file in case multiple workers are in the 75 | # process of converting the same video. 76 | temp_filepath = os.path.join(settings.MEDIA_ROOT, 77 | '{0}.{1}'.format(uuid.uuid4(), '%(ext)s')) 78 | output_filepath = os.path.join(settings.MEDIA_ROOT, audio_filename) 79 | 80 | ydl_opts = { 81 | 'format': 'bestaudio/best', 82 | 'extractaudio': True, 83 | 'audioformat': 'mp3', 84 | 'outtmpl': temp_filepath, 85 | 'noplaylist': True, 86 | 'postprocessors': [ 87 | { 88 | 'key': 'FFmpegMetadata' 89 | }, 90 | { 91 | 'key': 'FFmpegExtractAudio', 92 | 'preferredcodec': 'mp3', 93 | 'preferredquality': '192', 94 | }, 95 | ] 96 | } 97 | 98 | with youtube_dl.YoutubeDL(ydl_opts) as ydl: 99 | result = ydl.download([url]) 100 | 101 | # Status code 0 is successful. 102 | if result == 0: 103 | # Move the temporary file to the proper location. 104 | temp_filepath = temp_filepath.replace('.%(ext)s', '.mp3') 105 | shutil.move(temp_filepath, output_filepath) 106 | 107 | # Update the video object. 108 | video.audio_filename = audio_filename 109 | video.audio_filesize = os.path.getsize(output_filepath) 110 | video.save() 111 | 112 | return result -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | 3 | from youtubeadl.apps.downloader import views 4 | 5 | 6 | urlpatterns = patterns('', 7 | url( 8 | regex=r'^convert/$', 9 | view=views.ConvertAjaxView.as_view(), 10 | name='convert_ajax_view', 11 | ), 12 | url( 13 | regex=r'^download/(?P.*)/(?P.*)$', 14 | view=views.download, 15 | name='download_view', 16 | ), 17 | ) -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/utils.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | import youtube_dl 4 | 5 | from youtubeadl.apps.core.utils import slugify 6 | 7 | 8 | def create_filename(value): 9 | """ 10 | Generate a valid filename. 11 | 12 | Non-ASCII characters will be deleted from the value and replace spaces with 13 | underscores. Slashes and percent signs are also stripped. 14 | """ 15 | filename = slugify(value, u'_') 16 | 17 | # Generate a random filename if the title only contains non-ASCII 18 | # characters (i.e. slugifying it deletes everything). 19 | if not filename: 20 | filename = uuid.uuid4() 21 | 22 | return '{}.mp3'.format(filename) 23 | 24 | 25 | def get_video_info(url): 26 | """ 27 | Retrieve the YouTube videos' information without downloading it. 28 | 29 | Source: http://stackoverflow.com/questions/18054500/how-to-use-youtube-dl-\ 30 | from-a-python-programm/18947879#18947879 31 | """ 32 | ydl = youtube_dl.YoutubeDL() 33 | ydl.add_default_info_extractors() 34 | 35 | try: 36 | return ydl.extract_info(url, download=False) 37 | except youtube_dl.DownloadError: 38 | return None -------------------------------------------------------------------------------- /youtubeadl/apps/downloader/views.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | import os 4 | import urllib 5 | from urlparse import urlparse, parse_qs, urlsplit, urlunsplit 6 | 7 | from django.conf import settings 8 | from django.core.urlresolvers import reverse 9 | from django.http import HttpResponse, HttpResponseRedirect 10 | from django.utils.encoding import smart_str 11 | from django.views.generic import TemplateView, View 12 | 13 | from braces.views import JSONResponseMixin, AjaxResponseMixin 14 | from celery.result import AsyncResult 15 | 16 | from youtubeadl.apps.core.utils import get_client_ip 17 | 18 | from youtubeadl.apps.core.models import Ad 19 | from youtubeadl.apps.downloader import tasks 20 | from youtubeadl.apps.downloader.models import ActivityLog, Video 21 | 22 | 23 | logger = logging.getLogger(__name__) 24 | 25 | 26 | def download(request, youtube_id, filename): 27 | """ 28 | Serves the audio file. 29 | """ 30 | filepath = os.path.join(settings.MEDIA_ROOT, filename) 31 | file_exists = os.path.exists(filepath) 32 | 33 | video = None 34 | try: 35 | # We need to filter by both the youtube_id and audio_filename as the 36 | # record might exists but the video is still being converted. 37 | video = Video.objects.get(youtube_id=youtube_id, audio_filename=filename) 38 | except Video.DoesNotExist: 39 | pass 40 | 41 | if video and file_exists: 42 | ActivityLog.objects.create( 43 | video=video, 44 | action=ActivityLog.DOWNLOAD, 45 | client_ip=get_client_ip(request), 46 | ) 47 | 48 | video.download_count += 1 49 | video.last_download_date = datetime.datetime.now() 50 | video.save() 51 | 52 | if settings.DEBUG: 53 | with open(filepath, 'rb') as file_data: 54 | response = HttpResponse(file_data.read(), 55 | content_type='audio/mpeg') 56 | 57 | response['Content-Disposition'] = 'attachment; filename={}'.format( 58 | smart_str(filename)) 59 | response['Content-Length'] = os.path.getsize(filepath) 60 | 61 | return response 62 | else: 63 | # Have Nginx serve the file in production. 64 | response = HttpResponse(content_type='application/force-download') 65 | response['Content-Length'] = os.path.getsize(filepath) 66 | response['X-Accel-Redirect'] = os.path.join(settings.MEDIA_URL, 67 | smart_str(filename)) 68 | 69 | return response 70 | 71 | return HttpResponseRedirect(reverse('home')) 72 | 73 | 74 | class DownloadFormView(TemplateView): 75 | template_name = 'home.html' 76 | 77 | def get_context_data(self, **kwargs): 78 | context = super(DownloadFormView, self).get_context_data() 79 | context['ad_top'] = Ad.objects.filter(position=Ad.TOP).first() 80 | context['ad_bottom'] = Ad.objects.filter(position=Ad.BOTTOM).first() 81 | 82 | return context 83 | 84 | 85 | class ConvertAjaxView(JSONResponseMixin, AjaxResponseMixin, View): 86 | """ 87 | Ajax view to start the video conversion. 88 | """ 89 | 90 | def post_ajax(self, request, *args, **kwargs): 91 | url = self.parse_url(request.POST.get('url', '').strip()) 92 | 93 | client_ip = get_client_ip(request) 94 | client_convert_count = ActivityLog.objects\ 95 | .get_current_day_convert_count_by_ip(client_ip) 96 | daily_limit = settings.DAILY_CONVERT_LIMIT 97 | limit_reached = client_convert_count >= daily_limit 98 | 99 | if url and not limit_reached: 100 | task = tasks.convert.delay(url, client_ip) 101 | result = AsyncResult(task.id) 102 | 103 | # TODO: We're tying up resources here as we're waiting for the task 104 | # to finish. Remove this later and have the AJAX request retry 105 | # until result.ready(). 106 | result.wait() 107 | 108 | data = { 109 | 'task_id': task.id, 110 | 'is_ready': False, 111 | } 112 | if result.successful(): 113 | if result.result: 114 | youtube_id = result.result['youtube_id'] 115 | filename = result.result['filename'] 116 | download_link = reverse( 117 | 'download_view', 118 | kwargs={'youtube_id': youtube_id, 119 | 'filename': filename} 120 | ) 121 | 122 | data['message'] = 'Conversion successful!' 123 | data['is_ready'] = True 124 | data['youtube_id'] = youtube_id 125 | data['title'] = result.result['title'] 126 | data['filename'] = filename 127 | data['download_link'] = download_link 128 | 129 | return self.render_json_response(data, status=200) 130 | 131 | data['message'] = 'Could not convert the video. Please make ' \ 132 | 'sure the URL you entered is correct and ' \ 133 | 'the video is no more than {} minutes long.'\ 134 | .format(settings.MAX_DURATION_SECONDS / 60) 135 | return self.render_json_response(data, status=200) 136 | 137 | data['message'] = 'Something went wrong :(' 138 | return self.render_json_response(data, status=500) 139 | 140 | if limit_reached: 141 | logger.warn('Client reached convert limit: %s', client_ip) 142 | message = "Sorry, but you've reached your daily convert limit " \ 143 | "of {}. Please try again tomorrow.".format(daily_limit) 144 | return self.render_json_response({'message': message}, status=200) 145 | 146 | return self.render_json_response({'message': 'Please provide a URL.'}, 147 | status=200) 148 | 149 | def parse_url(self, url): 150 | """ 151 | Remove the list parameter from the URL as we currently don't support 152 | conversion of an entire playlist. 153 | """ 154 | qs = parse_qs(urlparse(url).query) 155 | if qs.get('list', None): 156 | del(qs['list']) 157 | parts = urlsplit(url) 158 | return urlunsplit([ 159 | parts.scheme, 160 | parts.netloc, 161 | parts.path, 162 | urllib.urlencode(qs, True), 163 | parts.fragment 164 | ]) 165 | 166 | return url -------------------------------------------------------------------------------- /youtubeadl/celery.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | 5 | from celery import Celery 6 | 7 | from django.conf import settings 8 | 9 | # set the default Django settings module for the 'celery' program. 10 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'youtubeadl.settings.local') 11 | 12 | app = Celery('youtubeadl') 13 | 14 | # Using a string here means the worker will not have to 15 | # pickle the object when using Windows. 16 | app.config_from_object('django.conf:settings') 17 | app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) 18 | 19 | 20 | @app.task(bind=True) 21 | def debug_task(self): 22 | print('Request: {0!r}'.format(self.request)) -------------------------------------------------------------------------------- /youtubeadl/settings/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'jonathan' 2 | -------------------------------------------------------------------------------- /youtubeadl/settings/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for youtubeadl project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.8/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.8/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | 14 | 15 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 16 | 17 | 18 | # Quick-start development settings - unsuitable for production 19 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 20 | 21 | # SECURITY WARNING: keep the secret key used in production secret! 22 | SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 23 | '^b46d^zsxhrthkq2p7ty14^zov%4@eq@j5e%f(ki_#2rnbp%-l') 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | # Grappelli needs to be defined before the admin. 35 | 'grappelli', 36 | 37 | 'django.contrib.admin', 38 | 'django.contrib.auth', 39 | 'django.contrib.contenttypes', 40 | 'django.contrib.sessions', 41 | 'django.contrib.messages', 42 | 'django.contrib.staticfiles', 43 | 44 | # 3rd-party apps. 45 | 'compressor', 46 | 'django_extensions', 47 | 48 | # Project apps. 49 | 'youtubeadl.apps.core', 50 | 'youtubeadl.apps.downloader', 51 | ) 52 | 53 | MIDDLEWARE_CLASSES = ( 54 | 'django.contrib.sessions.middleware.SessionMiddleware', 55 | 'django.middleware.common.CommonMiddleware', 56 | 'django.middleware.csrf.CsrfViewMiddleware', 57 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 58 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 59 | 'django.contrib.messages.middleware.MessageMiddleware', 60 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 61 | 'django.middleware.security.SecurityMiddleware', 62 | ) 63 | 64 | ROOT_URLCONF = 'youtubeadl.urls' 65 | 66 | TEMPLATES = [ 67 | { 68 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 69 | 'DIRS': [ 70 | os.path.join(BASE_DIR, 'templates'), 71 | ], 72 | 'OPTIONS': { 73 | 'context_processors': [ 74 | 'django.template.context_processors.debug', 75 | 'django.template.context_processors.request', 76 | 'django.contrib.auth.context_processors.auth', 77 | 'django.contrib.messages.context_processors.messages', 78 | 79 | # Local context processors. 80 | 'youtubeadl.apps.core.context_processors.third_party_tracking_ids', 81 | ], 82 | 'loaders': [ 83 | 'django.template.loaders.filesystem.Loader', 84 | 'django.template.loaders.app_directories.Loader', 85 | ], 86 | }, 87 | }, 88 | ] 89 | 90 | WSGI_APPLICATION = 'youtubeadl.wsgi.application' 91 | 92 | 93 | # Database 94 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 95 | 96 | DATABASES = { 97 | 'default': { 98 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 99 | 'NAME': os.getenv('DATABASE_NAME', 'youtubeadl'), 100 | 'USER': os.getenv('DATABASE_USER', 'youtubeadl'), 101 | 'PASSWORD': os.getenv('DATABASE_PASSWORD', 'password'), 102 | 'HOST': os.getenv('DATABASE_HOST', '127.0.0.1'), 103 | 'PORT': os.getenv('DATABASE_PORT', '5432'), 104 | } 105 | } 106 | 107 | # Internationalization 108 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 109 | 110 | LANGUAGE_CODE = 'en-us' 111 | 112 | TIME_ZONE = 'UTC' 113 | 114 | USE_I18N = True 115 | 116 | USE_L10N = True 117 | 118 | USE_TZ = True 119 | 120 | 121 | # Static files (CSS, JavaScript, Images) 122 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 123 | 124 | STATIC_ROOT = os.getenv('STATIC_ROOT', 125 | os.path.join(BASE_DIR, 'collected_static')) 126 | 127 | STATIC_URL = '/static/' 128 | 129 | # Additional locations of static files. 130 | STATICFILES_DIRS = ( 131 | os.path.join(BASE_DIR, 'static'), 132 | ) 133 | 134 | # List of finder classes that know how to find static files in 135 | # various locations. 136 | STATICFILES_FINDERS = ( 137 | 'django.contrib.staticfiles.finders.FileSystemFinder', 138 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 139 | 'compressor.finders.CompressorFinder', 140 | ) 141 | 142 | 143 | # Media files. 144 | 145 | MEDIA_ROOT = os.getenv('MEDIA_ROOT', os.path.join(BASE_DIR, 'media')) 146 | 147 | MEDIA_URL = '/media/' 148 | 149 | 150 | # Email settings. 151 | 152 | EMAIL_HOST = os.getenv('EMAIL_HOST') 153 | EMAIL_PORT = os.getenv('EMAIL_PORT', 587) 154 | EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER') 155 | EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') 156 | EMAIL_USE_TLS = True 157 | 158 | DEFAULT_FROM_EMAIL = 'Info ' 159 | SERVER_EMAIL = 'Alerts ' 160 | 161 | ADMINS = ( 162 | ('Admin', 'admin@youtubeadl.com'), 163 | ) 164 | 165 | 166 | # Logging settings. 167 | 168 | LOGGING = { 169 | 'version': 1, 170 | 'disable_existing_loggers': False, 171 | 'formatters': { 172 | 'default': { 173 | 'format': '%(asctime)s [%(name)s:%(lineno)s] %(levelname)s - %(message)s', 174 | }, 175 | 'simple': { 176 | 'format': '%(levelname)s %(message)s', 177 | }, 178 | }, 179 | 'filters': { 180 | 'require_debug_false': { 181 | '()': 'django.utils.log.RequireDebugFalse', 182 | } 183 | }, 184 | 'handlers': { 185 | 'null': { 186 | 'level': 'DEBUG', 187 | 'class': 'logging.NullHandler', 188 | }, 189 | 'console': { 190 | 'level': 'INFO', 191 | 'class': 'logging.StreamHandler', 192 | 'formatter': 'default', 193 | }, 194 | 'mail_admins': { 195 | 'level': 'ERROR', 196 | 'filters': ['require_debug_false'], 197 | 'class': 'django.utils.log.AdminEmailHandler', 198 | } 199 | }, 200 | 'loggers': { 201 | # Silence SuspiciousOperation.DisallowedHost exception ('Invalid 202 | # HTTP_HOST' header messages). Set the handler to 'null' so we don't 203 | # get those annoying emails. 204 | 'django.security.DisallowedHost': { 205 | 'handlers': ['null'], 206 | 'propagate': False, 207 | }, 208 | 'django.request': { 209 | 'handlers': ['mail_admins'], 210 | 'level': 'ERROR', 211 | 'propagate': True, 212 | }, 213 | '': { 214 | 'handlers': ['console', ], 215 | 'level': 'INFO', 216 | } 217 | } 218 | } 219 | 220 | 221 | # YouTube Audio Downloader settings. 222 | MAX_DURATION_SECONDS = 10800 223 | DAILY_CONVERT_LIMIT = 25 224 | 225 | 226 | # 3rd-party apps tracking IDs. 227 | GOOGLE_ANALYTICS_TRACKING_ID = os.getenv('GOOGLE_ANALYTICS_TRACKING_ID') 228 | ADDTHIS_PUBLISHER_ID = os.getenv('ADDTHIS_PUBLISHER_ID') 229 | 230 | 231 | # Celery settings. 232 | BROKER_URL = os.getenv('BROKER_URL', 'amqp://guest:guest@127.0.0.1//') 233 | CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND', 'amqp') 234 | 235 | 236 | # Grappelli settings. 237 | GRAPPELLI_ADMIN_TITLE = 'YouTube ADL Admin' -------------------------------------------------------------------------------- /youtubeadl/settings/local.py: -------------------------------------------------------------------------------- 1 | from youtubeadl.settings.base import * 2 | 3 | 4 | INSTALLED_APPS += ('debug_toolbar',) 5 | 6 | MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware', ) 7 | 8 | # The Django Debug Toolbar will only be shown to these client IPs. 9 | INTERNAL_IPS = ( 10 | '127.0.0.1', 11 | ) 12 | 13 | DEBUG_TOOLBAR_CONFIG = { 14 | 'INTERCEPT_REDIRECTS': False, 15 | 'SHOW_TEMPLATE_CONTEXT': True, 16 | 'HIDE_DJANGO_SQL': False, 17 | } -------------------------------------------------------------------------------- /youtubeadl/settings/production.py: -------------------------------------------------------------------------------- 1 | from youtubeadl.settings.base import * 2 | 3 | 4 | DEBUG = False 5 | 6 | ALLOWED_HOSTS = ['.youtubeadl.com'] 7 | 8 | 9 | # Use the cached template loader so template is compiled once and read from 10 | # memory instead of reading from disk on each load. 11 | TEMPLATES[0]['OPTIONS']['loaders'] = [ 12 | ('django.template.loaders.cached.Loader', [ 13 | 'django.template.loaders.filesystem.Loader', 14 | 'django.template.loaders.app_directories.Loader', 15 | ]), 16 | ] 17 | -------------------------------------------------------------------------------- /youtubeadl/static/js/main.js: -------------------------------------------------------------------------------- 1 | /* Main JavaScript file used throughout the YouTubeADL app */ 2 | 3 | function resetResultDiv() { 4 | $('#result').html(""); 5 | $('#result').removeClass(); 6 | } 7 | 8 | $(document).ajaxStart(function() { 9 | $('#result').removeClass(); 10 | $('#result').addClass('alert'); 11 | $('#result').html("


Converting video to MP3, please wait, this could take a few minutes...
"); 12 | }); 13 | 14 | $(document).ajaxStop(function() { 15 | $('#result').html(); 16 | $('#convertButton').prop('disabled', false); 17 | }); 18 | 19 | var downloadForm = $('#downloadForm'); 20 | downloadForm.submit(function () { 21 | // Disable convert button to prevent multiple submissions. 22 | $('#convertButton').prop('disabled', true); 23 | 24 | $.ajax({ 25 | type: downloadForm.attr('method'), 26 | url: downloadForm.attr('data-url'), 27 | data: downloadForm.serialize(), 28 | success: function (data, textStatus, xhr) { 29 | if (textStatus == 'success') { 30 | if (data['is_ready'] == true) { 31 | $('#result').addClass("alert-success"); 32 | $('#result').html("

""+ data['title'] + "" successfully converted!" 33 | + "

" 34 | + "Download MP3 File

"); 35 | } else { 36 | $('#result').addClass("alert-danger"); 37 | $('#result').html(data['message']); 38 | } 39 | } else { 40 | resetResultDiv(); 41 | alert(data['message']); 42 | } 43 | }, 44 | error: function(data) { 45 | resetResultDiv(); 46 | alert("Sorry, something went wrong. Please try again."); 47 | } 48 | }); 49 | return false; 50 | }); -------------------------------------------------------------------------------- /youtubeadl/static/vendor/bootstrap/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "vars": { 3 | "@gray-base": "#000", 4 | "@gray-darker": "lighten(@gray-base, 13.5%)", 5 | "@gray-dark": "lighten(@gray-base, 20%)", 6 | "@gray": "lighten(@gray-base, 33.5%)", 7 | "@gray-light": "lighten(@gray-base, 46.7%)", 8 | "@gray-lighter": "lighten(@gray-base, 93.5%)", 9 | "@brand-primary": "darken(#428bca, 6.5%)", 10 | "@brand-success": "#5cb85c", 11 | "@brand-info": "#5bc0de", 12 | "@brand-warning": "#f0ad4e", 13 | "@brand-danger": "#d9534f", 14 | "@body-bg": "#fff", 15 | "@text-color": "@gray-dark", 16 | "@link-color": "@brand-primary", 17 | "@link-hover-color": "darken(@link-color, 15%)", 18 | "@link-hover-decoration": "underline", 19 | "@font-family-sans-serif": "\"Helvetica Neue\", Helvetica, Arial, sans-serif", 20 | "@font-family-serif": "Georgia, \"Times New Roman\", Times, serif", 21 | "@font-family-monospace": "Menlo, Monaco, Consolas, \"Courier New\", monospace", 22 | "@font-family-base": "@font-family-sans-serif", 23 | "@font-size-base": "14px", 24 | "@font-size-large": "ceil((@font-size-base * 1.25))", 25 | "@font-size-small": "ceil((@font-size-base * 0.85))", 26 | "@font-size-h1": "floor((@font-size-base * 2.6))", 27 | "@font-size-h2": "floor((@font-size-base * 2.15))", 28 | "@font-size-h3": "ceil((@font-size-base * 1.7))", 29 | "@font-size-h4": "ceil((@font-size-base * 1.25))", 30 | "@font-size-h5": "@font-size-base", 31 | "@font-size-h6": "ceil((@font-size-base * 0.85))", 32 | "@line-height-base": "1.428571429", 33 | "@line-height-computed": "floor((@font-size-base * @line-height-base))", 34 | "@headings-font-family": "inherit", 35 | "@headings-font-weight": "500", 36 | "@headings-line-height": "1.1", 37 | "@headings-color": "inherit", 38 | "@icon-font-path": "\"../fonts/\"", 39 | "@icon-font-name": "\"glyphicons-halflings-regular\"", 40 | "@icon-font-svg-id": "\"glyphicons_halflingsregular\"", 41 | "@padding-base-vertical": "6px", 42 | "@padding-base-horizontal": "12px", 43 | "@padding-large-vertical": "10px", 44 | "@padding-large-horizontal": "16px", 45 | "@padding-small-vertical": "5px", 46 | "@padding-small-horizontal": "10px", 47 | "@padding-xs-vertical": "1px", 48 | "@padding-xs-horizontal": "5px", 49 | "@line-height-large": "1.3333333", 50 | "@line-height-small": "1.5", 51 | "@border-radius-base": "4px", 52 | "@border-radius-large": "6px", 53 | "@border-radius-small": "3px", 54 | "@component-active-color": "#fff", 55 | "@component-active-bg": "@brand-primary", 56 | "@caret-width-base": "4px", 57 | "@caret-width-large": "5px", 58 | "@table-cell-padding": "8px", 59 | "@table-condensed-cell-padding": "5px", 60 | "@table-bg": "transparent", 61 | "@table-bg-accent": "#f9f9f9", 62 | "@table-bg-hover": "#f5f5f5", 63 | "@table-bg-active": "@table-bg-hover", 64 | "@table-border-color": "#ddd", 65 | "@btn-font-weight": "normal", 66 | "@btn-default-color": "#333", 67 | "@btn-default-bg": "#fff", 68 | "@btn-default-border": "#ccc", 69 | "@btn-primary-color": "#fff", 70 | "@btn-primary-bg": "@brand-primary", 71 | "@btn-primary-border": "darken(@btn-primary-bg, 5%)", 72 | "@btn-success-color": "#fff", 73 | "@btn-success-bg": "@brand-success", 74 | "@btn-success-border": "darken(@btn-success-bg, 5%)", 75 | "@btn-info-color": "#fff", 76 | "@btn-info-bg": "@brand-info", 77 | "@btn-info-border": "darken(@btn-info-bg, 5%)", 78 | "@btn-warning-color": "#fff", 79 | "@btn-warning-bg": "@brand-warning", 80 | "@btn-warning-border": "darken(@btn-warning-bg, 5%)", 81 | "@btn-danger-color": "#fff", 82 | "@btn-danger-bg": "@brand-danger", 83 | "@btn-danger-border": "darken(@btn-danger-bg, 5%)", 84 | "@btn-link-disabled-color": "@gray-light", 85 | "@input-bg": "#fff", 86 | "@input-bg-disabled": "@gray-lighter", 87 | "@input-color": "@gray", 88 | "@input-border": "#ccc", 89 | "@input-border-radius": "@border-radius-base", 90 | "@input-border-radius-large": "@border-radius-large", 91 | "@input-border-radius-small": "@border-radius-small", 92 | "@input-border-focus": "#66afe9", 93 | "@input-color-placeholder": "#999", 94 | "@input-height-base": "(@line-height-computed + (@padding-base-vertical * 2) + 2)", 95 | "@input-height-large": "(ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2)", 96 | "@input-height-small": "(floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2)", 97 | "@form-group-margin-bottom": "15px", 98 | "@legend-color": "@gray-dark", 99 | "@legend-border-color": "#e5e5e5", 100 | "@input-group-addon-bg": "@gray-lighter", 101 | "@input-group-addon-border-color": "@input-border", 102 | "@cursor-disabled": "not-allowed", 103 | "@dropdown-bg": "#fff", 104 | "@dropdown-border": "rgba(0,0,0,.15)", 105 | "@dropdown-fallback-border": "#ccc", 106 | "@dropdown-divider-bg": "#e5e5e5", 107 | "@dropdown-link-color": "@gray-dark", 108 | "@dropdown-link-hover-color": "darken(@gray-dark, 5%)", 109 | "@dropdown-link-hover-bg": "#f5f5f5", 110 | "@dropdown-link-active-color": "@component-active-color", 111 | "@dropdown-link-active-bg": "@component-active-bg", 112 | "@dropdown-link-disabled-color": "@gray-light", 113 | "@dropdown-header-color": "@gray-light", 114 | "@dropdown-caret-color": "#000", 115 | "@screen-xs": "480px", 116 | "@screen-xs-min": "@screen-xs", 117 | "@screen-phone": "@screen-xs-min", 118 | "@screen-sm": "768px", 119 | "@screen-sm-min": "@screen-sm", 120 | "@screen-tablet": "@screen-sm-min", 121 | "@screen-md": "992px", 122 | "@screen-md-min": "@screen-md", 123 | "@screen-desktop": "@screen-md-min", 124 | "@screen-lg": "1200px", 125 | "@screen-lg-min": "@screen-lg", 126 | "@screen-lg-desktop": "@screen-lg-min", 127 | "@screen-xs-max": "(@screen-sm-min - 1)", 128 | "@screen-sm-max": "(@screen-md-min - 1)", 129 | "@screen-md-max": "(@screen-lg-min - 1)", 130 | "@grid-columns": "12", 131 | "@grid-gutter-width": "30px", 132 | "@grid-float-breakpoint": "@screen-sm-min", 133 | "@grid-float-breakpoint-max": "(@grid-float-breakpoint - 1)", 134 | "@container-tablet": "(720px + @grid-gutter-width)", 135 | "@container-sm": "@container-tablet", 136 | "@container-desktop": "(940px + @grid-gutter-width)", 137 | "@container-md": "@container-desktop", 138 | "@container-large-desktop": "(1140px + @grid-gutter-width)", 139 | "@container-lg": "@container-large-desktop", 140 | "@navbar-height": "50px", 141 | "@navbar-margin-bottom": "@line-height-computed", 142 | "@navbar-border-radius": "@border-radius-base", 143 | "@navbar-padding-horizontal": "floor((@grid-gutter-width / 2))", 144 | "@navbar-padding-vertical": "((@navbar-height - @line-height-computed) / 2)", 145 | "@navbar-collapse-max-height": "340px", 146 | "@navbar-default-color": "#777", 147 | "@navbar-default-bg": "#f8f8f8", 148 | "@navbar-default-border": "darken(@navbar-default-bg, 6.5%)", 149 | "@navbar-default-link-color": "#777", 150 | "@navbar-default-link-hover-color": "#333", 151 | "@navbar-default-link-hover-bg": "transparent", 152 | "@navbar-default-link-active-color": "#555", 153 | "@navbar-default-link-active-bg": "darken(@navbar-default-bg, 6.5%)", 154 | "@navbar-default-link-disabled-color": "#ccc", 155 | "@navbar-default-link-disabled-bg": "transparent", 156 | "@navbar-default-brand-color": "@navbar-default-link-color", 157 | "@navbar-default-brand-hover-color": "darken(@navbar-default-brand-color, 10%)", 158 | "@navbar-default-brand-hover-bg": "transparent", 159 | "@navbar-default-toggle-hover-bg": "#ddd", 160 | "@navbar-default-toggle-icon-bar-bg": "#888", 161 | "@navbar-default-toggle-border-color": "#ddd", 162 | "@navbar-inverse-color": "lighten(@gray-light, 15%)", 163 | "@navbar-inverse-bg": "#222", 164 | "@navbar-inverse-border": "darken(@navbar-inverse-bg, 10%)", 165 | "@navbar-inverse-link-color": "lighten(@gray-light, 15%)", 166 | "@navbar-inverse-link-hover-color": "#fff", 167 | "@navbar-inverse-link-hover-bg": "transparent", 168 | "@navbar-inverse-link-active-color": "@navbar-inverse-link-hover-color", 169 | "@navbar-inverse-link-active-bg": "darken(@navbar-inverse-bg, 10%)", 170 | "@navbar-inverse-link-disabled-color": "#444", 171 | "@navbar-inverse-link-disabled-bg": "transparent", 172 | "@navbar-inverse-brand-color": "@navbar-inverse-link-color", 173 | "@navbar-inverse-brand-hover-color": "#fff", 174 | "@navbar-inverse-brand-hover-bg": "transparent", 175 | "@navbar-inverse-toggle-hover-bg": "#333", 176 | "@navbar-inverse-toggle-icon-bar-bg": "#fff", 177 | "@navbar-inverse-toggle-border-color": "#333", 178 | "@nav-link-padding": "10px 15px", 179 | "@nav-link-hover-bg": "@gray-lighter", 180 | "@nav-disabled-link-color": "@gray-light", 181 | "@nav-disabled-link-hover-color": "@gray-light", 182 | "@nav-tabs-border-color": "#ddd", 183 | "@nav-tabs-link-hover-border-color": "@gray-lighter", 184 | "@nav-tabs-active-link-hover-bg": "@body-bg", 185 | "@nav-tabs-active-link-hover-color": "@gray", 186 | "@nav-tabs-active-link-hover-border-color": "#ddd", 187 | "@nav-tabs-justified-link-border-color": "#ddd", 188 | "@nav-tabs-justified-active-link-border-color": "@body-bg", 189 | "@nav-pills-border-radius": "@border-radius-base", 190 | "@nav-pills-active-link-hover-bg": "@component-active-bg", 191 | "@nav-pills-active-link-hover-color": "@component-active-color", 192 | "@pagination-color": "@link-color", 193 | "@pagination-bg": "#fff", 194 | "@pagination-border": "#ddd", 195 | "@pagination-hover-color": "@link-hover-color", 196 | "@pagination-hover-bg": "@gray-lighter", 197 | "@pagination-hover-border": "#ddd", 198 | "@pagination-active-color": "#fff", 199 | "@pagination-active-bg": "@brand-primary", 200 | "@pagination-active-border": "@brand-primary", 201 | "@pagination-disabled-color": "@gray-light", 202 | "@pagination-disabled-bg": "#fff", 203 | "@pagination-disabled-border": "#ddd", 204 | "@pager-bg": "@pagination-bg", 205 | "@pager-border": "@pagination-border", 206 | "@pager-border-radius": "15px", 207 | "@pager-hover-bg": "@pagination-hover-bg", 208 | "@pager-active-bg": "@pagination-active-bg", 209 | "@pager-active-color": "@pagination-active-color", 210 | "@pager-disabled-color": "@pagination-disabled-color", 211 | "@jumbotron-padding": "30px", 212 | "@jumbotron-color": "inherit", 213 | "@jumbotron-bg": "@gray-lighter", 214 | "@jumbotron-heading-color": "inherit", 215 | "@jumbotron-font-size": "ceil((@font-size-base * 1.5))", 216 | "@state-success-text": "#3c763d", 217 | "@state-success-bg": "#dff0d8", 218 | "@state-success-border": "darken(spin(@state-success-bg, -10), 5%)", 219 | "@state-info-text": "#31708f", 220 | "@state-info-bg": "#d9edf7", 221 | "@state-info-border": "darken(spin(@state-info-bg, -10), 7%)", 222 | "@state-warning-text": "#8a6d3b", 223 | "@state-warning-bg": "#fcf8e3", 224 | "@state-warning-border": "darken(spin(@state-warning-bg, -10), 5%)", 225 | "@state-danger-text": "#a94442", 226 | "@state-danger-bg": "#f2dede", 227 | "@state-danger-border": "darken(spin(@state-danger-bg, -10), 5%)", 228 | "@tooltip-max-width": "200px", 229 | "@tooltip-color": "#fff", 230 | "@tooltip-bg": "#000", 231 | "@tooltip-opacity": ".9", 232 | "@tooltip-arrow-width": "5px", 233 | "@tooltip-arrow-color": "@tooltip-bg", 234 | "@popover-bg": "#fff", 235 | "@popover-max-width": "276px", 236 | "@popover-border-color": "rgba(0,0,0,.2)", 237 | "@popover-fallback-border-color": "#ccc", 238 | "@popover-title-bg": "darken(@popover-bg, 3%)", 239 | "@popover-arrow-width": "10px", 240 | "@popover-arrow-color": "@popover-bg", 241 | "@popover-arrow-outer-width": "(@popover-arrow-width + 1)", 242 | "@popover-arrow-outer-color": "fadein(@popover-border-color, 5%)", 243 | "@popover-arrow-outer-fallback-color": "darken(@popover-fallback-border-color, 20%)", 244 | "@label-default-bg": "@gray-light", 245 | "@label-primary-bg": "@brand-primary", 246 | "@label-success-bg": "@brand-success", 247 | "@label-info-bg": "@brand-info", 248 | "@label-warning-bg": "@brand-warning", 249 | "@label-danger-bg": "@brand-danger", 250 | "@label-color": "#fff", 251 | "@label-link-hover-color": "#fff", 252 | "@modal-inner-padding": "15px", 253 | "@modal-title-padding": "15px", 254 | "@modal-title-line-height": "@line-height-base", 255 | "@modal-content-bg": "#fff", 256 | "@modal-content-border-color": "rgba(0,0,0,.2)", 257 | "@modal-content-fallback-border-color": "#999", 258 | "@modal-backdrop-bg": "#000", 259 | "@modal-backdrop-opacity": ".5", 260 | "@modal-header-border-color": "#e5e5e5", 261 | "@modal-footer-border-color": "@modal-header-border-color", 262 | "@modal-lg": "900px", 263 | "@modal-md": "600px", 264 | "@modal-sm": "300px", 265 | "@alert-padding": "15px", 266 | "@alert-border-radius": "@border-radius-base", 267 | "@alert-link-font-weight": "bold", 268 | "@alert-success-bg": "@state-success-bg", 269 | "@alert-success-text": "@state-success-text", 270 | "@alert-success-border": "@state-success-border", 271 | "@alert-info-bg": "@state-info-bg", 272 | "@alert-info-text": "@state-info-text", 273 | "@alert-info-border": "@state-info-border", 274 | "@alert-warning-bg": "@state-warning-bg", 275 | "@alert-warning-text": "@state-warning-text", 276 | "@alert-warning-border": "@state-warning-border", 277 | "@alert-danger-bg": "@state-danger-bg", 278 | "@alert-danger-text": "@state-danger-text", 279 | "@alert-danger-border": "@state-danger-border", 280 | "@progress-bg": "#f5f5f5", 281 | "@progress-bar-color": "#fff", 282 | "@progress-border-radius": "@border-radius-base", 283 | "@progress-bar-bg": "@brand-primary", 284 | "@progress-bar-success-bg": "@brand-success", 285 | "@progress-bar-warning-bg": "@brand-warning", 286 | "@progress-bar-danger-bg": "@brand-danger", 287 | "@progress-bar-info-bg": "@brand-info", 288 | "@list-group-bg": "#fff", 289 | "@list-group-border": "#ddd", 290 | "@list-group-border-radius": "@border-radius-base", 291 | "@list-group-hover-bg": "#f5f5f5", 292 | "@list-group-active-color": "@component-active-color", 293 | "@list-group-active-bg": "@component-active-bg", 294 | "@list-group-active-border": "@list-group-active-bg", 295 | "@list-group-active-text-color": "lighten(@list-group-active-bg, 40%)", 296 | "@list-group-disabled-color": "@gray-light", 297 | "@list-group-disabled-bg": "@gray-lighter", 298 | "@list-group-disabled-text-color": "@list-group-disabled-color", 299 | "@list-group-link-color": "#555", 300 | "@list-group-link-hover-color": "@list-group-link-color", 301 | "@list-group-link-heading-color": "#333", 302 | "@panel-bg": "#fff", 303 | "@panel-body-padding": "15px", 304 | "@panel-heading-padding": "10px 15px", 305 | "@panel-footer-padding": "@panel-heading-padding", 306 | "@panel-border-radius": "@border-radius-base", 307 | "@panel-inner-border": "#ddd", 308 | "@panel-footer-bg": "#f5f5f5", 309 | "@panel-default-text": "@gray-dark", 310 | "@panel-default-border": "#ddd", 311 | "@panel-default-heading-bg": "#f5f5f5", 312 | "@panel-primary-text": "#fff", 313 | "@panel-primary-border": "@brand-primary", 314 | "@panel-primary-heading-bg": "@brand-primary", 315 | "@panel-success-text": "@state-success-text", 316 | "@panel-success-border": "@state-success-border", 317 | "@panel-success-heading-bg": "@state-success-bg", 318 | "@panel-info-text": "@state-info-text", 319 | "@panel-info-border": "@state-info-border", 320 | "@panel-info-heading-bg": "@state-info-bg", 321 | "@panel-warning-text": "@state-warning-text", 322 | "@panel-warning-border": "@state-warning-border", 323 | "@panel-warning-heading-bg": "@state-warning-bg", 324 | "@panel-danger-text": "@state-danger-text", 325 | "@panel-danger-border": "@state-danger-border", 326 | "@panel-danger-heading-bg": "@state-danger-bg", 327 | "@thumbnail-padding": "4px", 328 | "@thumbnail-bg": "@body-bg", 329 | "@thumbnail-border": "#ddd", 330 | "@thumbnail-border-radius": "@border-radius-base", 331 | "@thumbnail-caption-color": "@text-color", 332 | "@thumbnail-caption-padding": "9px", 333 | "@well-bg": "#f5f5f5", 334 | "@well-border": "darken(@well-bg, 7%)", 335 | "@badge-color": "#fff", 336 | "@badge-link-hover-color": "#fff", 337 | "@badge-bg": "@gray-light", 338 | "@badge-active-color": "@link-color", 339 | "@badge-active-bg": "#fff", 340 | "@badge-font-weight": "bold", 341 | "@badge-line-height": "1", 342 | "@badge-border-radius": "10px", 343 | "@breadcrumb-padding-vertical": "8px", 344 | "@breadcrumb-padding-horizontal": "15px", 345 | "@breadcrumb-bg": "#f5f5f5", 346 | "@breadcrumb-color": "#ccc", 347 | "@breadcrumb-active-color": "@gray-light", 348 | "@breadcrumb-separator": "\"/\"", 349 | "@carousel-text-shadow": "0 1px 2px rgba(0,0,0,.6)", 350 | "@carousel-control-color": "#fff", 351 | "@carousel-control-width": "15%", 352 | "@carousel-control-opacity": ".5", 353 | "@carousel-control-font-size": "20px", 354 | "@carousel-indicator-active-bg": "#fff", 355 | "@carousel-indicator-border-color": "#fff", 356 | "@carousel-caption-color": "#fff", 357 | "@close-font-weight": "bold", 358 | "@close-color": "#000", 359 | "@close-text-shadow": "0 1px 0 #fff", 360 | "@code-color": "#c7254e", 361 | "@code-bg": "#f9f2f4", 362 | "@kbd-color": "#fff", 363 | "@kbd-bg": "#333", 364 | "@pre-bg": "#f5f5f5", 365 | "@pre-color": "@gray-dark", 366 | "@pre-border-color": "#ccc", 367 | "@pre-scrollable-max-height": "340px", 368 | "@component-offset-horizontal": "180px", 369 | "@text-muted": "@gray-light", 370 | "@abbr-border-color": "@gray-light", 371 | "@headings-small-color": "@gray-light", 372 | "@blockquote-small-color": "@gray-light", 373 | "@blockquote-font-size": "(@font-size-base * 1.25)", 374 | "@blockquote-border-color": "@gray-lighter", 375 | "@page-header-border-color": "@gray-lighter", 376 | "@dl-horizontal-offset": "@component-offset-horizontal", 377 | "@hr-border": "@gray-lighter" 378 | }, 379 | "css": [ 380 | "print.less", 381 | "type.less", 382 | "code.less", 383 | "grid.less", 384 | "tables.less", 385 | "forms.less", 386 | "buttons.less", 387 | "responsive-utilities.less", 388 | "button-groups.less", 389 | "input-groups.less", 390 | "navs.less", 391 | "navbar.less", 392 | "breadcrumbs.less", 393 | "pagination.less", 394 | "pager.less", 395 | "labels.less", 396 | "badges.less", 397 | "jumbotron.less", 398 | "thumbnails.less", 399 | "alerts.less", 400 | "progress-bars.less", 401 | "media.less", 402 | "list-group.less", 403 | "panels.less", 404 | "responsive-embed.less", 405 | "wells.less", 406 | "close.less", 407 | "component-animations.less", 408 | "dropdowns.less", 409 | "tooltip.less", 410 | "popovers.less", 411 | "modals.less", 412 | "carousel.less" 413 | ], 414 | "js": [ 415 | "alert.js", 416 | "button.js", 417 | "carousel.js", 418 | "dropdown.js", 419 | "modal.js", 420 | "tooltip.js", 421 | "popover.js", 422 | "tab.js", 423 | "affix.js", 424 | "collapse.js", 425 | "scrollspy.js", 426 | "transition.js" 427 | ], 428 | "customizerUrl": "http://getbootstrap.com/customize/?id=99a2c42cc1a0aed84751" 429 | } -------------------------------------------------------------------------------- /youtubeadl/static/vendor/bootstrap/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.4 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | /*! 8 | * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=99a2c42cc1a0aed84751) 9 | * Config saved to config.json and https://gist.github.com/99a2c42cc1a0aed84751 10 | */.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-default .badge,.btn-primary .badge,.btn-success .badge,.btn-info .badge,.btn-warning .badge,.btn-danger .badge{text-shadow:none}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:-o-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#e0e0e0));background-image:linear-gradient(to bottom, #fff 0, #e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top, #337ab7 0, #265a88 100%);background-image:-o-linear-gradient(top, #337ab7 0, #265a88 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#265a88));background-image:linear-gradient(to bottom, #337ab7 0, #265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#245580}.btn-primary:hover,.btn-primary:focus{background-color:#265a88;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary:disabled,.btn-primary[disabled]{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#419641));background-image:linear-gradient(to bottom, #5cb85c 0, #419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #2aabd2 100%);background-image:-o-linear-gradient(top, #5bc0de 0, #2aabd2 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5bc0de), to(#2aabd2));background-image:linear-gradient(to bottom, #5bc0de 0, #2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#eb9316));background-image:linear-gradient(to bottom, #f0ad4e 0, #eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c12e2a));background-image:linear-gradient(to bottom, #d9534f 0, #c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-o-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2e6da4));background-image:linear-gradient(to bottom, #337ab7 0, #2e6da4 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-color:#2e6da4}.navbar-default{background-image:-webkit-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:-o-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#f8f8f8));background-image:linear-gradient(to bottom, #fff 0, #f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);background-image:-o-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dbdbdb), to(#e2e2e2));background-image:linear-gradient(to bottom, #dbdbdb 0, #e2e2e2 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:-o-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #3c3c3c), to(#222));background-image:linear-gradient(to bottom, #3c3c3c 0, #222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #080808 0, #0f0f0f 100%);background-image:-o-linear-gradient(top, #080808 0, #0f0f0f 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #080808), to(#0f0f0f));background-image:linear-gradient(to bottom, #080808 0, #0f0f0f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-image:-webkit-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-o-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2e6da4));background-image:linear-gradient(to bottom, #337ab7 0, #2e6da4 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0)}}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#c8e5bc));background-image:linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:-o-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9edf7), to(#b9def0));background-image:linear-gradient(to bottom, #d9edf7 0, #b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#f8efc0));background-image:linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:-o-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#e7c3c3));background-image:linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:-o-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #ebebeb), to(#f5f5f5));background-image:linear-gradient(to bottom, #ebebeb 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top, #337ab7 0, #286090 100%);background-image:-o-linear-gradient(top, #337ab7 0, #286090 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#286090));background-image:linear-gradient(to bottom, #337ab7 0, #286090 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#449d44));background-image:linear-gradient(to bottom, #5cb85c 0, #449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:-o-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5bc0de), to(#31b0d5));background-image:linear-gradient(to bottom, #5bc0de 0, #31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#ec971f));background-image:linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c9302c));background-image:linear-gradient(to bottom, #d9534f 0, #c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top, #337ab7 0, #2b669a 100%);background-image:-o-linear-gradient(top, #337ab7 0, #2b669a 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2b669a));background-image:linear-gradient(to bottom, #337ab7 0, #2b669a 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:hover .badge,.list-group-item.active:focus .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-o-linear-gradient(top, #337ab7 0, #2e6da4 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #337ab7), to(#2e6da4));background-image:linear-gradient(to bottom, #337ab7 0, #2e6da4 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#d0e9c6));background-image:linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:-o-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9edf7), to(#c4e3f3));background-image:linear-gradient(to bottom, #d9edf7 0, #c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#faf2cc));background-image:linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:-o-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#ebcccc));background-image:linear-gradient(to bottom, #f2dede 0, #ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:-o-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #e8e8e8), to(#f5f5f5));background-image:linear-gradient(to bottom, #e8e8e8 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwestergren/docker-compose-tor-demo/5f0795e74b534b33a14a86c98f29a34b7ca0227d/youtubeadl/static/vendor/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwestergren/docker-compose-tor-demo/5f0795e74b534b33a14a86c98f29a34b7ca0227d/youtubeadl/static/vendor/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwestergren/docker-compose-tor-demo/5f0795e74b534b33a14a86c98f29a34b7ca0227d/youtubeadl/static/vendor/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwestergren/docker-compose-tor-demo/5f0795e74b534b33a14a86c98f29a34b7ca0227d/youtubeadl/static/vendor/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwestergren/docker-compose-tor-demo/5f0795e74b534b33a14a86c98f29a34b7ca0227d/youtubeadl/static/vendor/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .@{fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | 15 | .fa-icon-rotate(@degrees, @rotation) { 16 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation); 17 | -webkit-transform: rotate(@degrees); 18 | -ms-transform: rotate(@degrees); 19 | transform: rotate(@degrees); 20 | } 21 | 22 | .fa-icon-flip(@horiz, @vert, @rotation) { 23 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1); 24 | -webkit-transform: scale(@horiz, @vert); 25 | -ms-transform: scale(@horiz, @vert); 26 | transform: scale(@horiz, @vert); 27 | } 28 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/less/variables.less: -------------------------------------------------------------------------------- 1 | // Variables 2 | // -------------------------- 3 | 4 | @fa-font-path: "../fonts"; 5 | @fa-font-size-base: 14px; 6 | //@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.3.0/fonts"; // for referencing Bootstrap CDN font files directly 7 | @fa-css-prefix: fa; 8 | @fa-version: "4.3.0"; 9 | @fa-border-color: #eee; 10 | @fa-inverse: #fff; 11 | @fa-li-width: (30em / 14); 12 | 13 | @fa-var-adjust: "\f042"; 14 | @fa-var-adn: "\f170"; 15 | @fa-var-align-center: "\f037"; 16 | @fa-var-align-justify: "\f039"; 17 | @fa-var-align-left: "\f036"; 18 | @fa-var-align-right: "\f038"; 19 | @fa-var-ambulance: "\f0f9"; 20 | @fa-var-anchor: "\f13d"; 21 | @fa-var-android: "\f17b"; 22 | @fa-var-angellist: "\f209"; 23 | @fa-var-angle-double-down: "\f103"; 24 | @fa-var-angle-double-left: "\f100"; 25 | @fa-var-angle-double-right: "\f101"; 26 | @fa-var-angle-double-up: "\f102"; 27 | @fa-var-angle-down: "\f107"; 28 | @fa-var-angle-left: "\f104"; 29 | @fa-var-angle-right: "\f105"; 30 | @fa-var-angle-up: "\f106"; 31 | @fa-var-apple: "\f179"; 32 | @fa-var-archive: "\f187"; 33 | @fa-var-area-chart: "\f1fe"; 34 | @fa-var-arrow-circle-down: "\f0ab"; 35 | @fa-var-arrow-circle-left: "\f0a8"; 36 | @fa-var-arrow-circle-o-down: "\f01a"; 37 | @fa-var-arrow-circle-o-left: "\f190"; 38 | @fa-var-arrow-circle-o-right: "\f18e"; 39 | @fa-var-arrow-circle-o-up: "\f01b"; 40 | @fa-var-arrow-circle-right: "\f0a9"; 41 | @fa-var-arrow-circle-up: "\f0aa"; 42 | @fa-var-arrow-down: "\f063"; 43 | @fa-var-arrow-left: "\f060"; 44 | @fa-var-arrow-right: "\f061"; 45 | @fa-var-arrow-up: "\f062"; 46 | @fa-var-arrows: "\f047"; 47 | @fa-var-arrows-alt: "\f0b2"; 48 | @fa-var-arrows-h: "\f07e"; 49 | @fa-var-arrows-v: "\f07d"; 50 | @fa-var-asterisk: "\f069"; 51 | @fa-var-at: "\f1fa"; 52 | @fa-var-automobile: "\f1b9"; 53 | @fa-var-backward: "\f04a"; 54 | @fa-var-ban: "\f05e"; 55 | @fa-var-bank: "\f19c"; 56 | @fa-var-bar-chart: "\f080"; 57 | @fa-var-bar-chart-o: "\f080"; 58 | @fa-var-barcode: "\f02a"; 59 | @fa-var-bars: "\f0c9"; 60 | @fa-var-bed: "\f236"; 61 | @fa-var-beer: "\f0fc"; 62 | @fa-var-behance: "\f1b4"; 63 | @fa-var-behance-square: "\f1b5"; 64 | @fa-var-bell: "\f0f3"; 65 | @fa-var-bell-o: "\f0a2"; 66 | @fa-var-bell-slash: "\f1f6"; 67 | @fa-var-bell-slash-o: "\f1f7"; 68 | @fa-var-bicycle: "\f206"; 69 | @fa-var-binoculars: "\f1e5"; 70 | @fa-var-birthday-cake: "\f1fd"; 71 | @fa-var-bitbucket: "\f171"; 72 | @fa-var-bitbucket-square: "\f172"; 73 | @fa-var-bitcoin: "\f15a"; 74 | @fa-var-bold: "\f032"; 75 | @fa-var-bolt: "\f0e7"; 76 | @fa-var-bomb: "\f1e2"; 77 | @fa-var-book: "\f02d"; 78 | @fa-var-bookmark: "\f02e"; 79 | @fa-var-bookmark-o: "\f097"; 80 | @fa-var-briefcase: "\f0b1"; 81 | @fa-var-btc: "\f15a"; 82 | @fa-var-bug: "\f188"; 83 | @fa-var-building: "\f1ad"; 84 | @fa-var-building-o: "\f0f7"; 85 | @fa-var-bullhorn: "\f0a1"; 86 | @fa-var-bullseye: "\f140"; 87 | @fa-var-bus: "\f207"; 88 | @fa-var-buysellads: "\f20d"; 89 | @fa-var-cab: "\f1ba"; 90 | @fa-var-calculator: "\f1ec"; 91 | @fa-var-calendar: "\f073"; 92 | @fa-var-calendar-o: "\f133"; 93 | @fa-var-camera: "\f030"; 94 | @fa-var-camera-retro: "\f083"; 95 | @fa-var-car: "\f1b9"; 96 | @fa-var-caret-down: "\f0d7"; 97 | @fa-var-caret-left: "\f0d9"; 98 | @fa-var-caret-right: "\f0da"; 99 | @fa-var-caret-square-o-down: "\f150"; 100 | @fa-var-caret-square-o-left: "\f191"; 101 | @fa-var-caret-square-o-right: "\f152"; 102 | @fa-var-caret-square-o-up: "\f151"; 103 | @fa-var-caret-up: "\f0d8"; 104 | @fa-var-cart-arrow-down: "\f218"; 105 | @fa-var-cart-plus: "\f217"; 106 | @fa-var-cc: "\f20a"; 107 | @fa-var-cc-amex: "\f1f3"; 108 | @fa-var-cc-discover: "\f1f2"; 109 | @fa-var-cc-mastercard: "\f1f1"; 110 | @fa-var-cc-paypal: "\f1f4"; 111 | @fa-var-cc-stripe: "\f1f5"; 112 | @fa-var-cc-visa: "\f1f0"; 113 | @fa-var-certificate: "\f0a3"; 114 | @fa-var-chain: "\f0c1"; 115 | @fa-var-chain-broken: "\f127"; 116 | @fa-var-check: "\f00c"; 117 | @fa-var-check-circle: "\f058"; 118 | @fa-var-check-circle-o: "\f05d"; 119 | @fa-var-check-square: "\f14a"; 120 | @fa-var-check-square-o: "\f046"; 121 | @fa-var-chevron-circle-down: "\f13a"; 122 | @fa-var-chevron-circle-left: "\f137"; 123 | @fa-var-chevron-circle-right: "\f138"; 124 | @fa-var-chevron-circle-up: "\f139"; 125 | @fa-var-chevron-down: "\f078"; 126 | @fa-var-chevron-left: "\f053"; 127 | @fa-var-chevron-right: "\f054"; 128 | @fa-var-chevron-up: "\f077"; 129 | @fa-var-child: "\f1ae"; 130 | @fa-var-circle: "\f111"; 131 | @fa-var-circle-o: "\f10c"; 132 | @fa-var-circle-o-notch: "\f1ce"; 133 | @fa-var-circle-thin: "\f1db"; 134 | @fa-var-clipboard: "\f0ea"; 135 | @fa-var-clock-o: "\f017"; 136 | @fa-var-close: "\f00d"; 137 | @fa-var-cloud: "\f0c2"; 138 | @fa-var-cloud-download: "\f0ed"; 139 | @fa-var-cloud-upload: "\f0ee"; 140 | @fa-var-cny: "\f157"; 141 | @fa-var-code: "\f121"; 142 | @fa-var-code-fork: "\f126"; 143 | @fa-var-codepen: "\f1cb"; 144 | @fa-var-coffee: "\f0f4"; 145 | @fa-var-cog: "\f013"; 146 | @fa-var-cogs: "\f085"; 147 | @fa-var-columns: "\f0db"; 148 | @fa-var-comment: "\f075"; 149 | @fa-var-comment-o: "\f0e5"; 150 | @fa-var-comments: "\f086"; 151 | @fa-var-comments-o: "\f0e6"; 152 | @fa-var-compass: "\f14e"; 153 | @fa-var-compress: "\f066"; 154 | @fa-var-connectdevelop: "\f20e"; 155 | @fa-var-copy: "\f0c5"; 156 | @fa-var-copyright: "\f1f9"; 157 | @fa-var-credit-card: "\f09d"; 158 | @fa-var-crop: "\f125"; 159 | @fa-var-crosshairs: "\f05b"; 160 | @fa-var-css3: "\f13c"; 161 | @fa-var-cube: "\f1b2"; 162 | @fa-var-cubes: "\f1b3"; 163 | @fa-var-cut: "\f0c4"; 164 | @fa-var-cutlery: "\f0f5"; 165 | @fa-var-dashboard: "\f0e4"; 166 | @fa-var-dashcube: "\f210"; 167 | @fa-var-database: "\f1c0"; 168 | @fa-var-dedent: "\f03b"; 169 | @fa-var-delicious: "\f1a5"; 170 | @fa-var-desktop: "\f108"; 171 | @fa-var-deviantart: "\f1bd"; 172 | @fa-var-diamond: "\f219"; 173 | @fa-var-digg: "\f1a6"; 174 | @fa-var-dollar: "\f155"; 175 | @fa-var-dot-circle-o: "\f192"; 176 | @fa-var-download: "\f019"; 177 | @fa-var-dribbble: "\f17d"; 178 | @fa-var-dropbox: "\f16b"; 179 | @fa-var-drupal: "\f1a9"; 180 | @fa-var-edit: "\f044"; 181 | @fa-var-eject: "\f052"; 182 | @fa-var-ellipsis-h: "\f141"; 183 | @fa-var-ellipsis-v: "\f142"; 184 | @fa-var-empire: "\f1d1"; 185 | @fa-var-envelope: "\f0e0"; 186 | @fa-var-envelope-o: "\f003"; 187 | @fa-var-envelope-square: "\f199"; 188 | @fa-var-eraser: "\f12d"; 189 | @fa-var-eur: "\f153"; 190 | @fa-var-euro: "\f153"; 191 | @fa-var-exchange: "\f0ec"; 192 | @fa-var-exclamation: "\f12a"; 193 | @fa-var-exclamation-circle: "\f06a"; 194 | @fa-var-exclamation-triangle: "\f071"; 195 | @fa-var-expand: "\f065"; 196 | @fa-var-external-link: "\f08e"; 197 | @fa-var-external-link-square: "\f14c"; 198 | @fa-var-eye: "\f06e"; 199 | @fa-var-eye-slash: "\f070"; 200 | @fa-var-eyedropper: "\f1fb"; 201 | @fa-var-facebook: "\f09a"; 202 | @fa-var-facebook-f: "\f09a"; 203 | @fa-var-facebook-official: "\f230"; 204 | @fa-var-facebook-square: "\f082"; 205 | @fa-var-fast-backward: "\f049"; 206 | @fa-var-fast-forward: "\f050"; 207 | @fa-var-fax: "\f1ac"; 208 | @fa-var-female: "\f182"; 209 | @fa-var-fighter-jet: "\f0fb"; 210 | @fa-var-file: "\f15b"; 211 | @fa-var-file-archive-o: "\f1c6"; 212 | @fa-var-file-audio-o: "\f1c7"; 213 | @fa-var-file-code-o: "\f1c9"; 214 | @fa-var-file-excel-o: "\f1c3"; 215 | @fa-var-file-image-o: "\f1c5"; 216 | @fa-var-file-movie-o: "\f1c8"; 217 | @fa-var-file-o: "\f016"; 218 | @fa-var-file-pdf-o: "\f1c1"; 219 | @fa-var-file-photo-o: "\f1c5"; 220 | @fa-var-file-picture-o: "\f1c5"; 221 | @fa-var-file-powerpoint-o: "\f1c4"; 222 | @fa-var-file-sound-o: "\f1c7"; 223 | @fa-var-file-text: "\f15c"; 224 | @fa-var-file-text-o: "\f0f6"; 225 | @fa-var-file-video-o: "\f1c8"; 226 | @fa-var-file-word-o: "\f1c2"; 227 | @fa-var-file-zip-o: "\f1c6"; 228 | @fa-var-files-o: "\f0c5"; 229 | @fa-var-film: "\f008"; 230 | @fa-var-filter: "\f0b0"; 231 | @fa-var-fire: "\f06d"; 232 | @fa-var-fire-extinguisher: "\f134"; 233 | @fa-var-flag: "\f024"; 234 | @fa-var-flag-checkered: "\f11e"; 235 | @fa-var-flag-o: "\f11d"; 236 | @fa-var-flash: "\f0e7"; 237 | @fa-var-flask: "\f0c3"; 238 | @fa-var-flickr: "\f16e"; 239 | @fa-var-floppy-o: "\f0c7"; 240 | @fa-var-folder: "\f07b"; 241 | @fa-var-folder-o: "\f114"; 242 | @fa-var-folder-open: "\f07c"; 243 | @fa-var-folder-open-o: "\f115"; 244 | @fa-var-font: "\f031"; 245 | @fa-var-forumbee: "\f211"; 246 | @fa-var-forward: "\f04e"; 247 | @fa-var-foursquare: "\f180"; 248 | @fa-var-frown-o: "\f119"; 249 | @fa-var-futbol-o: "\f1e3"; 250 | @fa-var-gamepad: "\f11b"; 251 | @fa-var-gavel: "\f0e3"; 252 | @fa-var-gbp: "\f154"; 253 | @fa-var-ge: "\f1d1"; 254 | @fa-var-gear: "\f013"; 255 | @fa-var-gears: "\f085"; 256 | @fa-var-genderless: "\f1db"; 257 | @fa-var-gift: "\f06b"; 258 | @fa-var-git: "\f1d3"; 259 | @fa-var-git-square: "\f1d2"; 260 | @fa-var-github: "\f09b"; 261 | @fa-var-github-alt: "\f113"; 262 | @fa-var-github-square: "\f092"; 263 | @fa-var-gittip: "\f184"; 264 | @fa-var-glass: "\f000"; 265 | @fa-var-globe: "\f0ac"; 266 | @fa-var-google: "\f1a0"; 267 | @fa-var-google-plus: "\f0d5"; 268 | @fa-var-google-plus-square: "\f0d4"; 269 | @fa-var-google-wallet: "\f1ee"; 270 | @fa-var-graduation-cap: "\f19d"; 271 | @fa-var-gratipay: "\f184"; 272 | @fa-var-group: "\f0c0"; 273 | @fa-var-h-square: "\f0fd"; 274 | @fa-var-hacker-news: "\f1d4"; 275 | @fa-var-hand-o-down: "\f0a7"; 276 | @fa-var-hand-o-left: "\f0a5"; 277 | @fa-var-hand-o-right: "\f0a4"; 278 | @fa-var-hand-o-up: "\f0a6"; 279 | @fa-var-hdd-o: "\f0a0"; 280 | @fa-var-header: "\f1dc"; 281 | @fa-var-headphones: "\f025"; 282 | @fa-var-heart: "\f004"; 283 | @fa-var-heart-o: "\f08a"; 284 | @fa-var-heartbeat: "\f21e"; 285 | @fa-var-history: "\f1da"; 286 | @fa-var-home: "\f015"; 287 | @fa-var-hospital-o: "\f0f8"; 288 | @fa-var-hotel: "\f236"; 289 | @fa-var-html5: "\f13b"; 290 | @fa-var-ils: "\f20b"; 291 | @fa-var-image: "\f03e"; 292 | @fa-var-inbox: "\f01c"; 293 | @fa-var-indent: "\f03c"; 294 | @fa-var-info: "\f129"; 295 | @fa-var-info-circle: "\f05a"; 296 | @fa-var-inr: "\f156"; 297 | @fa-var-instagram: "\f16d"; 298 | @fa-var-institution: "\f19c"; 299 | @fa-var-ioxhost: "\f208"; 300 | @fa-var-italic: "\f033"; 301 | @fa-var-joomla: "\f1aa"; 302 | @fa-var-jpy: "\f157"; 303 | @fa-var-jsfiddle: "\f1cc"; 304 | @fa-var-key: "\f084"; 305 | @fa-var-keyboard-o: "\f11c"; 306 | @fa-var-krw: "\f159"; 307 | @fa-var-language: "\f1ab"; 308 | @fa-var-laptop: "\f109"; 309 | @fa-var-lastfm: "\f202"; 310 | @fa-var-lastfm-square: "\f203"; 311 | @fa-var-leaf: "\f06c"; 312 | @fa-var-leanpub: "\f212"; 313 | @fa-var-legal: "\f0e3"; 314 | @fa-var-lemon-o: "\f094"; 315 | @fa-var-level-down: "\f149"; 316 | @fa-var-level-up: "\f148"; 317 | @fa-var-life-bouy: "\f1cd"; 318 | @fa-var-life-buoy: "\f1cd"; 319 | @fa-var-life-ring: "\f1cd"; 320 | @fa-var-life-saver: "\f1cd"; 321 | @fa-var-lightbulb-o: "\f0eb"; 322 | @fa-var-line-chart: "\f201"; 323 | @fa-var-link: "\f0c1"; 324 | @fa-var-linkedin: "\f0e1"; 325 | @fa-var-linkedin-square: "\f08c"; 326 | @fa-var-linux: "\f17c"; 327 | @fa-var-list: "\f03a"; 328 | @fa-var-list-alt: "\f022"; 329 | @fa-var-list-ol: "\f0cb"; 330 | @fa-var-list-ul: "\f0ca"; 331 | @fa-var-location-arrow: "\f124"; 332 | @fa-var-lock: "\f023"; 333 | @fa-var-long-arrow-down: "\f175"; 334 | @fa-var-long-arrow-left: "\f177"; 335 | @fa-var-long-arrow-right: "\f178"; 336 | @fa-var-long-arrow-up: "\f176"; 337 | @fa-var-magic: "\f0d0"; 338 | @fa-var-magnet: "\f076"; 339 | @fa-var-mail-forward: "\f064"; 340 | @fa-var-mail-reply: "\f112"; 341 | @fa-var-mail-reply-all: "\f122"; 342 | @fa-var-male: "\f183"; 343 | @fa-var-map-marker: "\f041"; 344 | @fa-var-mars: "\f222"; 345 | @fa-var-mars-double: "\f227"; 346 | @fa-var-mars-stroke: "\f229"; 347 | @fa-var-mars-stroke-h: "\f22b"; 348 | @fa-var-mars-stroke-v: "\f22a"; 349 | @fa-var-maxcdn: "\f136"; 350 | @fa-var-meanpath: "\f20c"; 351 | @fa-var-medium: "\f23a"; 352 | @fa-var-medkit: "\f0fa"; 353 | @fa-var-meh-o: "\f11a"; 354 | @fa-var-mercury: "\f223"; 355 | @fa-var-microphone: "\f130"; 356 | @fa-var-microphone-slash: "\f131"; 357 | @fa-var-minus: "\f068"; 358 | @fa-var-minus-circle: "\f056"; 359 | @fa-var-minus-square: "\f146"; 360 | @fa-var-minus-square-o: "\f147"; 361 | @fa-var-mobile: "\f10b"; 362 | @fa-var-mobile-phone: "\f10b"; 363 | @fa-var-money: "\f0d6"; 364 | @fa-var-moon-o: "\f186"; 365 | @fa-var-mortar-board: "\f19d"; 366 | @fa-var-motorcycle: "\f21c"; 367 | @fa-var-music: "\f001"; 368 | @fa-var-navicon: "\f0c9"; 369 | @fa-var-neuter: "\f22c"; 370 | @fa-var-newspaper-o: "\f1ea"; 371 | @fa-var-openid: "\f19b"; 372 | @fa-var-outdent: "\f03b"; 373 | @fa-var-pagelines: "\f18c"; 374 | @fa-var-paint-brush: "\f1fc"; 375 | @fa-var-paper-plane: "\f1d8"; 376 | @fa-var-paper-plane-o: "\f1d9"; 377 | @fa-var-paperclip: "\f0c6"; 378 | @fa-var-paragraph: "\f1dd"; 379 | @fa-var-paste: "\f0ea"; 380 | @fa-var-pause: "\f04c"; 381 | @fa-var-paw: "\f1b0"; 382 | @fa-var-paypal: "\f1ed"; 383 | @fa-var-pencil: "\f040"; 384 | @fa-var-pencil-square: "\f14b"; 385 | @fa-var-pencil-square-o: "\f044"; 386 | @fa-var-phone: "\f095"; 387 | @fa-var-phone-square: "\f098"; 388 | @fa-var-photo: "\f03e"; 389 | @fa-var-picture-o: "\f03e"; 390 | @fa-var-pie-chart: "\f200"; 391 | @fa-var-pied-piper: "\f1a7"; 392 | @fa-var-pied-piper-alt: "\f1a8"; 393 | @fa-var-pinterest: "\f0d2"; 394 | @fa-var-pinterest-p: "\f231"; 395 | @fa-var-pinterest-square: "\f0d3"; 396 | @fa-var-plane: "\f072"; 397 | @fa-var-play: "\f04b"; 398 | @fa-var-play-circle: "\f144"; 399 | @fa-var-play-circle-o: "\f01d"; 400 | @fa-var-plug: "\f1e6"; 401 | @fa-var-plus: "\f067"; 402 | @fa-var-plus-circle: "\f055"; 403 | @fa-var-plus-square: "\f0fe"; 404 | @fa-var-plus-square-o: "\f196"; 405 | @fa-var-power-off: "\f011"; 406 | @fa-var-print: "\f02f"; 407 | @fa-var-puzzle-piece: "\f12e"; 408 | @fa-var-qq: "\f1d6"; 409 | @fa-var-qrcode: "\f029"; 410 | @fa-var-question: "\f128"; 411 | @fa-var-question-circle: "\f059"; 412 | @fa-var-quote-left: "\f10d"; 413 | @fa-var-quote-right: "\f10e"; 414 | @fa-var-ra: "\f1d0"; 415 | @fa-var-random: "\f074"; 416 | @fa-var-rebel: "\f1d0"; 417 | @fa-var-recycle: "\f1b8"; 418 | @fa-var-reddit: "\f1a1"; 419 | @fa-var-reddit-square: "\f1a2"; 420 | @fa-var-refresh: "\f021"; 421 | @fa-var-remove: "\f00d"; 422 | @fa-var-renren: "\f18b"; 423 | @fa-var-reorder: "\f0c9"; 424 | @fa-var-repeat: "\f01e"; 425 | @fa-var-reply: "\f112"; 426 | @fa-var-reply-all: "\f122"; 427 | @fa-var-retweet: "\f079"; 428 | @fa-var-rmb: "\f157"; 429 | @fa-var-road: "\f018"; 430 | @fa-var-rocket: "\f135"; 431 | @fa-var-rotate-left: "\f0e2"; 432 | @fa-var-rotate-right: "\f01e"; 433 | @fa-var-rouble: "\f158"; 434 | @fa-var-rss: "\f09e"; 435 | @fa-var-rss-square: "\f143"; 436 | @fa-var-rub: "\f158"; 437 | @fa-var-ruble: "\f158"; 438 | @fa-var-rupee: "\f156"; 439 | @fa-var-save: "\f0c7"; 440 | @fa-var-scissors: "\f0c4"; 441 | @fa-var-search: "\f002"; 442 | @fa-var-search-minus: "\f010"; 443 | @fa-var-search-plus: "\f00e"; 444 | @fa-var-sellsy: "\f213"; 445 | @fa-var-send: "\f1d8"; 446 | @fa-var-send-o: "\f1d9"; 447 | @fa-var-server: "\f233"; 448 | @fa-var-share: "\f064"; 449 | @fa-var-share-alt: "\f1e0"; 450 | @fa-var-share-alt-square: "\f1e1"; 451 | @fa-var-share-square: "\f14d"; 452 | @fa-var-share-square-o: "\f045"; 453 | @fa-var-shekel: "\f20b"; 454 | @fa-var-sheqel: "\f20b"; 455 | @fa-var-shield: "\f132"; 456 | @fa-var-ship: "\f21a"; 457 | @fa-var-shirtsinbulk: "\f214"; 458 | @fa-var-shopping-cart: "\f07a"; 459 | @fa-var-sign-in: "\f090"; 460 | @fa-var-sign-out: "\f08b"; 461 | @fa-var-signal: "\f012"; 462 | @fa-var-simplybuilt: "\f215"; 463 | @fa-var-sitemap: "\f0e8"; 464 | @fa-var-skyatlas: "\f216"; 465 | @fa-var-skype: "\f17e"; 466 | @fa-var-slack: "\f198"; 467 | @fa-var-sliders: "\f1de"; 468 | @fa-var-slideshare: "\f1e7"; 469 | @fa-var-smile-o: "\f118"; 470 | @fa-var-soccer-ball-o: "\f1e3"; 471 | @fa-var-sort: "\f0dc"; 472 | @fa-var-sort-alpha-asc: "\f15d"; 473 | @fa-var-sort-alpha-desc: "\f15e"; 474 | @fa-var-sort-amount-asc: "\f160"; 475 | @fa-var-sort-amount-desc: "\f161"; 476 | @fa-var-sort-asc: "\f0de"; 477 | @fa-var-sort-desc: "\f0dd"; 478 | @fa-var-sort-down: "\f0dd"; 479 | @fa-var-sort-numeric-asc: "\f162"; 480 | @fa-var-sort-numeric-desc: "\f163"; 481 | @fa-var-sort-up: "\f0de"; 482 | @fa-var-soundcloud: "\f1be"; 483 | @fa-var-space-shuttle: "\f197"; 484 | @fa-var-spinner: "\f110"; 485 | @fa-var-spoon: "\f1b1"; 486 | @fa-var-spotify: "\f1bc"; 487 | @fa-var-square: "\f0c8"; 488 | @fa-var-square-o: "\f096"; 489 | @fa-var-stack-exchange: "\f18d"; 490 | @fa-var-stack-overflow: "\f16c"; 491 | @fa-var-star: "\f005"; 492 | @fa-var-star-half: "\f089"; 493 | @fa-var-star-half-empty: "\f123"; 494 | @fa-var-star-half-full: "\f123"; 495 | @fa-var-star-half-o: "\f123"; 496 | @fa-var-star-o: "\f006"; 497 | @fa-var-steam: "\f1b6"; 498 | @fa-var-steam-square: "\f1b7"; 499 | @fa-var-step-backward: "\f048"; 500 | @fa-var-step-forward: "\f051"; 501 | @fa-var-stethoscope: "\f0f1"; 502 | @fa-var-stop: "\f04d"; 503 | @fa-var-street-view: "\f21d"; 504 | @fa-var-strikethrough: "\f0cc"; 505 | @fa-var-stumbleupon: "\f1a4"; 506 | @fa-var-stumbleupon-circle: "\f1a3"; 507 | @fa-var-subscript: "\f12c"; 508 | @fa-var-subway: "\f239"; 509 | @fa-var-suitcase: "\f0f2"; 510 | @fa-var-sun-o: "\f185"; 511 | @fa-var-superscript: "\f12b"; 512 | @fa-var-support: "\f1cd"; 513 | @fa-var-table: "\f0ce"; 514 | @fa-var-tablet: "\f10a"; 515 | @fa-var-tachometer: "\f0e4"; 516 | @fa-var-tag: "\f02b"; 517 | @fa-var-tags: "\f02c"; 518 | @fa-var-tasks: "\f0ae"; 519 | @fa-var-taxi: "\f1ba"; 520 | @fa-var-tencent-weibo: "\f1d5"; 521 | @fa-var-terminal: "\f120"; 522 | @fa-var-text-height: "\f034"; 523 | @fa-var-text-width: "\f035"; 524 | @fa-var-th: "\f00a"; 525 | @fa-var-th-large: "\f009"; 526 | @fa-var-th-list: "\f00b"; 527 | @fa-var-thumb-tack: "\f08d"; 528 | @fa-var-thumbs-down: "\f165"; 529 | @fa-var-thumbs-o-down: "\f088"; 530 | @fa-var-thumbs-o-up: "\f087"; 531 | @fa-var-thumbs-up: "\f164"; 532 | @fa-var-ticket: "\f145"; 533 | @fa-var-times: "\f00d"; 534 | @fa-var-times-circle: "\f057"; 535 | @fa-var-times-circle-o: "\f05c"; 536 | @fa-var-tint: "\f043"; 537 | @fa-var-toggle-down: "\f150"; 538 | @fa-var-toggle-left: "\f191"; 539 | @fa-var-toggle-off: "\f204"; 540 | @fa-var-toggle-on: "\f205"; 541 | @fa-var-toggle-right: "\f152"; 542 | @fa-var-toggle-up: "\f151"; 543 | @fa-var-train: "\f238"; 544 | @fa-var-transgender: "\f224"; 545 | @fa-var-transgender-alt: "\f225"; 546 | @fa-var-trash: "\f1f8"; 547 | @fa-var-trash-o: "\f014"; 548 | @fa-var-tree: "\f1bb"; 549 | @fa-var-trello: "\f181"; 550 | @fa-var-trophy: "\f091"; 551 | @fa-var-truck: "\f0d1"; 552 | @fa-var-try: "\f195"; 553 | @fa-var-tty: "\f1e4"; 554 | @fa-var-tumblr: "\f173"; 555 | @fa-var-tumblr-square: "\f174"; 556 | @fa-var-turkish-lira: "\f195"; 557 | @fa-var-twitch: "\f1e8"; 558 | @fa-var-twitter: "\f099"; 559 | @fa-var-twitter-square: "\f081"; 560 | @fa-var-umbrella: "\f0e9"; 561 | @fa-var-underline: "\f0cd"; 562 | @fa-var-undo: "\f0e2"; 563 | @fa-var-university: "\f19c"; 564 | @fa-var-unlink: "\f127"; 565 | @fa-var-unlock: "\f09c"; 566 | @fa-var-unlock-alt: "\f13e"; 567 | @fa-var-unsorted: "\f0dc"; 568 | @fa-var-upload: "\f093"; 569 | @fa-var-usd: "\f155"; 570 | @fa-var-user: "\f007"; 571 | @fa-var-user-md: "\f0f0"; 572 | @fa-var-user-plus: "\f234"; 573 | @fa-var-user-secret: "\f21b"; 574 | @fa-var-user-times: "\f235"; 575 | @fa-var-users: "\f0c0"; 576 | @fa-var-venus: "\f221"; 577 | @fa-var-venus-double: "\f226"; 578 | @fa-var-venus-mars: "\f228"; 579 | @fa-var-viacoin: "\f237"; 580 | @fa-var-video-camera: "\f03d"; 581 | @fa-var-vimeo-square: "\f194"; 582 | @fa-var-vine: "\f1ca"; 583 | @fa-var-vk: "\f189"; 584 | @fa-var-volume-down: "\f027"; 585 | @fa-var-volume-off: "\f026"; 586 | @fa-var-volume-up: "\f028"; 587 | @fa-var-warning: "\f071"; 588 | @fa-var-wechat: "\f1d7"; 589 | @fa-var-weibo: "\f18a"; 590 | @fa-var-weixin: "\f1d7"; 591 | @fa-var-whatsapp: "\f232"; 592 | @fa-var-wheelchair: "\f193"; 593 | @fa-var-wifi: "\f1eb"; 594 | @fa-var-windows: "\f17a"; 595 | @fa-var-won: "\f159"; 596 | @fa-var-wordpress: "\f19a"; 597 | @fa-var-wrench: "\f0ad"; 598 | @fa-var-xing: "\f168"; 599 | @fa-var-xing-square: "\f169"; 600 | @fa-var-yahoo: "\f19e"; 601 | @fa-var-yelp: "\f1e9"; 602 | @fa-var-yen: "\f157"; 603 | @fa-var-youtube: "\f167"; 604 | @fa-var-youtube-play: "\f16a"; 605 | @fa-var-youtube-square: "\f166"; 606 | 607 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | 15 | @mixin fa-icon-rotate($degrees, $rotation) { 16 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 17 | -webkit-transform: rotate($degrees); 18 | -ms-transform: rotate($degrees); 19 | transform: rotate($degrees); 20 | } 21 | 22 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 23 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 24 | -webkit-transform: scale($horiz, $vert); 25 | -ms-transform: scale($horiz, $vert); 26 | transform: scale($horiz, $vert); 27 | } 28 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | // Variables 2 | // -------------------------- 3 | 4 | $fa-font-path: "../fonts" !default; 5 | $fa-font-size-base: 14px !default; 6 | //$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.3.0/fonts" !default; // for referencing Bootstrap CDN font files directly 7 | $fa-css-prefix: fa !default; 8 | $fa-version: "4.3.0" !default; 9 | $fa-border-color: #eee !default; 10 | $fa-inverse: #fff !default; 11 | $fa-li-width: (30em / 14) !default; 12 | 13 | $fa-var-adjust: "\f042"; 14 | $fa-var-adn: "\f170"; 15 | $fa-var-align-center: "\f037"; 16 | $fa-var-align-justify: "\f039"; 17 | $fa-var-align-left: "\f036"; 18 | $fa-var-align-right: "\f038"; 19 | $fa-var-ambulance: "\f0f9"; 20 | $fa-var-anchor: "\f13d"; 21 | $fa-var-android: "\f17b"; 22 | $fa-var-angellist: "\f209"; 23 | $fa-var-angle-double-down: "\f103"; 24 | $fa-var-angle-double-left: "\f100"; 25 | $fa-var-angle-double-right: "\f101"; 26 | $fa-var-angle-double-up: "\f102"; 27 | $fa-var-angle-down: "\f107"; 28 | $fa-var-angle-left: "\f104"; 29 | $fa-var-angle-right: "\f105"; 30 | $fa-var-angle-up: "\f106"; 31 | $fa-var-apple: "\f179"; 32 | $fa-var-archive: "\f187"; 33 | $fa-var-area-chart: "\f1fe"; 34 | $fa-var-arrow-circle-down: "\f0ab"; 35 | $fa-var-arrow-circle-left: "\f0a8"; 36 | $fa-var-arrow-circle-o-down: "\f01a"; 37 | $fa-var-arrow-circle-o-left: "\f190"; 38 | $fa-var-arrow-circle-o-right: "\f18e"; 39 | $fa-var-arrow-circle-o-up: "\f01b"; 40 | $fa-var-arrow-circle-right: "\f0a9"; 41 | $fa-var-arrow-circle-up: "\f0aa"; 42 | $fa-var-arrow-down: "\f063"; 43 | $fa-var-arrow-left: "\f060"; 44 | $fa-var-arrow-right: "\f061"; 45 | $fa-var-arrow-up: "\f062"; 46 | $fa-var-arrows: "\f047"; 47 | $fa-var-arrows-alt: "\f0b2"; 48 | $fa-var-arrows-h: "\f07e"; 49 | $fa-var-arrows-v: "\f07d"; 50 | $fa-var-asterisk: "\f069"; 51 | $fa-var-at: "\f1fa"; 52 | $fa-var-automobile: "\f1b9"; 53 | $fa-var-backward: "\f04a"; 54 | $fa-var-ban: "\f05e"; 55 | $fa-var-bank: "\f19c"; 56 | $fa-var-bar-chart: "\f080"; 57 | $fa-var-bar-chart-o: "\f080"; 58 | $fa-var-barcode: "\f02a"; 59 | $fa-var-bars: "\f0c9"; 60 | $fa-var-bed: "\f236"; 61 | $fa-var-beer: "\f0fc"; 62 | $fa-var-behance: "\f1b4"; 63 | $fa-var-behance-square: "\f1b5"; 64 | $fa-var-bell: "\f0f3"; 65 | $fa-var-bell-o: "\f0a2"; 66 | $fa-var-bell-slash: "\f1f6"; 67 | $fa-var-bell-slash-o: "\f1f7"; 68 | $fa-var-bicycle: "\f206"; 69 | $fa-var-binoculars: "\f1e5"; 70 | $fa-var-birthday-cake: "\f1fd"; 71 | $fa-var-bitbucket: "\f171"; 72 | $fa-var-bitbucket-square: "\f172"; 73 | $fa-var-bitcoin: "\f15a"; 74 | $fa-var-bold: "\f032"; 75 | $fa-var-bolt: "\f0e7"; 76 | $fa-var-bomb: "\f1e2"; 77 | $fa-var-book: "\f02d"; 78 | $fa-var-bookmark: "\f02e"; 79 | $fa-var-bookmark-o: "\f097"; 80 | $fa-var-briefcase: "\f0b1"; 81 | $fa-var-btc: "\f15a"; 82 | $fa-var-bug: "\f188"; 83 | $fa-var-building: "\f1ad"; 84 | $fa-var-building-o: "\f0f7"; 85 | $fa-var-bullhorn: "\f0a1"; 86 | $fa-var-bullseye: "\f140"; 87 | $fa-var-bus: "\f207"; 88 | $fa-var-buysellads: "\f20d"; 89 | $fa-var-cab: "\f1ba"; 90 | $fa-var-calculator: "\f1ec"; 91 | $fa-var-calendar: "\f073"; 92 | $fa-var-calendar-o: "\f133"; 93 | $fa-var-camera: "\f030"; 94 | $fa-var-camera-retro: "\f083"; 95 | $fa-var-car: "\f1b9"; 96 | $fa-var-caret-down: "\f0d7"; 97 | $fa-var-caret-left: "\f0d9"; 98 | $fa-var-caret-right: "\f0da"; 99 | $fa-var-caret-square-o-down: "\f150"; 100 | $fa-var-caret-square-o-left: "\f191"; 101 | $fa-var-caret-square-o-right: "\f152"; 102 | $fa-var-caret-square-o-up: "\f151"; 103 | $fa-var-caret-up: "\f0d8"; 104 | $fa-var-cart-arrow-down: "\f218"; 105 | $fa-var-cart-plus: "\f217"; 106 | $fa-var-cc: "\f20a"; 107 | $fa-var-cc-amex: "\f1f3"; 108 | $fa-var-cc-discover: "\f1f2"; 109 | $fa-var-cc-mastercard: "\f1f1"; 110 | $fa-var-cc-paypal: "\f1f4"; 111 | $fa-var-cc-stripe: "\f1f5"; 112 | $fa-var-cc-visa: "\f1f0"; 113 | $fa-var-certificate: "\f0a3"; 114 | $fa-var-chain: "\f0c1"; 115 | $fa-var-chain-broken: "\f127"; 116 | $fa-var-check: "\f00c"; 117 | $fa-var-check-circle: "\f058"; 118 | $fa-var-check-circle-o: "\f05d"; 119 | $fa-var-check-square: "\f14a"; 120 | $fa-var-check-square-o: "\f046"; 121 | $fa-var-chevron-circle-down: "\f13a"; 122 | $fa-var-chevron-circle-left: "\f137"; 123 | $fa-var-chevron-circle-right: "\f138"; 124 | $fa-var-chevron-circle-up: "\f139"; 125 | $fa-var-chevron-down: "\f078"; 126 | $fa-var-chevron-left: "\f053"; 127 | $fa-var-chevron-right: "\f054"; 128 | $fa-var-chevron-up: "\f077"; 129 | $fa-var-child: "\f1ae"; 130 | $fa-var-circle: "\f111"; 131 | $fa-var-circle-o: "\f10c"; 132 | $fa-var-circle-o-notch: "\f1ce"; 133 | $fa-var-circle-thin: "\f1db"; 134 | $fa-var-clipboard: "\f0ea"; 135 | $fa-var-clock-o: "\f017"; 136 | $fa-var-close: "\f00d"; 137 | $fa-var-cloud: "\f0c2"; 138 | $fa-var-cloud-download: "\f0ed"; 139 | $fa-var-cloud-upload: "\f0ee"; 140 | $fa-var-cny: "\f157"; 141 | $fa-var-code: "\f121"; 142 | $fa-var-code-fork: "\f126"; 143 | $fa-var-codepen: "\f1cb"; 144 | $fa-var-coffee: "\f0f4"; 145 | $fa-var-cog: "\f013"; 146 | $fa-var-cogs: "\f085"; 147 | $fa-var-columns: "\f0db"; 148 | $fa-var-comment: "\f075"; 149 | $fa-var-comment-o: "\f0e5"; 150 | $fa-var-comments: "\f086"; 151 | $fa-var-comments-o: "\f0e6"; 152 | $fa-var-compass: "\f14e"; 153 | $fa-var-compress: "\f066"; 154 | $fa-var-connectdevelop: "\f20e"; 155 | $fa-var-copy: "\f0c5"; 156 | $fa-var-copyright: "\f1f9"; 157 | $fa-var-credit-card: "\f09d"; 158 | $fa-var-crop: "\f125"; 159 | $fa-var-crosshairs: "\f05b"; 160 | $fa-var-css3: "\f13c"; 161 | $fa-var-cube: "\f1b2"; 162 | $fa-var-cubes: "\f1b3"; 163 | $fa-var-cut: "\f0c4"; 164 | $fa-var-cutlery: "\f0f5"; 165 | $fa-var-dashboard: "\f0e4"; 166 | $fa-var-dashcube: "\f210"; 167 | $fa-var-database: "\f1c0"; 168 | $fa-var-dedent: "\f03b"; 169 | $fa-var-delicious: "\f1a5"; 170 | $fa-var-desktop: "\f108"; 171 | $fa-var-deviantart: "\f1bd"; 172 | $fa-var-diamond: "\f219"; 173 | $fa-var-digg: "\f1a6"; 174 | $fa-var-dollar: "\f155"; 175 | $fa-var-dot-circle-o: "\f192"; 176 | $fa-var-download: "\f019"; 177 | $fa-var-dribbble: "\f17d"; 178 | $fa-var-dropbox: "\f16b"; 179 | $fa-var-drupal: "\f1a9"; 180 | $fa-var-edit: "\f044"; 181 | $fa-var-eject: "\f052"; 182 | $fa-var-ellipsis-h: "\f141"; 183 | $fa-var-ellipsis-v: "\f142"; 184 | $fa-var-empire: "\f1d1"; 185 | $fa-var-envelope: "\f0e0"; 186 | $fa-var-envelope-o: "\f003"; 187 | $fa-var-envelope-square: "\f199"; 188 | $fa-var-eraser: "\f12d"; 189 | $fa-var-eur: "\f153"; 190 | $fa-var-euro: "\f153"; 191 | $fa-var-exchange: "\f0ec"; 192 | $fa-var-exclamation: "\f12a"; 193 | $fa-var-exclamation-circle: "\f06a"; 194 | $fa-var-exclamation-triangle: "\f071"; 195 | $fa-var-expand: "\f065"; 196 | $fa-var-external-link: "\f08e"; 197 | $fa-var-external-link-square: "\f14c"; 198 | $fa-var-eye: "\f06e"; 199 | $fa-var-eye-slash: "\f070"; 200 | $fa-var-eyedropper: "\f1fb"; 201 | $fa-var-facebook: "\f09a"; 202 | $fa-var-facebook-f: "\f09a"; 203 | $fa-var-facebook-official: "\f230"; 204 | $fa-var-facebook-square: "\f082"; 205 | $fa-var-fast-backward: "\f049"; 206 | $fa-var-fast-forward: "\f050"; 207 | $fa-var-fax: "\f1ac"; 208 | $fa-var-female: "\f182"; 209 | $fa-var-fighter-jet: "\f0fb"; 210 | $fa-var-file: "\f15b"; 211 | $fa-var-file-archive-o: "\f1c6"; 212 | $fa-var-file-audio-o: "\f1c7"; 213 | $fa-var-file-code-o: "\f1c9"; 214 | $fa-var-file-excel-o: "\f1c3"; 215 | $fa-var-file-image-o: "\f1c5"; 216 | $fa-var-file-movie-o: "\f1c8"; 217 | $fa-var-file-o: "\f016"; 218 | $fa-var-file-pdf-o: "\f1c1"; 219 | $fa-var-file-photo-o: "\f1c5"; 220 | $fa-var-file-picture-o: "\f1c5"; 221 | $fa-var-file-powerpoint-o: "\f1c4"; 222 | $fa-var-file-sound-o: "\f1c7"; 223 | $fa-var-file-text: "\f15c"; 224 | $fa-var-file-text-o: "\f0f6"; 225 | $fa-var-file-video-o: "\f1c8"; 226 | $fa-var-file-word-o: "\f1c2"; 227 | $fa-var-file-zip-o: "\f1c6"; 228 | $fa-var-files-o: "\f0c5"; 229 | $fa-var-film: "\f008"; 230 | $fa-var-filter: "\f0b0"; 231 | $fa-var-fire: "\f06d"; 232 | $fa-var-fire-extinguisher: "\f134"; 233 | $fa-var-flag: "\f024"; 234 | $fa-var-flag-checkered: "\f11e"; 235 | $fa-var-flag-o: "\f11d"; 236 | $fa-var-flash: "\f0e7"; 237 | $fa-var-flask: "\f0c3"; 238 | $fa-var-flickr: "\f16e"; 239 | $fa-var-floppy-o: "\f0c7"; 240 | $fa-var-folder: "\f07b"; 241 | $fa-var-folder-o: "\f114"; 242 | $fa-var-folder-open: "\f07c"; 243 | $fa-var-folder-open-o: "\f115"; 244 | $fa-var-font: "\f031"; 245 | $fa-var-forumbee: "\f211"; 246 | $fa-var-forward: "\f04e"; 247 | $fa-var-foursquare: "\f180"; 248 | $fa-var-frown-o: "\f119"; 249 | $fa-var-futbol-o: "\f1e3"; 250 | $fa-var-gamepad: "\f11b"; 251 | $fa-var-gavel: "\f0e3"; 252 | $fa-var-gbp: "\f154"; 253 | $fa-var-ge: "\f1d1"; 254 | $fa-var-gear: "\f013"; 255 | $fa-var-gears: "\f085"; 256 | $fa-var-genderless: "\f1db"; 257 | $fa-var-gift: "\f06b"; 258 | $fa-var-git: "\f1d3"; 259 | $fa-var-git-square: "\f1d2"; 260 | $fa-var-github: "\f09b"; 261 | $fa-var-github-alt: "\f113"; 262 | $fa-var-github-square: "\f092"; 263 | $fa-var-gittip: "\f184"; 264 | $fa-var-glass: "\f000"; 265 | $fa-var-globe: "\f0ac"; 266 | $fa-var-google: "\f1a0"; 267 | $fa-var-google-plus: "\f0d5"; 268 | $fa-var-google-plus-square: "\f0d4"; 269 | $fa-var-google-wallet: "\f1ee"; 270 | $fa-var-graduation-cap: "\f19d"; 271 | $fa-var-gratipay: "\f184"; 272 | $fa-var-group: "\f0c0"; 273 | $fa-var-h-square: "\f0fd"; 274 | $fa-var-hacker-news: "\f1d4"; 275 | $fa-var-hand-o-down: "\f0a7"; 276 | $fa-var-hand-o-left: "\f0a5"; 277 | $fa-var-hand-o-right: "\f0a4"; 278 | $fa-var-hand-o-up: "\f0a6"; 279 | $fa-var-hdd-o: "\f0a0"; 280 | $fa-var-header: "\f1dc"; 281 | $fa-var-headphones: "\f025"; 282 | $fa-var-heart: "\f004"; 283 | $fa-var-heart-o: "\f08a"; 284 | $fa-var-heartbeat: "\f21e"; 285 | $fa-var-history: "\f1da"; 286 | $fa-var-home: "\f015"; 287 | $fa-var-hospital-o: "\f0f8"; 288 | $fa-var-hotel: "\f236"; 289 | $fa-var-html5: "\f13b"; 290 | $fa-var-ils: "\f20b"; 291 | $fa-var-image: "\f03e"; 292 | $fa-var-inbox: "\f01c"; 293 | $fa-var-indent: "\f03c"; 294 | $fa-var-info: "\f129"; 295 | $fa-var-info-circle: "\f05a"; 296 | $fa-var-inr: "\f156"; 297 | $fa-var-instagram: "\f16d"; 298 | $fa-var-institution: "\f19c"; 299 | $fa-var-ioxhost: "\f208"; 300 | $fa-var-italic: "\f033"; 301 | $fa-var-joomla: "\f1aa"; 302 | $fa-var-jpy: "\f157"; 303 | $fa-var-jsfiddle: "\f1cc"; 304 | $fa-var-key: "\f084"; 305 | $fa-var-keyboard-o: "\f11c"; 306 | $fa-var-krw: "\f159"; 307 | $fa-var-language: "\f1ab"; 308 | $fa-var-laptop: "\f109"; 309 | $fa-var-lastfm: "\f202"; 310 | $fa-var-lastfm-square: "\f203"; 311 | $fa-var-leaf: "\f06c"; 312 | $fa-var-leanpub: "\f212"; 313 | $fa-var-legal: "\f0e3"; 314 | $fa-var-lemon-o: "\f094"; 315 | $fa-var-level-down: "\f149"; 316 | $fa-var-level-up: "\f148"; 317 | $fa-var-life-bouy: "\f1cd"; 318 | $fa-var-life-buoy: "\f1cd"; 319 | $fa-var-life-ring: "\f1cd"; 320 | $fa-var-life-saver: "\f1cd"; 321 | $fa-var-lightbulb-o: "\f0eb"; 322 | $fa-var-line-chart: "\f201"; 323 | $fa-var-link: "\f0c1"; 324 | $fa-var-linkedin: "\f0e1"; 325 | $fa-var-linkedin-square: "\f08c"; 326 | $fa-var-linux: "\f17c"; 327 | $fa-var-list: "\f03a"; 328 | $fa-var-list-alt: "\f022"; 329 | $fa-var-list-ol: "\f0cb"; 330 | $fa-var-list-ul: "\f0ca"; 331 | $fa-var-location-arrow: "\f124"; 332 | $fa-var-lock: "\f023"; 333 | $fa-var-long-arrow-down: "\f175"; 334 | $fa-var-long-arrow-left: "\f177"; 335 | $fa-var-long-arrow-right: "\f178"; 336 | $fa-var-long-arrow-up: "\f176"; 337 | $fa-var-magic: "\f0d0"; 338 | $fa-var-magnet: "\f076"; 339 | $fa-var-mail-forward: "\f064"; 340 | $fa-var-mail-reply: "\f112"; 341 | $fa-var-mail-reply-all: "\f122"; 342 | $fa-var-male: "\f183"; 343 | $fa-var-map-marker: "\f041"; 344 | $fa-var-mars: "\f222"; 345 | $fa-var-mars-double: "\f227"; 346 | $fa-var-mars-stroke: "\f229"; 347 | $fa-var-mars-stroke-h: "\f22b"; 348 | $fa-var-mars-stroke-v: "\f22a"; 349 | $fa-var-maxcdn: "\f136"; 350 | $fa-var-meanpath: "\f20c"; 351 | $fa-var-medium: "\f23a"; 352 | $fa-var-medkit: "\f0fa"; 353 | $fa-var-meh-o: "\f11a"; 354 | $fa-var-mercury: "\f223"; 355 | $fa-var-microphone: "\f130"; 356 | $fa-var-microphone-slash: "\f131"; 357 | $fa-var-minus: "\f068"; 358 | $fa-var-minus-circle: "\f056"; 359 | $fa-var-minus-square: "\f146"; 360 | $fa-var-minus-square-o: "\f147"; 361 | $fa-var-mobile: "\f10b"; 362 | $fa-var-mobile-phone: "\f10b"; 363 | $fa-var-money: "\f0d6"; 364 | $fa-var-moon-o: "\f186"; 365 | $fa-var-mortar-board: "\f19d"; 366 | $fa-var-motorcycle: "\f21c"; 367 | $fa-var-music: "\f001"; 368 | $fa-var-navicon: "\f0c9"; 369 | $fa-var-neuter: "\f22c"; 370 | $fa-var-newspaper-o: "\f1ea"; 371 | $fa-var-openid: "\f19b"; 372 | $fa-var-outdent: "\f03b"; 373 | $fa-var-pagelines: "\f18c"; 374 | $fa-var-paint-brush: "\f1fc"; 375 | $fa-var-paper-plane: "\f1d8"; 376 | $fa-var-paper-plane-o: "\f1d9"; 377 | $fa-var-paperclip: "\f0c6"; 378 | $fa-var-paragraph: "\f1dd"; 379 | $fa-var-paste: "\f0ea"; 380 | $fa-var-pause: "\f04c"; 381 | $fa-var-paw: "\f1b0"; 382 | $fa-var-paypal: "\f1ed"; 383 | $fa-var-pencil: "\f040"; 384 | $fa-var-pencil-square: "\f14b"; 385 | $fa-var-pencil-square-o: "\f044"; 386 | $fa-var-phone: "\f095"; 387 | $fa-var-phone-square: "\f098"; 388 | $fa-var-photo: "\f03e"; 389 | $fa-var-picture-o: "\f03e"; 390 | $fa-var-pie-chart: "\f200"; 391 | $fa-var-pied-piper: "\f1a7"; 392 | $fa-var-pied-piper-alt: "\f1a8"; 393 | $fa-var-pinterest: "\f0d2"; 394 | $fa-var-pinterest-p: "\f231"; 395 | $fa-var-pinterest-square: "\f0d3"; 396 | $fa-var-plane: "\f072"; 397 | $fa-var-play: "\f04b"; 398 | $fa-var-play-circle: "\f144"; 399 | $fa-var-play-circle-o: "\f01d"; 400 | $fa-var-plug: "\f1e6"; 401 | $fa-var-plus: "\f067"; 402 | $fa-var-plus-circle: "\f055"; 403 | $fa-var-plus-square: "\f0fe"; 404 | $fa-var-plus-square-o: "\f196"; 405 | $fa-var-power-off: "\f011"; 406 | $fa-var-print: "\f02f"; 407 | $fa-var-puzzle-piece: "\f12e"; 408 | $fa-var-qq: "\f1d6"; 409 | $fa-var-qrcode: "\f029"; 410 | $fa-var-question: "\f128"; 411 | $fa-var-question-circle: "\f059"; 412 | $fa-var-quote-left: "\f10d"; 413 | $fa-var-quote-right: "\f10e"; 414 | $fa-var-ra: "\f1d0"; 415 | $fa-var-random: "\f074"; 416 | $fa-var-rebel: "\f1d0"; 417 | $fa-var-recycle: "\f1b8"; 418 | $fa-var-reddit: "\f1a1"; 419 | $fa-var-reddit-square: "\f1a2"; 420 | $fa-var-refresh: "\f021"; 421 | $fa-var-remove: "\f00d"; 422 | $fa-var-renren: "\f18b"; 423 | $fa-var-reorder: "\f0c9"; 424 | $fa-var-repeat: "\f01e"; 425 | $fa-var-reply: "\f112"; 426 | $fa-var-reply-all: "\f122"; 427 | $fa-var-retweet: "\f079"; 428 | $fa-var-rmb: "\f157"; 429 | $fa-var-road: "\f018"; 430 | $fa-var-rocket: "\f135"; 431 | $fa-var-rotate-left: "\f0e2"; 432 | $fa-var-rotate-right: "\f01e"; 433 | $fa-var-rouble: "\f158"; 434 | $fa-var-rss: "\f09e"; 435 | $fa-var-rss-square: "\f143"; 436 | $fa-var-rub: "\f158"; 437 | $fa-var-ruble: "\f158"; 438 | $fa-var-rupee: "\f156"; 439 | $fa-var-save: "\f0c7"; 440 | $fa-var-scissors: "\f0c4"; 441 | $fa-var-search: "\f002"; 442 | $fa-var-search-minus: "\f010"; 443 | $fa-var-search-plus: "\f00e"; 444 | $fa-var-sellsy: "\f213"; 445 | $fa-var-send: "\f1d8"; 446 | $fa-var-send-o: "\f1d9"; 447 | $fa-var-server: "\f233"; 448 | $fa-var-share: "\f064"; 449 | $fa-var-share-alt: "\f1e0"; 450 | $fa-var-share-alt-square: "\f1e1"; 451 | $fa-var-share-square: "\f14d"; 452 | $fa-var-share-square-o: "\f045"; 453 | $fa-var-shekel: "\f20b"; 454 | $fa-var-sheqel: "\f20b"; 455 | $fa-var-shield: "\f132"; 456 | $fa-var-ship: "\f21a"; 457 | $fa-var-shirtsinbulk: "\f214"; 458 | $fa-var-shopping-cart: "\f07a"; 459 | $fa-var-sign-in: "\f090"; 460 | $fa-var-sign-out: "\f08b"; 461 | $fa-var-signal: "\f012"; 462 | $fa-var-simplybuilt: "\f215"; 463 | $fa-var-sitemap: "\f0e8"; 464 | $fa-var-skyatlas: "\f216"; 465 | $fa-var-skype: "\f17e"; 466 | $fa-var-slack: "\f198"; 467 | $fa-var-sliders: "\f1de"; 468 | $fa-var-slideshare: "\f1e7"; 469 | $fa-var-smile-o: "\f118"; 470 | $fa-var-soccer-ball-o: "\f1e3"; 471 | $fa-var-sort: "\f0dc"; 472 | $fa-var-sort-alpha-asc: "\f15d"; 473 | $fa-var-sort-alpha-desc: "\f15e"; 474 | $fa-var-sort-amount-asc: "\f160"; 475 | $fa-var-sort-amount-desc: "\f161"; 476 | $fa-var-sort-asc: "\f0de"; 477 | $fa-var-sort-desc: "\f0dd"; 478 | $fa-var-sort-down: "\f0dd"; 479 | $fa-var-sort-numeric-asc: "\f162"; 480 | $fa-var-sort-numeric-desc: "\f163"; 481 | $fa-var-sort-up: "\f0de"; 482 | $fa-var-soundcloud: "\f1be"; 483 | $fa-var-space-shuttle: "\f197"; 484 | $fa-var-spinner: "\f110"; 485 | $fa-var-spoon: "\f1b1"; 486 | $fa-var-spotify: "\f1bc"; 487 | $fa-var-square: "\f0c8"; 488 | $fa-var-square-o: "\f096"; 489 | $fa-var-stack-exchange: "\f18d"; 490 | $fa-var-stack-overflow: "\f16c"; 491 | $fa-var-star: "\f005"; 492 | $fa-var-star-half: "\f089"; 493 | $fa-var-star-half-empty: "\f123"; 494 | $fa-var-star-half-full: "\f123"; 495 | $fa-var-star-half-o: "\f123"; 496 | $fa-var-star-o: "\f006"; 497 | $fa-var-steam: "\f1b6"; 498 | $fa-var-steam-square: "\f1b7"; 499 | $fa-var-step-backward: "\f048"; 500 | $fa-var-step-forward: "\f051"; 501 | $fa-var-stethoscope: "\f0f1"; 502 | $fa-var-stop: "\f04d"; 503 | $fa-var-street-view: "\f21d"; 504 | $fa-var-strikethrough: "\f0cc"; 505 | $fa-var-stumbleupon: "\f1a4"; 506 | $fa-var-stumbleupon-circle: "\f1a3"; 507 | $fa-var-subscript: "\f12c"; 508 | $fa-var-subway: "\f239"; 509 | $fa-var-suitcase: "\f0f2"; 510 | $fa-var-sun-o: "\f185"; 511 | $fa-var-superscript: "\f12b"; 512 | $fa-var-support: "\f1cd"; 513 | $fa-var-table: "\f0ce"; 514 | $fa-var-tablet: "\f10a"; 515 | $fa-var-tachometer: "\f0e4"; 516 | $fa-var-tag: "\f02b"; 517 | $fa-var-tags: "\f02c"; 518 | $fa-var-tasks: "\f0ae"; 519 | $fa-var-taxi: "\f1ba"; 520 | $fa-var-tencent-weibo: "\f1d5"; 521 | $fa-var-terminal: "\f120"; 522 | $fa-var-text-height: "\f034"; 523 | $fa-var-text-width: "\f035"; 524 | $fa-var-th: "\f00a"; 525 | $fa-var-th-large: "\f009"; 526 | $fa-var-th-list: "\f00b"; 527 | $fa-var-thumb-tack: "\f08d"; 528 | $fa-var-thumbs-down: "\f165"; 529 | $fa-var-thumbs-o-down: "\f088"; 530 | $fa-var-thumbs-o-up: "\f087"; 531 | $fa-var-thumbs-up: "\f164"; 532 | $fa-var-ticket: "\f145"; 533 | $fa-var-times: "\f00d"; 534 | $fa-var-times-circle: "\f057"; 535 | $fa-var-times-circle-o: "\f05c"; 536 | $fa-var-tint: "\f043"; 537 | $fa-var-toggle-down: "\f150"; 538 | $fa-var-toggle-left: "\f191"; 539 | $fa-var-toggle-off: "\f204"; 540 | $fa-var-toggle-on: "\f205"; 541 | $fa-var-toggle-right: "\f152"; 542 | $fa-var-toggle-up: "\f151"; 543 | $fa-var-train: "\f238"; 544 | $fa-var-transgender: "\f224"; 545 | $fa-var-transgender-alt: "\f225"; 546 | $fa-var-trash: "\f1f8"; 547 | $fa-var-trash-o: "\f014"; 548 | $fa-var-tree: "\f1bb"; 549 | $fa-var-trello: "\f181"; 550 | $fa-var-trophy: "\f091"; 551 | $fa-var-truck: "\f0d1"; 552 | $fa-var-try: "\f195"; 553 | $fa-var-tty: "\f1e4"; 554 | $fa-var-tumblr: "\f173"; 555 | $fa-var-tumblr-square: "\f174"; 556 | $fa-var-turkish-lira: "\f195"; 557 | $fa-var-twitch: "\f1e8"; 558 | $fa-var-twitter: "\f099"; 559 | $fa-var-twitter-square: "\f081"; 560 | $fa-var-umbrella: "\f0e9"; 561 | $fa-var-underline: "\f0cd"; 562 | $fa-var-undo: "\f0e2"; 563 | $fa-var-university: "\f19c"; 564 | $fa-var-unlink: "\f127"; 565 | $fa-var-unlock: "\f09c"; 566 | $fa-var-unlock-alt: "\f13e"; 567 | $fa-var-unsorted: "\f0dc"; 568 | $fa-var-upload: "\f093"; 569 | $fa-var-usd: "\f155"; 570 | $fa-var-user: "\f007"; 571 | $fa-var-user-md: "\f0f0"; 572 | $fa-var-user-plus: "\f234"; 573 | $fa-var-user-secret: "\f21b"; 574 | $fa-var-user-times: "\f235"; 575 | $fa-var-users: "\f0c0"; 576 | $fa-var-venus: "\f221"; 577 | $fa-var-venus-double: "\f226"; 578 | $fa-var-venus-mars: "\f228"; 579 | $fa-var-viacoin: "\f237"; 580 | $fa-var-video-camera: "\f03d"; 581 | $fa-var-vimeo-square: "\f194"; 582 | $fa-var-vine: "\f1ca"; 583 | $fa-var-vk: "\f189"; 584 | $fa-var-volume-down: "\f027"; 585 | $fa-var-volume-off: "\f026"; 586 | $fa-var-volume-up: "\f028"; 587 | $fa-var-warning: "\f071"; 588 | $fa-var-wechat: "\f1d7"; 589 | $fa-var-weibo: "\f18a"; 590 | $fa-var-weixin: "\f1d7"; 591 | $fa-var-whatsapp: "\f232"; 592 | $fa-var-wheelchair: "\f193"; 593 | $fa-var-wifi: "\f1eb"; 594 | $fa-var-windows: "\f17a"; 595 | $fa-var-won: "\f159"; 596 | $fa-var-wordpress: "\f19a"; 597 | $fa-var-wrench: "\f0ad"; 598 | $fa-var-xing: "\f168"; 599 | $fa-var-xing-square: "\f169"; 600 | $fa-var-yahoo: "\f19e"; 601 | $fa-var-yelp: "\f1e9"; 602 | $fa-var-yen: "\f157"; 603 | $fa-var-youtube: "\f167"; 604 | $fa-var-youtube-play: "\f16a"; 605 | $fa-var-youtube-square: "\f166"; 606 | 607 | -------------------------------------------------------------------------------- /youtubeadl/static/vendor/font-awesome/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | -------------------------------------------------------------------------------- /youtubeadl/templates/403.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block main %} 4 |

Sorry, you're not allowed to view that page.

5 | {% endblock %} -------------------------------------------------------------------------------- /youtubeadl/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block main %} 4 |

Sorry, that page does not exist.

5 | {% endblock %} -------------------------------------------------------------------------------- /youtubeadl/templates/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Server Error 6 | 28 | 29 | 30 |
31 |

Server Error :(

32 |

Sorry, but the server encountered an error while processing your request.

33 |
34 | 35 | -------------------------------------------------------------------------------- /youtubeadl/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | {% load compress %} 5 | 6 | 7 | 8 | 9 | {% block title %}Convert YouTube videos to MP3 audio{% endblock %} | YouTube Audio Downloader 10 | 11 | 12 | 13 | {% compress css %} 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | {% block extracss %}{% endblock %} 24 | {% endcompress %} 25 | 26 | 27 | 28 | 29 | 30 | 31 | 37 | 38 |
39 | {% block main %}{% endblock %} 40 |
41 | 42 | {% compress js %} 43 | 44 | 45 | 46 | 47 | {% block extrajs %}{% endblock %} 48 | {% endcompress %} 49 | 50 | 51 | 52 | 55 | 56 | 57 | 66 | 67 | -------------------------------------------------------------------------------- /youtubeadl/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% load static %} 4 | 5 | {% block main %} 6 | 7 | 8 | {% if ad_top %} 9 |
10 | {{ ad_top.code|safe }} 11 |
12 | {% endif %} 13 | 14 |
15 |
16 |
17 | {% csrf_token %} 18 |
19 |

Enter the video URL to convert to MP3

20 |
21 |
22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 |
30 | 31 |
32 |
33 |
34 |
35 | 36 |
37 |
38 |
39 | 40 | 41 | {% if ad_bottom %} 42 |
43 | {{ ad_bottom.code|safe }} 44 |
45 | {% endif %} 46 | 47 | {% endblock %} 48 | 49 | {% block extrajs %} 50 | 51 | {% endblock %} -------------------------------------------------------------------------------- /youtubeadl/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | from django.contrib import admin 3 | 4 | from youtubeadl.apps.downloader.views import DownloadFormView 5 | 6 | 7 | admin.site.site_header = 'YouTube ADL Admin' 8 | admin.site.site_title = 'YouTube ADL Admin' 9 | 10 | urlpatterns = [ 11 | url(r'^$', DownloadFormView.as_view(), name='home'), 12 | url(r'^downloader/', include('youtubeadl.apps.downloader.urls')), 13 | 14 | # Grappelli needs to be defined before the admin. 15 | url(r'^grappelli/', include('grappelli.urls')), 16 | 17 | url(r'^admin/', include(admin.site.urls)), 18 | ] 19 | -------------------------------------------------------------------------------- /youtubeadl/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for youtubeadl project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 15 | 'youtubeadl.settings.local') 16 | 17 | application = get_wsgi_application() 18 | --------------------------------------------------------------------------------