This repository contains the source code of AuralCandy.Net. Please note that this application is tailored to our needs - it's not a generic, turn-key podcast platform. This source code is released for educational purposes for anyone who wishes to learn more about developing Contentful applications using Ruby and Sinatra.
This source code is distributed under Unlicense and can be used for non-commericial purposes only. Any commercial use of this source code requires explicit permission from the author. This application comes with absolutely no warranty. The author assumes no responsibility of data loss or any other unintended side-effects.
Teemu Tammela
- [email protected]
- www.auralcandy.net
- github.com/teemutammela
- www.linkedin.com/in/teemutammela
- t.me/teemutammela
AuralCandy.Net is a House Music podcast hosted by the Finnish DJ duo MK-Ultra & Mesmic with more than three decades of combined experience under their belt. Ever since its establishment in 2008 AuralCandy.Net podcasts have reached tens of thousands of listeners from over 150 countries. AuralCandy.Net collaborates with over 50 record labels such as Piston Recordings, Bosh Recordings and Monog Records.
- Features
- Requirements
- Installation
- Deployment
- Asset Pipeline
- Application Structure
- Importing Chartable Downloads
- Testing
-
Technology Stack
- Built upon the Sinatra framework
- Utilizes Padrino stand-alone helpers
- Content management and delivery by Contentful
- Ready to be deployed on Heroku (tested with
heroku-22
stack) - Includes sample data and Rack::Test tests
-
Mobile Friendly Responsive Layout
- Built with Bootstrap 4
- Vector icons by Fontawesome 5
- WebP image support on Chromium based browsers (JPEG fallback for Firefox, Safari etc.)
-
Episode Search
- Search by brand and genre
- Pagination and variable items per page
- Sort by date, title or popularity (requires Chartable integration)
-
Embedded Media Player
- Saves player state in localStorage
- Continuous playback between page loads 1)
-
Episode Landing Pages
- Episode description
- Genre tags (as defined in MusicRecording schema)
- Track listing
- Related recording labels
- Related episodes
-
RSS/XML Feed
- Compatible with Apple Podcasts, Google Podcasts, iTunes, VLC Media Player etc.
- Episode descriptions
- Episode images (as defined in iTunes Podcast DTD)
- Genre keywords (as defined in iTunes Podcast DTD)
- Track listing
- Related recording labels
-
Statistics (Optional)
- Download tracking via Chartable
- Import Chartable download count to Contentful
-
Search Engine Optimization
- Machine-readable microdata schemas
- Sitemap XML
- Web Application Manifest
- Support for Twitter Card and Open Graph embedding
-
Performance Optimization
- Efficient use of caching, content compression and headers on the application level
- Low amount of HTTP requests and memory footprint
- JavaScript and SASS asset pipeline via Grunt
- Full Cloudflare compatibility
-
Certification
1) Unless prevented by browser autoplay policy. See Media Engagement Index documentation for further details. Some browsers like Brave will require explicit permission from user to allow autoplay.
- Git
- Ruby (3.3.2)
- Bundler
- npm
- Grunt
- Contentful account & Contentful CLI
- Heroku account & Heroku CLI
- Chartable account (optional)
1) Clone or fork the repository and install the required Ruby gems listed in Gemfile
via Bundler.
$ git clone https://github.com/teemutammela/auralcandy.net.git
$ cd auralcandy.net
$ bundle install
2) Login to Contentful CLI and select the target space.
$ contentful login
$ contentful space use
3) Import content models to target space.
$ contentful space import --content-file import/content-models.json
4) Import example content to target space.
$ contentful space import --content-file import/example-content.json
NOTE! Tests (app/test/tests.rb
) are designed to match the contents of example-content.json
. Altering the example content in Contentful is likely to cause the tests to fail. It is recommended to set up two spaces or environments (e.g. production
and testing
) and keep the unmodified example content in the latter.
Contentful API keys, space ID and Chartable credentials must be set as environment variables. Create a new .env
file by copying the example file.
$ cp .env.example .env
$ nano .env
On Heroku environment variables, also known as Config Vars, can be set either via the Dashboard or via Heroku CLI.
$ heroku config:set VARIABLE_NAME=variable_value
Variable Name | Description |
---|---|
CONTENTFUL_SPACE_ID |
Contentful Space ID (source of content). |
CONTENTFUL_DELIVERY_KEY |
Contentful Delivery API key. |
CONTENTFUL_MANAGEMENT_KEY |
Contentful Management API key. 1) |
CONTENTFUL_ENVIRONMENT |
Contentful environment name (e.g. master ). 1) |
CHARTABLE_PODCAST_ID |
Chartable team and podcast ID (e.g. auralcandynet ). 1) |
CHARTABLE_ACCESS_TOKEN |
Chartable cookie access token. 1) 2) |
CHARTABLE_ID |
Chartable link ID (optional). 3) |
APPLE_PODCAST_ID |
Apple Podcasts ID (optional). 4) |
1) Required only by the Chartable import functionality.
2) Chartable does not officially offer a public API, but it's possible to utilize the dashboard JSON end-point by acquiring the access token from a cookie. Login to Chartable dashboard, open the browser's developer tools and look for a request to URL /dashboard?podcast_id=<PODCAST_ID>
. The access token can be found in a cookie called remember_token
. Please note, that the access token will expire after one year.
3) If ENV["CHARTABLE_ID"]
is not set, @audio_url_chartable
property found in class Episode
simply returns the original Contentful asset URL. Chartable ID can be found at Dashboard → Integrations.
4) If ENV["APPLE_PODCAST_ID"]
is set, a apple-itunes-app
meta tag will be inserted into the <head>
section of the page. Read Apple's Smart App Banners documentation for further information.
1) Start the application via the heroku local
command. Application is now running at http://localhost:9292.
$ heroku local -p 9292
2) Default environment is development
. Set production environment via the APP_ENV
variable.
$ export APP_ENV=production
NOTE! Global variable $base_url
(set in app/modules/podcast/defaults.rb
) forces HTTPS in production mode. This may break some links while running the application in production mode on a local workstation. You may disable this feature by commenting the following line in app/modules/podcast/defaults.rb
.
$base_url = $base_url.sub("http://", "https://") unless settings.development?
1) Create a new Heroku application via the dashboard.
2) Login to Heroku and associate the repository with the Heroku application.
$ heroku login
$ heroku git:remote -a <APP_NAME>
3) Deploy commits to production by pushing to Heroku master repository.
$ git push heroku master
The default URL of the application is https://appname.herokuapp.com
. More domains can be attached to the application via the Settings tab in the dashboard.
NOTE! Before deploying your site to public production environment, change the line Sitemap: https://www.auralcandy.net/sitemap.xml
in public/robots.txt
to match the domain of your production site.
1) Install the required npm packages listed in package.json
.
$ npm install
$ npm install -g grunt-cli
2) Launch the task runner while working with JavaScripts and stylesheets. Upon file save, *.js
and *.scss
files in directories /assets/javascripts/
and /assets/sass/
will be combined and compressed into target directories /public/javascripts/
and /public/stylesheets/
as configured in Gruntfile.js
.
$ grunt watch
Configuration for development
and production
environments is set in app/app.rb
. See Sinatra documentation for further details about configuration settings.
Directory | Description |
---|---|
app/assets |
JavaScripts and SASS stylesheets. |
app/classes |
Classes for wrapping content objects. |
app/modules |
Contentful clients, Chartable functionality and application modules. |
app/public |
Static files (images, compiled JavaScripts and CSS stylesheets etc.). |
app/views |
ERB view templates and partials. |
Modules are included and registered in app/app.rb
. Modules follow Sinatra's standard modular extensions pattern.
Directory | Module | Description |
---|---|---|
app/modules/contentful |
delivery.rb |
Contentful Delivery API client. |
app/modules/contentful |
management.rb |
Contentful Management API client. |
app/modules/chartable |
chartable.rb |
Chartable download count import. |
app/modules/podcast |
defaults.rb |
Shared defaults (brands, genres, search form parameters and footer). |
app/modules/podcast |
helpers.rb |
Generic helpers, mostly for parsing strings for various purposes. |
app/modules/podcast |
queries.rb |
Query content from Contentful and wrap it to objects (registered as helpers). |
app/modules/podcast |
routes.rb |
Route and URL parameter handling. |
Classes are included in app/app.rb
. Classes are wrappers for corresponding Contentful content models. Classes are used for formatting field values, handling related content by wrapping them with appropriate classes, adding helper methods as object properties and defining the accessible properties of said class.
Content Model | Contentful ID | Class | Description |
---|---|---|---|
Brand |
brand |
brand.rb |
Podcast brand 1). |
DJ |
author |
dj.rb |
Author DJ of a podcast episode. |
Episode |
episode |
episode.rb |
Podcast episode. |
Label |
label |
label.rb |
Recording label related to an episode. |
Navigation Anchor |
navigationAnchor |
navigation_anchor.rb |
Navigation menu in-page anchor. |
Navigation Link |
navigationLink |
navigation_link.rb |
Navigation menu internal or external URL. |
1) Brand
content model is also used to manage site's default settings and navigation menu links and anchors. Each Brand
instance can have its unique navigation menu.
Contentful's free Community tier service package enforces a maximum asset size of 50MB, which is insufficient for podcast usage. Therefor the Episode
content type is designed with external audio file hosting such as Amazon S3 in mind. Please note, that Dropbox and Google Drive are not suitable for audio file hosting, as they introduce too many redirects and latency issues.
However, if you have Team or Enterprise service package and wish to host audio files in Contentful and use the asset reference field type, simply apply the following modifications.
1) Add a file attachment field called Audio
to the Episode
content type.
2) Modify the @audio_url
and @file_size
property definitions in app/classes/episode.rb
as follows.
@audio_url = entry.fields[:audio].url
@file_size = entry.fields[:audio].file.details['size']
3) Remove fields File URL
and File Size
from the Episode
content type.
Run the dedicated Rake task to import download statistics from Chartable. On Heroku it's recommended to execute the task daily via the free Scheduler add-on.
$ rake chartable:import
Run the dedicated Rake task to perform tests for all routes defined in routes.rb
using the Rack::Test library. Test cases are defined in app/test/tests.rb
.
NOTE! Enviromental variables CONTENTFUL_DELIVERY_KEY_TEST
and CONTENTFUL_SPACE_ID_TEST
set in the .env
file enable to use different Contentful space for testing and for actual production content.
$ rake podcast:test