├── .gitignore ├── extractor ├── .gitignore ├── package.json ├── pages │ └── .gitkeep └── script.js ├── input.txt ├── readme.md ├── script.sh └── videos └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | videos/ 2 | key.txt 3 | errors.txt 4 | -------------------------------------------------------------------------------- /extractor/.gitignore: -------------------------------------------------------------------------------- 1 | pages/*.html 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /extractor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "input_generator", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": "", 10 | "author": "", 11 | "license": "BSD", 12 | "dependencies": { 13 | "cheerio": "~0.12.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /extractor/pages/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caarlos0-graveyard/railscasts-downloader/8124bf159a7fd15e6d691d57fce5c1844dcc03af/extractor/pages/.gitkeep -------------------------------------------------------------------------------- /extractor/script.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs') 4 | , cheerio = require('cheerio') 5 | , http = require('http'); 6 | 7 | var MAX_PAGE = 48 8 | , EPS = '/episodes/'; 9 | 10 | // get all pages 11 | var getPages = function () { 12 | for (var i = 1; i <= MAX_PAGE; i++) { 13 | getPage(i); 14 | } 15 | }; 16 | 17 | // get a given page 18 | var getPage = function (page) { 19 | console.log('Downloading page %s', page); 20 | var filename = 'pages/' + page + '.html' 21 | , stream = fs.createWriteStream(filename); 22 | 23 | stream.on('close', function () { 24 | parse(filename); 25 | }); 26 | 27 | http.get('http://railscasts.com/?page=' + page, function (res) { 28 | res.pipe(stream); 29 | }); 30 | }; 31 | 32 | // parse a specific filename from some page 33 | var parse = function (filename) { 34 | console.log('\nParsing "%s"', filename); 35 | var html = fs.readFileSync(filename) 36 | , $ = cheerio.load(html); 37 | 38 | $('h2 a[href^="' + EPS + '"]').each(function (i, elem) { 39 | var href = $(this).attr('href'); 40 | appendEpName(href); 41 | }); 42 | }; 43 | 44 | // append it to input.txt 45 | var appendEpName = function (href) { 46 | var epname = href.substring(href.indexOf(EPS) + EPS.length) 47 | , index = epname.indexOf('-') 48 | , number = ('000' + epname.substring(0, index)).slice(-3) 49 | , name = epname.substring(index) 50 | , newEpName = number + name; 51 | 52 | console.log('Found EP "%s"', newEpName); 53 | fs.appendFile('../input.txt', newEpName + '\n', function (err) { 54 | if (err) { 55 | console.error('Failed to append ' + newEpName + ' to input file!'); 56 | } 57 | }); 58 | } 59 | 60 | if (require.main == module) { 61 | fs.unlink('../input.txt', function (err) { 62 | getPages(); 63 | }); 64 | } else { 65 | exports.getPages = getPages; 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /input.txt: -------------------------------------------------------------------------------- 1 | 417-foundation 2 | 416-form-objects 3 | 415-upgrading-to-rails-4 4 | 414-batch-api-requests 5 | 413-fast-tests 6 | 412-fast-rails-commands 7 | 411-performance-testing 8 | 410-ember-part-2 9 | 409-active-model-serializers 10 | 408-ember-part-1 11 | 407-activity-feed-from-scratch 12 | 406-public-activity 13 | 405-angularjs 14 | 403-dynamic-forms 15 | 402-better-errors-railspanel 16 | 401-actioncontroller-live 17 | 400-what-s-new-in-rails-4 18 | 399-autocomplete-search-terms 19 | 398-service-objects 20 | 397-action-view-walkthrough 21 | 396-importing-csv-and-excel 22 | 395-action-controller-walkthrough 23 | 394-sti-and-polymorphic-associations 24 | 393-guest-user-record 25 | 392-a-tour-of-state-machines 26 | 391-testing-javascript-with-phantomjs 27 | 390-turbolinks 28 | 389-multitenancy-with-postgresql 29 | 388-multitenancy-with-scopes 30 | 387-cache-digests 31 | 386-authorization-from-scratch-part-2 32 | 385-authorization-from-scratch-part-1 33 | 384-exploring-rubygems 34 | 383-uploading-to-amazon-s3 35 | 382-tagging 36 | 381-jquery-file-upload 37 | 380-memcached-dalli 38 | 379-template-handlers 39 | 378-fnordmetric 40 | 377-trinidad 41 | 376-jruby-basics 42 | 375-monit 43 | 374-image-manipulation 44 | 373-zero-downtime-deployment 45 | 372-bullet 46 | 371-strong-parameters 47 | 370-ransack 48 | 369-client-side-performance 49 | 368-miniprofiler 50 | 367-celluloid 51 | 366-sidekiq 52 | 365-thread-safety 53 | 364-active-record-reputation-system 54 | 363-facebook-open-graph 55 | 362-exporting-csv-and-excel 56 | 361-facebook-graph-api 57 | 360-facebook-authentication 58 | 359-twitter-integration 59 | 358-brakeman 60 | 357-adding-ssl 61 | 356-dangers-of-session-hijacking 62 | 355-hacking-with-arel 63 | 354-squeel 64 | 353-oauth-with-doorkeeper 65 | 352-securing-an-api 66 | 351-a-look-at-meteor 67 | 350-rest-api-versioning 68 | 349-rails-modularity 69 | 348-the-rails-api-gem 70 | 347-rubber-and-amazon-ec2 71 | 346-wizard-forms-with-wicked 72 | 345-hstore 73 | 344-queue-classic 74 | 343-full-text-search-in-postgresql 75 | 342-migrating-to-postgresql 76 | 341-asset-pipeline-in-production 77 | 340-datatables 78 | 339-chef-solo-basics 79 | 338-globalize3 80 | 337-capistrano-recipes 81 | 336-copycopter 82 | 335-deploying-to-a-vps 83 | 334-compass-css-sprites 84 | 333-extending-refinery-cms 85 | 332-refinery-cms-basics 86 | 331-a-b-testing-with-split 87 | 330-better-sass-with-bourbon 88 | 329-more-on-twitter-bootstrap 89 | 328-twitter-bootstrap-basics 90 | 327-minitest-with-rails 91 | 326-activeattr 92 | 325-backbone-on-rails-part-2 93 | 324-passing-data-to-javascript 94 | 323-backbone-on-rails-part-1 95 | 322-rabl 96 | 321-http-caching 97 | 320-jbuilder 98 | 319-rails-middleware-walkthrough 99 | 318-upgrading-to-rails-3-2 100 | 317-rack-app-from-scratch 101 | 316-private-pub 102 | 315-rollout-and-degrade 103 | 314-pretty-urls-with-friendlyid 104 | 313-receiving-email-with-mailman 105 | 312-sending-html-email 106 | 311-form-builders 107 | 310-getting-started-with-rails 108 | 309-a-shell-scripting-story 109 | 308-oh-my-zsh 110 | 307-elasticsearch-part-2 111 | 306-elasticsearch-part-1 112 | 305-authentication-with-warden 113 | 304-omniauth-identity 114 | 303-publishing-a-gem 115 | 302-in-place-editing 116 | 301-extracting-a-ruby-gem 117 | 300-contributing-to-open-source 118 | 299-rails-initialization-walkthrough 119 | 298-getting-started-with-spree 120 | 297-running-javascript-in-ruby 121 | 296-mercury-editor 122 | 295-sharing-mustache-templates 123 | 294-playing-with-pjax 124 | 293-nginx-unicorn 125 | 292-virtual-machines-with-vagrant 126 | 291-testing-with-vcr 127 | 290-soap-with-savon 128 | 289-paypal-recurring-billing 129 | 288-billing-with-stripe 130 | 287-presenters-from-scratch 131 | 286-draper 132 | 285-spork 133 | 284-active-admin 134 | 283-authentication-with-sorcery 135 | 282-upgrading-to-rails-3-1 136 | 281-foreman 137 | 280-pry-with-rails 138 | 279-understanding-the-asset-pipeline 139 | 278-search-with-sunspot 140 | 277-mountable-engines 141 | 276-testing-time-web-requests 142 | 275-how-i-test 143 | 274-remember-me-reset-password 144 | 273-geocoder 145 | 272-markdown-with-redcarpet 146 | 271-resque 147 | 270-authentication-in-rails-3-1 148 | 269-template-inheritance 149 | 268-sass-basics 150 | 267-coffeescript-basics 151 | 266-http-streaming 152 | 265-rails-3-1-overview 153 | 264-guard 154 | 263-client-side-validations 155 | 262-trees-with-ancestry 156 | 261-testing-javascript-with-jasmine-revised 157 | 261-testing-javascript-with-jasmine 158 | 260-messaging-with-faye 159 | 259-decent-exposure 160 | 258-token-fields-revised 161 | 258-token-fields 162 | 257-request-specs-and-capybara 163 | 256-i18n-backends 164 | 255-undo-with-paper-trail 165 | 254-pagination-with-kaminari 166 | 253-carrierwave-file-uploads 167 | 252-metrics-metrics-metrics 168 | 251-metawhere-metasearch 169 | 250-authentication-from-scratch-revised 170 | 250-authentication-from-scratch 171 | 249-notifications-in-rails-3 172 | 248-offline-apps-part-2 173 | 247-offline-apps-part-1 174 | 246-ajax-history-state 175 | 245-new-gem-with-bundler 176 | 244-gravatar 177 | 243-beanstalkd-and-stalker 178 | 242-thor 179 | 241-simple-omniauth-revised 180 | 241-simple-omniauth 181 | 240-search-sort-paginate-with-ajax 182 | 239-activerecord-relation-walkthrough 183 | 238-mongoid-revised 184 | 238-mongoid 185 | 237-dynamic-attr-accessible 186 | 236-omniauth-part-2 187 | 235-omniauth-part-1 188 | 235-devise-and-omniauth-revised 189 | 234-simple-form-revised 190 | 234-simple-form 191 | 233-engage-with-devise 192 | 232-routing-walkthrough-part-2 193 | 231-routing-walkthrough 194 | 230-inherited-resources 195 | 229-polling-for-changes-revised 196 | 229-polling-for-changes 197 | 228-sortable-table-columns 198 | 227-upgrading-to-rails-3-part-3 199 | 226-upgrading-to-rails-3-part-2 200 | 225-upgrading-to-rails-3-part-1 201 | 224-controllers-in-rails-3 202 | 223-charts-graphs-revised 203 | 223-charts 204 | 222-rack-in-rails-3 205 | 221-subdomains-in-rails-3 206 | 220-pdfkit 207 | 219-active-model 208 | 218-making-generators-in-rails-3 209 | 217-multistep-forms 210 | 216-generators-in-rails-3 211 | 215-advanced-queries-in-rails-3 212 | 214-a-b-testing-with-a-bingo 213 | 213-calendars-revised 214 | 213-calendars 215 | 212-refactoring-dynamic-delegator 216 | 211-validations-in-rails-3 217 | 210-customizing-devise 218 | 209-introducing-devise 219 | 209-devise-revised 220 | 208-erb-blocks-in-rails-3 221 | 207-syntax-highlighting-revised 222 | 207-syntax-highlighting 223 | 206-action-mailer-in-rails-3 224 | 205-unobtrusive-javascript 225 | 204-xss-protection-in-rails-3 226 | 203-routing-in-rails-3 227 | 202-active-record-queries-in-rails-3 228 | 201-bundler-revised 229 | 201-bundler 230 | 200-rails-3-beta-and-rvm 231 | 199-mobile-devices 232 | 198-edit-multiple-individually 233 | 197-nested-model-form-part-2 234 | 196-nested-model-form-revised 235 | 196-nested-model-form-part-1 236 | 195-my-favorite-web-apps-in-2009 237 | 194-mongodb-and-mongomapper 238 | 193-tableless-model 239 | 192-authorization-with-cancan 240 | 191-mechanize 241 | 190-screen-scraping-with-nokogiri 242 | 189-embedded-association 243 | 188-declarative-authorization 244 | 187-testing-exceptions 245 | 186-pickle-with-cucumber 246 | 185-formtastic-part-2 247 | 184-formtastic-part-1 248 | 183-gemcutter-jeweler 249 | 182-cropping-images-revised 250 | 182-cropping-images 251 | 181-include-vs-joins 252 | 180-finding-unused-css 253 | 179-seed-data 254 | 178-seven-security-tips 255 | 177-model-versioning 256 | 176-searchlogic 257 | 175-ajax-history-and-bookmarks 258 | 174-pagination-with-ajax 259 | 173-screen-scraping-with-scrapi 260 | 172-touch-and-cache 261 | 171-delayed-job-revised 262 | 171-delayed-job 263 | 170-openid-with-authlogic 264 | 169-dynamic-page-caching-revised 265 | 169-dynamic-page-caching 266 | 168-feed-parsing 267 | 167-more-on-virtual-attributes 268 | 166-metric-fu 269 | 165-edit-multiple-revised 270 | 165-edit-multiple 271 | 164-cron-in-ruby-revised 272 | 164-cron-in-ruby 273 | 163-self-referential-association 274 | 162-tree-based-navigation-revised 275 | 162-tree-based-navigation 276 | 161-three-profiling-tools 277 | 160-authlogic 278 | 159-more-on-cucumber 279 | 158-factories-not-fixtures-revised 280 | 158-factories-not-fixtures 281 | 157-rspec-matchers-macros 282 | 156-webrat 283 | 155-beginning-with-cucumber 284 | 154-polymorphic-association-revised 285 | 154-polymorphic-association 286 | 153-pdfs-with-prawn-revised 287 | 153-pdfs-with-prawn 288 | 152-rails-2-3-extras 289 | 151-rack-middleware 290 | 150-rails-metal-revised 291 | 150-rails-metal 292 | 149-rails-engines 293 | 148-custom-app-generators-revised 294 | 148-app-templates-in-rails-2-3 295 | 147-sortable-lists-revised 296 | 147-sortable-lists 297 | 146-paypal-express-checkout 298 | 145-integrating-active-merchant 299 | 144-active-merchant-basics 300 | 143-paypal-security 301 | 142-paypal-notifications 302 | 141-paypal-basics 303 | 140-rails-2-2-extras 304 | 139-nested-resources 305 | 138-i18n-revised 306 | 138-i18n 307 | 137-memoization-revised 308 | 137-memoization 309 | 136-jquery-ajax-revised 310 | 136-jquery 311 | 135-making-a-gem 312 | 134-paperclip 313 | 133-capistrano-tasks-revised 314 | 133-capistrano-tasks 315 | 132-helpers-outside-views 316 | 131-going-back 317 | 130-monitoring-with-god 318 | 129-custom-daemon 319 | 128-starling-and-workling 320 | 127-rake-in-background 321 | 126-populating-a-database 322 | 125-dynamic-layouts 323 | 124-beta-invitations 324 | 123-subdomains-revised 325 | 123-subdomains 326 | 122-passenger-in-development 327 | 121-non-active-record-model 328 | 120-thinking-sphinx-revised 329 | 120-thinking-sphinx 330 | 119-session-based-model 331 | 118-liquid 332 | 117-semi-static-pages-revised 333 | 117-semi-static-pages 334 | 116-selenium 335 | 115-model-caching-revised 336 | 115-caching-in-rails-2-1 337 | 114-endless-page-revised 338 | 114-endless-page 339 | 113-contributing-to-rails-with-git 340 | 112-anonymous-scopes 341 | 111-advanced-search-form-revised 342 | 111-advanced-search-form 343 | 110-gem-dependencies 344 | 109-tracking-attribute-changes 345 | 108-named-scope 346 | 107-migrations-in-rails-2-1 347 | 106-time-zones-revised 348 | 106-time-zones-in-rails-2-1 349 | 105-gitting-rails-2-1-rc1 350 | 104-exception-notifications-revised 351 | 104-exception-notifications 352 | 103-site-wide-announcements-revised 353 | 103-site-wide-announcements 354 | 102-auto-complete-association-revised 355 | 102-auto-complete-association 356 | 101-refactoring-out-helper-object 357 | 100-5-view-tips 358 | 099-complex-partials 359 | 098-request-profiling 360 | 097-analyzing-the-production-log 361 | 096-git-on-rails 362 | 095-more-on-activeresource 363 | 094-activeresource-basics 364 | 093-action-caching 365 | 092-make-resourceful 366 | 091-refactoring-long-methods 367 | 090-fragment-caching-revised 368 | 090-fragment-caching 369 | 089-page-caching-revised 370 | 089-page-caching 371 | 088-dynamic-select-menus-revised 372 | 088-dynamic-select-menus 373 | 087-generating-rss-feeds-revised 374 | 087-generating-rss-feeds 375 | 086-logging-variables 376 | 085-yaml-configuration-revised 377 | 085-yaml-configuration-file 378 | 084-cookie-based-session-store 379 | 083-migrations-in-rails-2-0 380 | 082-http-basic-authentication 381 | 081-fixtures-in-rails-2-0 382 | 080-simplify-views-with-rails-2-0 383 | 079-generate-named-routes 384 | 078-generating-pdf-documents 385 | 077-destroy-without-javascript-revised 386 | 077-destroy-without-javascript 387 | 076-scope-out 388 | 075-complex-forms-part-3 389 | 074-complex-forms-part-2 390 | 073-complex-forms-part-1 391 | 072-adding-an-environment-revised 392 | 072-adding-an-environment 393 | 071-testing-controllers-with-rspec 394 | 070-custom-routes 395 | 069-markaby-in-helper 396 | 068-openid-authentication 397 | 067-restful-authentication 398 | 066-custom-rake-tasks 399 | 065-stopping-spam-with-akismet 400 | 064-custom-helper-modules 401 | 063-model-name-in-url-revised 402 | 063-model-name-in-url 403 | 062-hacking-activerecord 404 | 061-sending-email-revised 405 | 061-sending-email 406 | 060-testing-without-fixtures 407 | 059-optimistic-locking-revised 408 | 059-optimistic-locking 409 | 058-how-to-make-a-generator 410 | 057-create-model-through-text-field 411 | 056-the-logger-revised 412 | 056-the-logger 413 | 055-cleaning-up-the-view 414 | 054-debugging-with-ruby-debug 415 | 054-debugging-ruby-revised 416 | 053-handling-exceptions-revised 417 | 053-handling-exceptions 418 | 052-update-through-checkboxes 419 | 051-will-paginate-revised 420 | 051-will-paginate 421 | 050-contributing-to-rails 422 | 049-reading-the-api 423 | 048-console-tricks-revised 424 | 048-console-tricks 425 | 047-two-many-to-many 426 | 046-catch-all-route 427 | 045-rjs-tips 428 | 044-debugging-rjs 429 | 043-ajax-with-rjs 430 | 042-with-options 431 | 041-conditional-validations 432 | 040-blocks-in-view 433 | 039-customize-field-error 434 | 038-multibutton-form 435 | 037-simple-search-form 436 | 036-subversion-on-rails 437 | 035-custom-rest-actions 438 | 034-named-routes 439 | 033-making-a-plugin 440 | 032-time-in-text-field 441 | 031-formatting-time 442 | 030-pretty-page-title 443 | 029-group-by-month 444 | 028-in-groups-of 445 | 027-cross-site-scripting 446 | 026-hackers-love-mass-assignment-revised 447 | 026-hackers-love-mass-assignment 448 | 025-sql-injection 449 | 024-the-stack-trace 450 | 023-counter-cache-column 451 | 022-eager-loading-revised 452 | 022-eager-loading 453 | 021-super-simple-authentication 454 | 020-restricting-access 455 | 019-where-administration-goes 456 | 018-looping-through-flash 457 | 017-habtm-checkboxes-revised 458 | 017-habtm-checkboxes 459 | 016-virtual-attributes-revised 460 | 016-virtual-attributes 461 | 015-fun-with-find-conditions 462 | 014-performing-calculations-on-models 463 | 013-dangers-of-model-in-session 464 | 012-refactoring-user-name-part-3 465 | 011-refactoring-user-name-part-2 466 | 010-refactoring-user-name-part-1 467 | 009-filtering-sensitive-logs 468 | 008-layouts-and-content-for 469 | 007-all-about-layouts 470 | 006-shortcut-blocks-with-symbol-to-proc 471 | 005-using-with-scope 472 | 004-move-find-into-model 473 | 003-find-through-association 474 | 002-dynamic-find-by-methods 475 | 001-caching-with-instance-variables 476 | 477 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # RailsCasts downloader 2 | 3 | ## Disclaimer 4 | 5 | **It's not intended by this repository to incentive and/or do piracy. I use 6 | this script to download the videos for myself and watch them later in my lunch 7 | time and bus. To download PRO videos, you will have to provide your KEY.** 8 | 9 | ![print](http://f.cl.ly/items/3f2T130z3t2E2g1E2p1X/Screen%20Shot%202013-07-09%20at%202.23.25%20AM.png) 10 | 11 | # Usage 12 | 13 | To download PRO videos, create a `key.txt` file with your API key in it. 14 | 15 | You can found your API while downloading a PRO video, the URL will be something 16 | like: 17 | 18 | ``` 19 | http://media.railscasts.com/assets/subscriptions/{KEY}/videos/{EP_NAME}.{FORMAT} 20 | /\ this is the key =) 21 | ``` 22 | 23 | You can buy a PRO railscasts account in [railscast site](http://railscasts.com). 24 | 25 | # Downloading 26 | 27 | There is already a node.js script called `extractor`, which will generate the 28 | `input.txt` file with all EPs. 29 | 30 | Run `./script.sh` to download all EPs specified in `input.txt` or run 31 | `./script.sh 123 321 234` to download episodes 123, 321 and 234. 32 | 33 | I will try to keep `input.txt` file updated, but, if you want to update it 34 | by yourself, just `cd` to `extractor` folder and run: 35 | 36 | ``` 37 | npm install 38 | ./script.js 39 | ``` 40 | 41 | It will automagically update your `input.txt` file. 42 | 43 | Enjoy. 44 | 45 | # Size? 46 | 47 | - Free: 346 episodes = 6.1 GB 48 | - Pro: 140 episodes = 4.9 GB (including revised ones) 49 | - Total: 486 episodes = 11 GB 50 | 51 | # Thanks 52 | 53 | Thanks Ryan Bates for the great videos, great site and great API. 54 | 55 | # Donate 56 | 57 | Please, donate to Ryan instead, or just get a PRO account. It's worth it! 58 | -------------------------------------------------------------------------------- /script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export KEY=$(cat key.txt 2> /dev/null) 4 | export PAID_URL="http://media.railscasts.com/assets/subscriptions" 5 | export FREE_URL="http://media.railscasts.com/assets/episodes/videos" 6 | 7 | echo -e "\n" 8 | 9 | if [[ -z $KEY ]]; then 10 | cat < 🍺 Successfully downloaded ${1}.${FORMAT}!" 89 | ;; 90 | $FILE_ALREADY_EXISTS) 91 | green "---> 🍺 File already exists! Skipping!" 92 | ;; 93 | *) 94 | red "---> 😭 Fail to download ${1}.${FORMAT} =(" 95 | echo ${0} >> errors.txt 96 | ;; 97 | esac 98 | } 99 | 100 | # try to download an Ep 101 | function download_ep { 102 | download_free_ep ${1} || download_paid_ep ${1} 103 | error_treatment ${1} 104 | } 105 | 106 | # load eps from arguments if given. 107 | function loadEps { 108 | if [[ -z "$@" ]]; then 109 | CAT=$(cat input.txt) 110 | else 111 | eps=$(printf " -e %s" "${@}") 112 | CAT="$(cat input.txt | grep -i${eps})" 113 | fi 114 | count=$(echo "${CAT}" | wc -l) 115 | export TOTAL=$(echo ${count} | sed -e 's/^[ \t]*//') 116 | export VIDEOS=${CAT} 117 | } 118 | 119 | # Main function 120 | function main { 121 | bold "Downloading ${TOTAL} videos...\n\n" 122 | export INDEX=1 123 | for video in $VIDEOS; do 124 | bold "\n(${INDEX}/${TOTAL}) Downloading ${video}... \t" 125 | download_ep $video 126 | INDEX=$((${INDEX} + 1)) 127 | done 128 | 129 | green "All done!" 130 | } 131 | 132 | # RUN FORREST RUN! 133 | loadEps ${@} 134 | main 135 | -------------------------------------------------------------------------------- /videos/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caarlos0-graveyard/railscasts-downloader/8124bf159a7fd15e6d691d57fce5c1844dcc03af/videos/.gitkeep --------------------------------------------------------------------------------