├── Procfile
├── runtime.txt
├── redis.sh
├── static
├── img
│ ├── play.png
│ ├── brand.png
│ ├── marker.png
│ ├── pause.png
│ ├── marker@2X.png
│ ├── pause@2X.png
│ ├── play@2X.png
│ ├── pause-original.png
│ └── play-original.png
├── js
│ ├── upload.js
│ ├── map.js
│ ├── index.js
│ └── bootstrap.min.js
└── css
│ ├── project.css
│ └── bootstrap.min.css
├── requirements.txt
├── templates
├── _formhelpers.html
├── soundcloud-callback.html
├── index.html
├── upload-sound.html
└── base.html
├── .gitignore
├── models.py
├── README.md
├── server.py
└── LICENSE
/Procfile:
--------------------------------------------------------------------------------
1 | web: python server.py
2 |
--------------------------------------------------------------------------------
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-2.7.13
2 |
3 |
--------------------------------------------------------------------------------
/redis.sh:
--------------------------------------------------------------------------------
1 | redis-server /usr/local/etc/redis.conf
2 |
--------------------------------------------------------------------------------
/static/img/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffbr13/socialsoundsproject-map/master/static/img/play.png
--------------------------------------------------------------------------------
/static/img/brand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffbr13/socialsoundsproject-map/master/static/img/brand.png
--------------------------------------------------------------------------------
/static/img/marker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffbr13/socialsoundsproject-map/master/static/img/marker.png
--------------------------------------------------------------------------------
/static/img/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffbr13/socialsoundsproject-map/master/static/img/pause.png
--------------------------------------------------------------------------------
/static/img/marker@2X.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffbr13/socialsoundsproject-map/master/static/img/marker@2X.png
--------------------------------------------------------------------------------
/static/img/pause@2X.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffbr13/socialsoundsproject-map/master/static/img/pause@2X.png
--------------------------------------------------------------------------------
/static/img/play@2X.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffbr13/socialsoundsproject-map/master/static/img/play@2X.png
--------------------------------------------------------------------------------
/static/img/pause-original.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffbr13/socialsoundsproject-map/master/static/img/pause-original.png
--------------------------------------------------------------------------------
/static/img/play-original.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffbr13/socialsoundsproject-map/master/static/img/play-original.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask==0.10.1
2 | Flask-Redis~=0.3.0
3 | Jinja2==2.7.2
4 | MarkupSafe==0.18
5 | WTForms==1.0.5
6 | Werkzeug==0.9.4
7 | fudge==1.0.3
8 | gunicorn==18.0
9 | itsdangerous==0.23
10 | redis==2.9.1
11 | requests==2.2.1
12 | simplejson==3.3.2
13 | soundcloud==0.4.1
14 | wsgiref==0.1.2
15 |
--------------------------------------------------------------------------------
/templates/_formhelpers.html:
--------------------------------------------------------------------------------
1 | {% macro render_field(field, class_str) %}
2 |
7 |
Soundcloud account authenticated successfully!
8 |
9 |
Welcome, {{user.username}}!
10 |
11 |
The Social Sounds Project application will now use your SoundCloud account to store uploaded sounds. Geotagged sounds will be shown on the main map.
12 |
13 | {% endblock %}
14 |
--------------------------------------------------------------------------------
/static/js/upload.js:
--------------------------------------------------------------------------------
1 | // Click-and-drag marker to update form latitude and longitude:
2 | // ------------------------------------------------------------
3 |
4 | position_marker = L.marker([55.946, -3.186], {
5 | icon: soundIcon,
6 | draggable: true
7 | });
8 |
9 | function onMapClick(e) {
10 | $('#latitude').val(e.latlng.lat);
11 | $('#longitude').val(e.latlng.lng);
12 | position_marker.setLatLng(e.latlng).addTo(map);
13 | }
14 |
15 | function onMarkerDrag (e) {
16 | $('#latitude').val(e.target._latlng.lat);
17 | $('#longitude').val(e.target._latlng.lng);
18 | }
19 |
20 | map.on('click', onMapClick);
21 | position_marker.on('drag', onMarkerDrag);
22 |
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block description %}The Social Sounds Project explores the place of sound, audio methods and sonic environments in the social sciences, with practical activities leading to an online installation.{% endblock %}
4 |
5 | {% block content %}
6 |
9 |
12 |
13 | {% from "_formhelpers.html" import render_field %}
14 |
32 |
33 | {% endblock %}
34 |
35 |
36 | {% block js %}
37 |
38 |
39 |
40 |
41 |
42 | {% endblock %}
43 |
--------------------------------------------------------------------------------
/static/js/index.js:
--------------------------------------------------------------------------------
1 | // Load and display SoundCloud sounds:
2 | // -----------------------------------
3 |
4 | SC.initialize({
5 | client_id: '0183346830552e3721f88daf4c6a8f8d'
6 | });
7 |
8 | function loopSound(sound) {
9 | sound.play({
10 | onfinish: function() {
11 | loopSound(sound);
12 | }
13 | });
14 | };
15 |
16 | $.getJSON('/sounds.json', function (json) {
17 | $.each(json.sounds, function (index, sound) {
18 |
19 | var sound_marker = L.marker([sound.latitude, sound.longitude], {icon: playIcon}).addTo(map);
20 |
21 | var popup_str = '
64 | {% with messages = get_flashed_messages(with_categories=True) %}
65 | {% if messages %}
66 | {% for category, message in messages %}
67 |
{{ message }}
68 | {% endfor %}
69 |
70 | {% endif %}
71 | {% endwith %}
72 |
73 | {% block content %}
74 |
Nothing here!
75 | {% endblock %}
76 |
77 |
78 |
79 |
81 |
82 |
83 |
84 |
85 | {% block js %}{% endblock %}
86 |
87 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/server.py:
--------------------------------------------------------------------------------
1 | #!python
2 | # -*- coding: utf-8 -*-
3 | """Back-end server for socialsoundsproject.com"""
4 | import logging
5 | from os import environ as env
6 |
7 | import soundcloud
8 | from flask import Flask, render_template, request, jsonify, redirect, flash
9 | from flask_redis import FlaskRedis
10 |
11 | from models import LOCATIONS, Sound, UploadSoundForm
12 |
13 |
14 | SERVER_URL = env['SERVER_URL']
15 | REDIS_URL = env['REDIS_URL']
16 | SOUNDCLOUD_CLIENT = None
17 | SOUNDCLOUD_SOUNDS = None
18 |
19 | logging.basicConfig(level=logging.DEBUG if env.get('DEBUG') else logging.INFO)
20 |
21 | app = Flask(__name__)
22 | app.secret_key = env['SECRET_KEY']
23 | app.config['REDIS_URL'] = REDIS_URL
24 | REDIS_CACHE = FlaskRedis(app)
25 |
26 |
27 | def init_soundcloud(token_store):
28 | """
29 | Returns SoundCloud client to use.
30 | """
31 | logging.debug('Initialising SoundCloud client...')
32 | return soundcloud.Client(client_id=env['SOUNDCLOUD_CLIENT_ID'],
33 | client_secret=env['SOUNDCLOUD_CLIENT_SECRET'],
34 | username=env['SOUNDCLOUD_ACCOUNT_USERNAME'],
35 | password=env['SOUNDCLOUD_ACCOUNT_PASSWORD'])
36 |
37 |
38 | def get_sounds(soundcloud_client):
39 | """
40 | Get all geolocated Sounds in the authenticated user's stream.
41 | """
42 | logging.info('Fetching sound data from authenticated user\'s SoundCloud stream.')
43 | sounds = []
44 | try:
45 | page_size = 50
46 | offset = 0
47 | tracks = []
48 | page = soundcloud_client.get('/me/tracks', limit=page_size)
49 | while len(page) > 0:
50 | tracks.extend(page)
51 | offset += page_size
52 | page = soundcloud_client.get('/me/tracks', limit=page_size, offset=offset)
53 |
54 | logging.info('Got list of {0} sounds from SoundCloud.'.format(len(tracks)))
55 | except Exception as e:
56 | logging.error('Couldn\'t get SoundCloud sounds, try authenticating: {0}'.format(e))
57 | return
58 |
59 | for track in tracks:
60 | sound = build_sound(track)
61 | if sound:
62 | sounds.append(sound)
63 |
64 | logging.info('Built {0} geolocated sound objects from SoundCloud.'.format(len(sounds)))
65 | return sounds
66 |
67 |
68 | def build_sound(soundcloud_track):
69 | """
70 | Build a Sound from the track object returned by the SoundCloud API,
71 | or return None.
72 | """
73 | try:
74 | logging.debug(u'Building sound object: "{0}"'.format(soundcloud_track.obj.get('title')))
75 | tags = soundcloud_track.obj.get('tag_list').split()
76 | lats = {float(tag.split(u'=')[1]) for tag in tags if u'geo:lat=' in tag}
77 | lons = {float(tag.split(u'=')[1]) for tag in tags if u'geo:lon=' in tag}
78 | human_readable_location = soundcloud_track.obj.get('title')
79 |
80 | if lats and lons:
81 | sound = Sound(soundcloud_id=soundcloud_track.obj.get('id'),
82 | latitude=lats.pop(),
83 | longitude=lons.pop(),
84 | human_readable_location=human_readable_location,
85 | description=soundcloud_track.obj.get('description'))
86 | logging.debug('Sound successfully processed: "{0}"'.format(sound))
87 | return sound
88 |
89 | except Exception as e:
90 | logging.warning(
91 | 'Exception in processing sound "{title}": {exception}'.format(title=soundcloud_track.obj.get('title'),
92 | exception=e)
93 | )
94 | return None
95 |
96 |
97 | def check_sounds_refresh():
98 | """
99 | Gets sounds again, if the