send_from_directory, flash, get_flashed_messages
from werkzeug.middleware.proxy_fix import ProxyFix
-from . import activity, auth, colors, comments, datadir, db, jams, playlists, \
- profiles, push_notifications, songs, users
+from . import activity, auth, colors, comments, datadir, db, dreams_importer, \
+ jams, playlists, profiles, push_notifications, songs, users
from .logutils import flash_and_log
# Logging
app.register_blueprint(activity.bp)
app.register_blueprint(auth.bp)
app.register_blueprint(comments.bp)
+app.register_blueprint(dreams_importer.bp)
app.register_blueprint(jams.bp)
app.register_blueprint(playlists.bp)
app.register_blueprint(profiles.bp)
--- /dev/null
+{% extends "base.html" %}
+{% block title %}Dreams Importer{% endblock %}
+{% block body %}
+
+<h1>dreams importer</h1>
+
+<h2>how it works</h2>
+<p>
+The Dreams importer is a script that runs on my PC. It automates interactions
+with littlesong.place, indreams.me, and Dreams running on my PS5.
+</p>
+<p>
+This means that my PC and PS5 need to be turned on and connected for the
+importer to work. Generally, I will try to make sure it runs at least once a
+week (usually on weekends) when there are songs in the import queue. I will
+also make sure to run it the day of the little song jam to upload any jam
+entries.
+</p>
+<p>
+If more than one weekend has passed and it still hasn't run, please send me an
+email at
+<a href="mailto:littlesongplace@gmail.com">littlesongplace@gmail.com</a>.
+</p>
+<p>
+The script performs the following sequence for each song in the queue:
+<ol>
+ <li>Connect to LSP and get the song's InDreams URL</li>
+ <li>Load the InDreams URL and click the "Play Later" button</li>
+ <li>Remix the element in Dreams</li>
+ <li>Play the song without recording to make sure all samples are loaded</li>
+ <li>Play the song again, recording the audio</li>
+ <li>Upload the song to LSP</li>
+</ol>
+</p>
+<p>
+<i>
+(For the audio nerds: The audio is recorded through a pair of high-quality USB
+audio interfaces, one connected to the PS5 and one to my PC. Both interfaces
+operate at 24 bits/48 kHz. The audio signal gets converted from digital to
+analog and back again, so there will be some inherent losses in this process.
+But in general it should be better than the lossy compressed audio you get from
+most HDMI capture cards or a screen recording.)
+</i>
+</p>
+
+<h2>tips for imports</h2>
+For best results:
+<ul>
+ <li>Make sure your timeline doesn't loop if it isn't supposed to.</li>
+ <li>If it <i>is</i> supposed to loop, use the Fade Out option in the
+ importer to fade out the last 10 seconds of the recording.</li>
+ <li>I have all three audio settings in Dreams turned all the way up. Make
+ sure your song sounds correct in this configuration.</li>
+ <li>When setting the duration in the importer, make sure to leave enough
+ time for any reverb/effect trails at the end. If in doubt, add extra
+ time - any extra silence at the end will be removed automatically.</li>
+ <li>Remember that the Dreams timeline's duration timer will be wrong if you
+ use keyframes to change the tempo - make sure to add extra time to
+ compensate.</li>
+</ul>
+
+{% endblock %}
{% block body %}
-<p>
-<em>Handy Tip:</em>
-If you upload a video (e.g. a PS4/PS5 capture file), the audio will be extracted automatically!
-Most standard audio/video formats are supported - .wav, .mp3, .ogg, .mp4, etc.
-</p>
+<h2 class="mt0">{% if song %}Edit Song{% else %}Upload a New Song{% endif %}</h2>
{% if song %}
<form action="/upload-song?songid={{ song.songid }}" method="post" enctype="multipart/form-data" onsubmit="onUpload()">
- <h2>Edit Song</h2>
{% else %}
<form action="/upload-song{% if eventid %}?eventid={{ eventid }}{% endif %}" method="post" enctype="multipart/form-data" onsubmit="onUpload()">
- <h2>Upload a New Song</h2>
{% endif %}
+ <!-- Upload Types -->
<div class="upload-form">
- <input type="radio" id="file" name="upload-type" value="file" onchange="selectUploadMethod()" checked />
- <label for="file">Upload a song from my device</label><br/>
+ <input type="radio" id="file-radio" name="upload-type" value="file" onchange="selectUploadMethod()" checked />
+ <label for="file-radio">Upload a song from my device</label><br/>
</div>
<div class="upload-form">
- <input type="radio" id="yt" name="upload-type" value="yt" onchange="selectUploadMethod()"/>
- <label for="yt">Import a song from YouTube</label>
+ <input type="radio" id="dreams-radio" name="upload-type" value="dreams" onchange="selectUploadMethod()"/>
+ <label for="dreams-radio">Import a song using the <a href="/dreams-importer">Dreams Importer</a></label>
</div>
- <div class="upload-form" id="audio-file">
+ <div class="upload-form">
+ <input type="radio" id="yt-radio" name="upload-type" value="yt" onchange="selectUploadMethod()"/>
+ <label for="yt-radio">Import a song from YouTube</label>
+ </div>
+
+ <!-- Song Metadata -->
+ <div class="upload-form" id="song-file-container">
<label for="song-file">{% if song %}Replace {% endif %}Audio File</label><br>
- <input type="file" name="song-file" id="song-file" {% if not song %}required{% endif %}>
+ <input type="file" name="song-file" id="song-file" {% if not song %}required{% endif %}><br/>
+ <small>
+ <em>Handy Tip:</em>
+ If you upload a video (e.g. a PS4/PS5 capture file), the audio will be extracted automatically!
+ Most standard audio/video formats are supported - .wav, .mp3, .ogg, .mp4, etc.
+ </small>
</div>
- <div class="upload-form" id="yt-url" hidden>
- <label for="song-url">YouTube URL</label><br>
+ <div class="upload-form" id="song-url-container" hidden>
+ <label for="song-url" id="song-url-label">YouTube URL</label><br>
<input type="url" name="song-url" id="song-url">
</div>
+ <div class="upload-form" id="song-duration-container">
+ <label>Song Duration (Minutes:Seconds)<br>
+ <input type="text" pattern="\d+:\d\d" name="song-duration" value="3:00" /></label>
+ </div>
+ <div class="upload-form" id="fade-out-container">
+ <label>Fade Out (for Loops)<br>
+ <input type="checkbox" name="fade-out" style="margin: 10px"/></label>
+ </div>
<div class="upload-form">
<label for="title">Title</label><br>
<input type="text" name="title" id="song-title" value="{{ song.title }}" maxlength="80" required>
<input type="text" name="collabs" placeholder="@fren_user, John Doe, ..." value="{{ ", ".join(song.collaborators) }}" maxlength="350">
</div>
<div class="upload-form">
- {% if song %}
- <input type="submit" value="Update" />
- {% else %}
- <input type="submit" value="Upload" />
- {% endif %}
+ <input type="submit" value="Submit" />
<p id="uploading" hidden>uploading...</p>
</div>
</form>
// Toggle YouTube import/File upload
function selectUploadMethod() {
- if (document.getElementById("file").checked) {
+ if (document.getElementById("file-radio").checked) {
// Show audio file upload button
- document.getElementById("yt-url").hidden = true;
+ document.getElementById("song-url-container").hidden = true;
document.getElementById("song-url").required = false;
- document.getElementById("audio-file").hidden = false;
+ document.getElementById("song-file-container").hidden = false;
document.getElementById("song-file").required = {% if song %}false{% else %}true{% endif %};
+
+ document.getElementById("song-duration-container").hidden = true;
+ document.getElementById("song-duration-container").required = false;
+
+ document.getElementById("fade-out-container").hidden = true;
}
- else {
+ else if (document.getElementById("yt-radio").checked) {
// Show youtube import URL box
- document.getElementById("yt-url").hidden = false;
+ document.getElementById("song-url-container").hidden = false;
document.getElementById("song-url").required = {% if song %}false{% else %}true{% endif %};
+ document.getElementById("song-url-label").innerText = "YouTube URL"
- document.getElementById("audio-file").hidden = true;
+ document.getElementById("song-file-container").hidden = true;
document.getElementById("song-file").required = false;
+
+ document.getElementById("song-duration-container").hidden = true;
+ document.getElementById("song-duration-container").required = false;
+
+ document.getElementById("fade-out-container").hidden = true;
+ }
+ else if (document.getElementById("dreams-radio").checked) {
+ // Show dreams import URL box
+ document.getElementById("song-url-container").hidden = false;
+ document.getElementById("song-url").required = {% if song %}false{% else %}true{% endif %};
+ document.getElementById("song-url-label").innerText = "InDreams Element URL (element must be public!)"
+
+ document.getElementById("song-file-container").hidden = true;
+ document.getElementById("song-file").required = false;
+
+ document.getElementById("song-duration-container").hidden = false;
+ document.getElementById("song-duration-container").required = true;
+
+ document.getElementById("fade-out-container").hidden = false;
}
}