├── .DS_Store ├── README.md ├── backend ├── .gitignore ├── .sequelizerc ├── app.js ├── awsS3.js ├── bin │ └── www ├── config │ ├── database.js │ └── index.js ├── db │ ├── migrations │ │ ├── 20210519232259-create-user.js │ │ ├── 20210521203955-create-artist.js │ │ ├── 20210521204043-create-album.js │ │ ├── 20210521204143-create-song.js │ │ ├── 20210521204300-create-comment.js │ │ └── 20210521204338-create-collection.js │ ├── models │ │ ├── album.js │ │ ├── artist.js │ │ ├── collection.js │ │ ├── comment.js │ │ ├── index.js │ │ ├── song.js │ │ └── user.js │ └── seeders │ │ ├── 20210519234104-demo-user.js │ │ ├── 20210521222649-Artist-data.js │ │ ├── 20210521222709-Album-data.js │ │ ├── 20210521222719-Song-data.js │ │ ├── 20210524013811-collection-data.js │ │ └── 20210524014129-comments-seeder.js ├── package-lock.json ├── package.json ├── routes │ ├── api │ │ ├── albums.js │ │ ├── collectionRoutes.js │ │ ├── index.js │ │ ├── search.js │ │ ├── session.js │ │ └── users.js │ └── index.js └── utils │ ├── auth.js │ └── validation.js ├── frontend ├── .gitignore ├── package-lock.json ├── package.json ├── public │ ├── favicon │ │ ├── favicon-16x16.png │ │ └── favicon.ico │ ├── index.html │ └── manifest.json └── src │ ├── App.js │ ├── components │ ├── AlbumPage │ │ ├── AlbumPage.css │ │ └── index.js │ ├── Comment │ │ ├── CommentBox.js │ │ ├── comment.css │ │ └── index.js │ ├── Home │ │ ├── AboutBarBand.js │ │ ├── home.css │ │ └── index.js │ ├── LoginFormPage │ │ ├── LoginForm.css │ │ └── index.js │ ├── Navigation │ │ ├── Navigation.css │ │ ├── ProfileButton.js │ │ ├── SearchBar.js │ │ └── index.js │ ├── SearchResults │ │ ├── index.js │ │ └── search.css │ ├── SignupFormPage │ │ ├── index.js │ │ └── signup.css │ └── UserProfile │ │ ├── Collection.js │ │ ├── CollectionPage.css │ │ ├── EditProfile.js │ │ ├── OtherCollections.js │ │ └── index.js │ ├── images │ ├── audioplayer-screenshot.png │ ├── bar-ext.jpeg │ ├── bar-ext.jpg │ ├── collectionCount-screenshot.png │ ├── countCollection-redux-log.png │ ├── crowd.jpeg │ ├── music.jpeg │ ├── schema.png │ ├── screenshot.png │ ├── search-screenshot.png │ └── sources.txt │ ├── index.css │ ├── index.js │ └── store │ ├── album.js │ ├── collection.js │ ├── collectionCount.js │ ├── comment.js │ ├── csrf.js │ ├── index.js │ └── session.js └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boothjacobs/React-barBand/91e01286b544a859f6e4809d581bb1aef249db98/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BarBand 2 | 3 | [BarBand](https://bar-band.herokuapp.com/) is a Bandcamp clone for cover songs. 4 | 5 | Users can search of browse the database from the navigation bar; a dropdown menu allows the user to submit an empty search string, which will return every entry in that category for browsing. 6 | 7 | ```javascript 8 | router.post('/', asyncHandler(async (req, res, next) => { 9 | const { searchTerm, searchBy } = req.body; 10 | 11 | let recording; 12 | 13 | if (searchBy === "album" && searchTerm) { 14 | recording = await Album.findAll({ 15 | include: { model: Song, include: Artist }, 16 | where: { title: { [Op.iLike]: '%' + searchTerm + '%' } } 17 | }) 18 | } else if (searchBy === "album" && !searchTerm) { 19 | recording = await Album.findAll({ 20 | include: { model: Song, include: Artist } 21 | }) 22 | } else if (searchBy === "song" && searchTerm) { 23 | recording = await Song.findAll({ 24 | include: {model: Artist}, 25 | where: { title: { [Op.iLike]: '%' + searchTerm + '%' } } 26 | }) 27 | } else if (searchBy === "song" && !searchTerm) { 28 | recording = await Song.findAll({ 29 | include: [{ model: Album }, {model: Artist}] 30 | }) 31 | } else if (searchBy === "original-artist" && searchTerm) { 32 | recording = await Song.findAll({ 33 | include: [{ model: Album }, {model: Artist}], 34 | where: { originalArtist: { [Op.iLike]: '%' + searchTerm + '%' } } 35 | }) 36 | } else if (searchBy === "original-artist" && !searchTerm) { 37 | recording = await Song.findAll({ 38 | include: [{ model: Album }, {model: Artist}], 39 | where: { originalArtist: { [Op.iLike]: '%' + searchTerm + '%' } } 40 | }) 41 | } else if (searchBy === "artist" && searchTerm) { 42 | recording = await Artist.findAll({ 43 | include: { model: Song, include: Album }, 44 | where: { name: { [Op.iLike]: '%' + searchTerm + '%' } } 45 | }) 46 | } else if (searchBy === "artist" && !searchTerm) { 47 | recording = await Artist.findAll({ 48 | include: Song 49 | }) 50 | } 51 | 52 | return res.json(recording); 53 | 54 | })); 55 | ``` 56 | 57 |  58 | 59 | A BarBand user has a "collection" of saved albums. Collections can be added (from the album page) and deleted (from the collection page), and each album in a collection displays how many other collections it belongs to. 60 | 61 | ```javascript 62 | //GET COLLECTIONS FOR USER 63 | router.get("/:id", asyncHandler(async (req, res) => { 64 | const collections = await Collection.findAll({ 65 | where: { 66 | 'userId': req.params.id 67 | }, 68 | include: [ 69 | {model: Album}, 70 | {model: User} 71 | ] 72 | }); 73 | res.json(collections); 74 | })); 75 | ``` 76 | 77 |  78 | 79 | On the user's collection page, their own collection is rendered by mapping over the state, and each item in that collection also renders an additional React component which dispatches a countCollections thunk to make a separate database call for the collection data of all users. 80 | 81 | ```javascript 82 | router.post("/count", asyncHandler(async(req, res) => { 83 | const {albumId} = req.body; 84 | const otherCollections = await Collection.count({ 85 | where: { 86 | 'albumId': albumId, 87 | } 88 | }); 89 | const count = {albumId: albumId, count: otherCollections} 90 | 91 | return res.json(count); 92 | })); 93 | ``` 94 | 95 |  96 | 97 | Clicking on an album from the collection, home page, or search results brings the user to an album page, which lists the tracks and their original artists, and features an audio player. 98 | 99 | The "ownThis" function is used to render the "You own this" icon when applicable. 100 | ```javascript 101 | const ownThis = collection.find(collect => collect.albumId === +id ); 102 | ``` 103 | 104 |  105 | 106 | The album page also offers commenting for logged in users with full CRUD. 107 | 108 | 109 | (Future updates will include original recording artists as a database model and the ability to "follow" artists to show them on the home page on login) 110 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | .eslintcache 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /backend/.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | config: path.resolve('config', 'database.js'), 5 | 'models-path': path.resolve('db', 'models'), 6 | 'seeders-path': path.resolve('db', 'seeders'), 7 | 'migrations-path': path.resolve('db', 'migrations'), 8 | }; 9 | -------------------------------------------------------------------------------- /backend/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const morgan = require('morgan'); 3 | const cors = require('cors'); 4 | const csurf = require('csurf'); 5 | const helmet = require('helmet'); 6 | const cookieParser = require('cookie-parser'); 7 | const { ValidationError } = require('sequelize'); 8 | 9 | const { environment } = require('./config'); 10 | const isProduction = environment === 'production'; 11 | 12 | const app = express(); 13 | const routes = require('../backend/routes'); 14 | 15 | app.use(morgan('dev')); 16 | app.use(cookieParser()); 17 | app.use(express.urlencoded({ extended: false })); 18 | app.use(express.json()); 19 | 20 | 21 | if (!isProduction) app.use(cors()); 22 | 23 | app.use(helmet({ contentSecurityPolicy: false })); 24 | 25 | app.use( 26 | csurf({ 27 | cookie: { 28 | secure: isProduction, 29 | sameSite: isProduction && "Lax", 30 | httpOnly: true, 31 | }, 32 | }) 33 | ); 34 | 35 | app.use(routes); 36 | 37 | //resource not found error handler 38 | app.use((_req, _res, next) => { 39 | const err = new Error("The requested resource couldn't be found."); 40 | err.title = "Resource Not Found"; 41 | err.errors = ["The requested resource couldn't be found."]; 42 | err.status = 404; 43 | next(err); 44 | }); 45 | 46 | // Process sequelize errors 47 | app.use((err, _req, _res, next) => { 48 | // check if error is a Sequelize error: 49 | if (err instanceof ValidationError) { 50 | err.errors = err.errors.map((e) => e.message); 51 | err.title = 'Validation error'; 52 | } 53 | next(err); 54 | }); 55 | 56 | // Error formatter 57 | app.use((err, _req, res, _next) => { 58 | res.status(err.status || 500); 59 | console.error(err); 60 | res.json({ 61 | title: err.title || 'Server Error', 62 | message: err.message, 63 | errors: err.errors, 64 | stack: isProduction ? null : err.stack, 65 | }); 66 | }); 67 | 68 | 69 | module.exports = app; 70 | -------------------------------------------------------------------------------- /backend/awsS3.js: -------------------------------------------------------------------------------- 1 | const AWS = require("aws-sdk"); 2 | // name of your bucket here 3 | const NAME_OF_BUCKET = "barband-seeds"; 4 | 5 | const multer = require("multer"); 6 | 7 | const s3 = new AWS.S3({ apiVersion: "2006-03-01" }); 8 | 9 | // --------------------------- Public UPLOAD ------------------------ 10 | 11 | const singlePublicFileUpload = async (file) => { 12 | const { originalname, mimetype, buffer } = await file; 13 | const path = require("path"); 14 | // name of the file in your S3 bucket will be the date in ms plus the extension name 15 | const Key = new Date().getTime().toString() + path.extname(originalname); 16 | const uploadParams = { 17 | Bucket: NAME_OF_BUCKET, 18 | Key, 19 | Body: buffer, 20 | ACL: "public-read", 21 | }; 22 | const result = await s3.upload(uploadParams).promise(); 23 | 24 | // save the name of the file in your bucket as the key in your database to retrieve for later 25 | return result.Location; 26 | }; 27 | 28 | const multiplePublicFileUpload = async (files) => { 29 | return await Promise.all( 30 | files.map((file) => { 31 | return singlePublicFileUpload(file); 32 | }) 33 | ); 34 | }; 35 | 36 | // --------------------------- Prviate UPLOAD ------------------------ 37 | 38 | const singlePrivateFileUpload = async (file) => { 39 | const { originalname, mimetype, buffer } = await file; 40 | const path = require("path"); 41 | // name of the file in your S3 bucket will be the date in ms plus the extension name 42 | const Key = new Date().getTime().toString() + path.extname(originalname); 43 | const uploadParams = { 44 | Bucket: NAME_OF_BUCKET, 45 | Key, 46 | Body: buffer, 47 | }; 48 | const result = await s3.upload(uploadParams).promise(); 49 | 50 | // save the name of the file in your bucket as the key in your database to retrieve for later 51 | return result.Key; 52 | }; 53 | 54 | const multiplePrivateFileUpload = async (files) => { 55 | return await Promise.all( 56 | files.map((file) => { 57 | return singlePrivateFileUpload(file); 58 | }) 59 | ); 60 | }; 61 | 62 | const retrievePrivateFile = (key) => { 63 | let fileUrl; 64 | if (key) { 65 | fileUrl = s3.getSignedUrl("getObject", { 66 | Bucket: NAME_OF_BUCKET, 67 | Key: key, 68 | }); 69 | } 70 | return fileUrl || key; 71 | }; 72 | 73 | // --------------------------- Storage ------------------------ 74 | 75 | const storage = multer.memoryStorage({ 76 | destination: function (req, file, callback) { 77 | callback(null, ""); 78 | }, 79 | }); 80 | 81 | const singleMulterUpload = (nameOfKey) => 82 | multer({ storage: storage }).single(nameOfKey); 83 | const multipleMulterUpload = (nameOfKey) => 84 | multer({ storage: storage }).array(nameOfKey); 85 | 86 | module.exports = { 87 | s3, 88 | singlePublicFileUpload, 89 | multiplePublicFileUpload, 90 | singlePrivateFileUpload, 91 | multiplePrivateFileUpload, 92 | retrievePrivateFile, 93 | singleMulterUpload, 94 | multipleMulterUpload, 95 | }; 96 | -------------------------------------------------------------------------------- /backend/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // backend/bin/www 3 | const { port } = require('../config'); 4 | 5 | const app = require('../app'); 6 | const db = require('../db/models'); 7 | 8 | // Check the database connection before starting the app 9 | db.sequelize 10 | .authenticate() 11 | .then(() => { 12 | console.log('Database connection success! Sequelize is ready to use...'); 13 | 14 | // Start listening for connections 15 | app.listen(port, () => console.log(`Listening on port ${port}...`)); 16 | }) 17 | .catch((err) => { 18 | console.log('Database connection failure.'); 19 | console.error(err); 20 | }); 21 | -------------------------------------------------------------------------------- /backend/config/database.js: -------------------------------------------------------------------------------- 1 | const config = require('./index'); 2 | 3 | const db = config.db; 4 | const username = db.username; 5 | const password = db.password; 6 | const database = db.database; 7 | const host = db.host; 8 | 9 | module.exports = { 10 | development: { 11 | username, 12 | password, 13 | database, 14 | host, 15 | dialect: 'postgres', 16 | seederStorage: 'sequelize', 17 | }, 18 | production: { 19 | use_env_variable: 'DATABASE_URL', 20 | dialect: 'postgres', 21 | seederStorage: 'sequelize', 22 | dialectOptions: { 23 | ssl: { 24 | require: true, 25 | rejectUnauthorized: false, 26 | }, 27 | }, 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /backend/config/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | environment: process.env.NODE_ENV || 'development', 3 | port: process.env.PORT || 5000, 4 | db: { 5 | username: process.env.DB_USERNAME, 6 | password: process.env.DB_PASSWORD, 7 | database: process.env.DB_DATABASE, 8 | host: process.env.DB_HOST, 9 | }, 10 | jwtConfig: { 11 | secret: process.env.JWT_SECRET, 12 | expiresIn: process.env.JWT_EXPIRES_IN, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /backend/db/migrations/20210519232259-create-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Users', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | username: { 12 | type: Sequelize.STRING(30), 13 | allowNull: false, 14 | unique: true 15 | }, 16 | email: { 17 | type: Sequelize.STRING(200), 18 | allowNull: false, 19 | unique: true 20 | }, 21 | location: { 22 | type: Sequelize.STRING(200) 23 | }, 24 | bio: { 25 | type: Sequelize.TEXT 26 | }, 27 | hashedPassword: { 28 | type: Sequelize.STRING.BINARY, 29 | allowNull: false 30 | }, 31 | profileImage: { 32 | type: Sequelize.STRING(200), 33 | }, 34 | createdAt: { 35 | allowNull: false, 36 | type: Sequelize.DATE, 37 | defaultValue: Sequelize.fn('now'), 38 | }, 39 | updatedAt: { 40 | allowNull: false, 41 | type: Sequelize.DATE, 42 | defaultValue: Sequelize.fn('now'), 43 | } 44 | }); 45 | }, 46 | down: (queryInterface, Sequelize) => { 47 | return queryInterface.dropTable('Users'); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /backend/db/migrations/20210521203955-create-artist.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Artists', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | name: { 12 | allowNull: false, 13 | type: Sequelize.STRING(100) 14 | }, 15 | location: { 16 | type: Sequelize.STRING(50) 17 | }, 18 | createdAt: { 19 | allowNull: false, 20 | type: Sequelize.DATE 21 | }, 22 | updatedAt: { 23 | allowNull: false, 24 | type: Sequelize.DATE 25 | } 26 | }); 27 | }, 28 | down: (queryInterface, Sequelize) => { 29 | return queryInterface.dropTable('Artists'); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /backend/db/migrations/20210521204043-create-album.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Albums', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | title: { 12 | allowNull: false, 13 | type: Sequelize.STRING(100) 14 | }, 15 | imgUrl: { 16 | type: Sequelize.STRING 17 | }, 18 | description: { 19 | type: Sequelize.STRING(750) 20 | }, 21 | createdAt: { 22 | allowNull: false, 23 | type: Sequelize.DATE 24 | }, 25 | updatedAt: { 26 | allowNull: false, 27 | type: Sequelize.DATE 28 | } 29 | }); 30 | }, 31 | down: (queryInterface, Sequelize) => { 32 | return queryInterface.dropTable('Albums'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /backend/db/migrations/20210521204143-create-song.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Songs', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | title: { 12 | allowNull: false, 13 | type: Sequelize.STRING(100) 14 | }, 15 | artistId: { 16 | allowNull: false, 17 | references: { model: "Artists" }, 18 | type: Sequelize.INTEGER 19 | }, 20 | originalArtist: { 21 | allowNull: false, 22 | type: Sequelize.STRING(50) 23 | }, 24 | albumId: { 25 | allowNull: false, 26 | references: { model: "Albums" }, 27 | type: Sequelize.INTEGER 28 | }, 29 | fileUrl: { 30 | type: Sequelize.TEXT 31 | }, 32 | createdAt: { 33 | allowNull: false, 34 | type: Sequelize.DATE 35 | }, 36 | updatedAt: { 37 | allowNull: false, 38 | type: Sequelize.DATE 39 | } 40 | }); 41 | }, 42 | down: (queryInterface, Sequelize) => { 43 | return queryInterface.dropTable('Songs'); 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /backend/db/migrations/20210521204300-create-comment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Comments', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | body: { 12 | allowNull: false, 13 | type: Sequelize.STRING(500) 14 | }, 15 | userId: { 16 | allowNull: false, 17 | references: { model: 'Users' }, 18 | type: Sequelize.INTEGER 19 | }, 20 | albumId: { 21 | allowNull: false, 22 | references: { model: 'Albums' }, 23 | type: Sequelize.INTEGER 24 | }, 25 | createdAt: { 26 | allowNull: false, 27 | type: Sequelize.DATE 28 | }, 29 | updatedAt: { 30 | allowNull: false, 31 | type: Sequelize.DATE 32 | } 33 | }); 34 | }, 35 | down: (queryInterface, Sequelize) => { 36 | return queryInterface.dropTable('Comments'); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /backend/db/migrations/20210521204338-create-collection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Collections', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | userId: { 12 | allowNull: false, 13 | references: { model: 'Users' }, 14 | type: Sequelize.INTEGER 15 | }, 16 | albumId: { 17 | allowNull: false, 18 | references: { model: 'Albums' }, 19 | type: Sequelize.INTEGER 20 | }, 21 | createdAt: { 22 | allowNull: false, 23 | type: Sequelize.DATE 24 | }, 25 | updatedAt: { 26 | allowNull: false, 27 | type: Sequelize.DATE 28 | } 29 | }); 30 | }, 31 | down: (queryInterface, Sequelize) => { 32 | return queryInterface.dropTable('Collections'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /backend/db/models/album.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | const Album = sequelize.define('Album', { 4 | title: { 5 | type: DataTypes.STRING, 6 | allowNull: false 7 | }, 8 | imgUrl: DataTypes.STRING, 9 | description: DataTypes.STRING 10 | }, {}); 11 | Album.associate = function(models) { 12 | // associations can be defined here 13 | Album.hasMany(models.Song, { foreignKey: 'albumId' }); 14 | Album.hasMany(models.Comment, { foreignKey: 'albumId' }); 15 | const columnMapping = { 16 | through: 'Collection', 17 | otherKey: 'userId', 18 | foreignKey: 'albumId' 19 | } 20 | Album.belongsToMany(models.User, columnMapping); 21 | Album.hasMany(models.Collection, { foreignKey: 'albumId' }); 22 | }; 23 | return Album; 24 | }; 25 | -------------------------------------------------------------------------------- /backend/db/models/artist.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | const Artist = sequelize.define('Artist', { 4 | name: { 5 | allowNull: false, 6 | type: DataTypes.STRING(100) 7 | }, 8 | location: DataTypes.STRING(50) 9 | }, {}); 10 | Artist.associate = function(models) { 11 | // associations can be defined here 12 | Artist.hasMany(models.Song, { foreignKey: 'artistId' }); 13 | }; 14 | return Artist; 15 | }; 16 | -------------------------------------------------------------------------------- /backend/db/models/collection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | const Collection = sequelize.define('Collection', { 4 | id: { 5 | allowNull: false, 6 | primaryKey: true, 7 | autoIncrement: true, 8 | type: DataTypes.INTEGER 9 | }, 10 | userId: { 11 | type: DataTypes.INTEGER, 12 | allowNull: false, 13 | references: { model: "Users" } 14 | }, 15 | albumId: { 16 | type: DataTypes.INTEGER, 17 | allowNull: false, 18 | references: { model: "Albums" } 19 | } 20 | }, {}); 21 | Collection.associate = function(models) { 22 | // associations can be defined here 23 | Collection.belongsTo(models.User, { foreignKey: 'userId' }); 24 | Collection.belongsTo(models.Album, { foreignKey: 'albumId' }); 25 | }; 26 | return Collection; 27 | }; 28 | -------------------------------------------------------------------------------- /backend/db/models/comment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | const Comment = sequelize.define('Comment', { 4 | body: { 5 | allowNull: false, 6 | type: DataTypes.STRING(500) 7 | }, 8 | userId: { 9 | allowNull: false, 10 | type: DataTypes.INTEGER, 11 | references: { model: "Users" } 12 | }, 13 | albumId: { 14 | allowNull: false, 15 | type: DataTypes.INTEGER, 16 | references: { model: "Albums" } 17 | } 18 | }, {}); 19 | Comment.associate = function(models) { 20 | // associations can be defined here 21 | Comment.belongsTo(models.User, { foreignKey: 'userId' }); 22 | Comment.belongsTo(models.Album, { foreignKey: 'albumId' }); 23 | }; 24 | return Comment; 25 | }; 26 | -------------------------------------------------------------------------------- /backend/db/models/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const Sequelize = require('sequelize'); 6 | const basename = path.basename(__filename); 7 | const env = process.env.NODE_ENV || 'development'; 8 | const config = require(__dirname + '/../../config/database.js')[env]; 9 | const db = {}; 10 | 11 | let sequelize; 12 | if (config.use_env_variable) { 13 | sequelize = new Sequelize(process.env[config.use_env_variable], config); 14 | } else { 15 | sequelize = new Sequelize(config.database, config.username, config.password, config); 16 | } 17 | 18 | fs 19 | .readdirSync(__dirname) 20 | .filter(file => { 21 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 22 | }) 23 | .forEach(file => { 24 | const model = sequelize['import'](path.join(__dirname, file)); 25 | db[model.name] = model; 26 | }); 27 | 28 | Object.keys(db).forEach(modelName => { 29 | if (db[modelName].associate) { 30 | db[modelName].associate(db); 31 | } 32 | }); 33 | 34 | db.sequelize = sequelize; 35 | db.Sequelize = Sequelize; 36 | 37 | module.exports = db; 38 | -------------------------------------------------------------------------------- /backend/db/models/song.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | const Song = sequelize.define('Song', { 4 | title: { 5 | allowNull: false, 6 | type: DataTypes.STRING 7 | }, 8 | artistId: { 9 | type: DataTypes.INTEGER, 10 | allowNull: false, 11 | references: { model: "Artists" } 12 | }, 13 | originalArtist: { 14 | type: DataTypes.STRING 15 | }, 16 | albumId: { 17 | type: DataTypes.INTEGER, 18 | allowNull: false, 19 | references: { model: "Albums" } 20 | }, 21 | fileUrl: DataTypes.TEXT, 22 | originalArtist: { 23 | allowNull: false, 24 | type: DataTypes.STRING 25 | }, 26 | }, {}); 27 | Song.associate = function(models) { 28 | // associations can be defined here 29 | Song.belongsTo(models.Artist, { foreignKey: 'artistId' }); 30 | Song.belongsTo(models.Album, { foreignKey: 'albumId' }); 31 | }; 32 | return Song; 33 | }; 34 | -------------------------------------------------------------------------------- /backend/db/models/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const bcrypt = require('bcryptjs'); 3 | const { Validator } = require('sequelize'); 4 | 5 | module.exports = (sequelize, DataTypes) => { 6 | const User = sequelize.define('User', { 7 | username: { 8 | type: DataTypes.STRING, 9 | allowNull: false, 10 | validate: { 11 | len: [4, 30], 12 | isNotEmail(value) { 13 | if (Validator.isEmail(value)) { 14 | throw new Error('Cannot be an email.'); 15 | } 16 | } 17 | } 18 | }, 19 | email: { 20 | type: DataTypes.STRING, 21 | allowNull: false, 22 | validate: { 23 | len: [3, 200] 24 | } 25 | }, 26 | location: { 27 | type: DataTypes.STRING, 28 | }, 29 | bio: { 30 | type: DataTypes.TEXT, 31 | }, 32 | hashedPassword: { 33 | type: DataTypes.STRING.BINARY, 34 | allowNull: false, 35 | validate: { 36 | len: [60, 60] 37 | } 38 | }, 39 | profileImage: DataTypes.STRING, 40 | }, 41 | { 42 | defaultScope: { 43 | attributes: { 44 | exclude: ['hashedPassword', 'email', 'createdAt', 'updatedAt'], 45 | }, 46 | }, 47 | scopes: { 48 | currentUser: { 49 | attributes: { exclude: ['hashedPassword'] }, 50 | }, 51 | loginUser: { 52 | attributes: {}, 53 | }, 54 | }, 55 | }); 56 | User.associate = function(models) { 57 | // associations can be defined here 58 | User.hasMany(models.Comment, { foreignKey: 'userId' }); 59 | const collectionMapping = { 60 | through: 'Collection', 61 | otherKey: 'albumId', 62 | foreignKey: 'userId' 63 | } 64 | User.belongsToMany(models.Album, collectionMapping); 65 | User.hasMany(models.Collection, { foreignKey: 'userId' }); 66 | }; 67 | 68 | //instance methods 69 | User.prototype.toSafeObject = function() { 70 | //front-end friendly data only 71 | const { id, username, email, location, bio, profileImage } = this; 72 | return { id, username, email, location, bio, profileImage }; 73 | }; 74 | 75 | User.prototype.validatePassword = function (password) { 76 | return bcrypt.compareSync(password, this.hashedPassword.toString()); 77 | }; 78 | 79 | //static methods 80 | User.getCurrentUserById = async function (id) { 81 | return await User.scope('currentUser').findByPk(id); 82 | }; 83 | 84 | User.login = async function ({ credential, password }) { 85 | const { Op } = require('sequelize'); 86 | const user = await User.scope('loginUser').findOne({ 87 | where: { 88 | [Op.or]: { 89 | username: credential, 90 | email: credential, 91 | }, 92 | }, 93 | }); 94 | if (user && user.validatePassword(password)) { 95 | return await User.scope('currentUser').findByPk(user.id); 96 | } 97 | }; 98 | 99 | User.signup = async function ({ username, email, location, bio, password, profileImage }) { 100 | const hashedPassword = bcrypt.hashSync(password); 101 | const user = await User.create({ 102 | username, email, location, bio, hashedPassword, profileImage 103 | }); 104 | return await User.scope('currentUser').findByPk(user.id); 105 | }; 106 | 107 | return User; 108 | }; 109 | -------------------------------------------------------------------------------- /backend/db/seeders/20210519234104-demo-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const faker = require('faker'); 3 | const bcrypt = require('bcryptjs'); 4 | 5 | module.exports = { 6 | up: (queryInterface, Sequelize) => { 7 | return queryInterface.bulkInsert('Users', [ 8 | { 9 | username: 'Demo-lition', 10 | email: 'Evan.Monahan8@hotmail.com', 11 | location: null, 12 | bio: null, 13 | hashedPassword: bcrypt.hashSync('password'), 14 | profileImage: "https://cdn.fakercloud.com/avatars/dudestein_128.jpg" 15 | }, 16 | { 17 | username: 'Bruce', 18 | email: 'Johnathan0@hotmail.com', 19 | location: 'Hoegermouth, CO', 20 | bio: 'Voluptates sit praesentium eos.', 21 | hashedPassword: bcrypt.hashSync('password'), 22 | profileImage: "https://www.insidehook.com/wp-content/uploads/2016/10/Springsteen-2_1200-e1477589531774-1-1.jpg?fit=857%2C1200" 23 | }, 24 | { 25 | username: 'Jazmyn57', 26 | email: 'Wilfrid69@hotmail.com', 27 | location: 'South Maximo, MN', 28 | bio: null, 29 | hashedPassword: bcrypt.hashSync('password'), 30 | profileImage: 'https://cdn.fakercloud.com/avatars/llun_128.jpg' 31 | }, 32 | { 33 | username: 'Lamar.Homenick', 34 | email: 'Wava.Beier15@yahoo.com', 35 | location: 'North Kyleightown, IA', 36 | bio: 'Perferendis minus qui.', 37 | hashedPassword: bcrypt.hashSync('password'), 38 | profileImage: 'https://cdn.fakercloud.com/avatars/hugomano_128.jpg' 39 | }, 40 | { 41 | username: 'FakeUser1', 42 | email: 'Candelario.Bayer@hotmail.com', 43 | location: null, 44 | bio: null, 45 | hashedPassword: bcrypt.hashSync('password'), 46 | profileImage: "https://cdn.fakercloud.com/avatars/S0ufi4n3_128.jpg" 47 | }, 48 | { 49 | username: 'Forest.Hills', 50 | email: 'Precious_Schiller@yahoo.com', 51 | location: null, 52 | bio: 'Aut maiores sapiente quae earum ducimus magni occaecati totam iste.', 53 | hashedPassword: bcrypt.hashSync('password'), 54 | profileImage: "https://cdn.fakercloud.com/avatars/horaciobella_128.jpg" 55 | }, 56 | ], {}); 57 | }, 58 | 59 | down: (queryInterface, Sequelize) => { 60 | const Op = Sequelize.Op; 61 | return queryInterface.bulkDelete('Users', null, {}); 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /backend/db/seeders/20210521222649-Artist-data.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | 6 | return queryInterface.bulkInsert('Artists', [ 7 | {name: "Bartees Strange", location: "New York, NY", createdAt: new Date(), updatedAt: new Date() }, 8 | {name: "Future Teens", location: "New York, NY", createdAt: new Date(), updatedAt: new Date() }, 9 | {name: "Lilith", location: "New York, NY", createdAt: new Date(), updatedAt: new Date() }, 10 | {name: "ManDancing", location: "New York, NY", createdAt: new Date(), updatedAt: new Date() }, 11 | {name: "The Superweaks", location: "Los Angeles, CA", createdAt: new Date(), updatedAt: new Date() }, 12 | {name: "Pushflowers", location: "Los Angeles, CA", createdAt: new Date(), updatedAt: new Date() }, 13 | {name: "Cheer Up", location: "Los Angeles, CA", createdAt: new Date(), updatedAt: new Date() }, 14 | {name: "oldsoul", location: "Los Angeles, CA", createdAt: new Date(), updatedAt: new Date() }, 15 | {name: "Kiki Maddog", location: "Los Angeles, CA", createdAt: new Date(), updatedAt: new Date() }, 16 | {name: "THE AUX", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 17 | {name: "Happy Just To See You", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 18 | {name: "Gabe Goodman", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 19 | {name: "Good Looking Friends", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 20 | {name: "Photocomfort", location: "London, UK", createdAt: new Date(), updatedAt: new Date() }, 21 | {name: "Tuft", location: "London, UK", createdAt: new Date(), updatedAt: new Date() }, 22 | {name: "Wild Pink", location: "London, UK", createdAt: new Date(), updatedAt: new Date() }, 23 | {name: "Provide", location: "London, UK", createdAt: new Date(), updatedAt: new Date() }, 24 | {name: "I Wish I Could Skateboard", location: "London, UK", createdAt: new Date(), updatedAt: new Date() }, 25 | {name: "Dan Campbell", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 26 | {name: "Bad Bad Hats", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 27 | {name: "Mint Green", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 28 | {name: "Chris Farren", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 29 | {name: "Weakened Friends", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 30 | {name: "Adult Mom", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 31 | {name: "Paige Chaplin", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 32 | {name: "Telethon", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 33 | {name: "Calicoco", location: "Boston, MA", createdAt: new Date(), updatedAt: new Date() }, 34 | {name: "Abi Reimold", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 35 | {name: "Allegra Anka", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 36 | {name: "Anika Pyle", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 37 | {name: "Augusta Koch", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 38 | {name: "awakebutstillinbed", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 39 | {name: "Bartley, Clarke, Harris & Harvey", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 40 | {name: "Ben Hughes", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 41 | {name: "Ben Walsh", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 42 | {name: "Black Paw", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 43 | {name: "Brackish", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 44 | {name: "Brittany Corrigan", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 45 | {name: "Broad Cast", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 46 | {name: "Broken Field Runner", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 47 | {name: "Carly Comando", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 48 | {name: "Cave People", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 49 | {name: "Cherry", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 50 | {name: "Constant Companion", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 51 | {name: "Craig Finn", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 52 | {name: "Dan Zimmerman", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 53 | {name: "Expert Timing", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 54 | {name: "Feminine Issues", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 55 | {name: "Grace Vonderkuhn", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 56 | {name: "Greg Mendez", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 57 | {name: "Heart Harbor", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 58 | {name: "Hemming", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 59 | {name: "Hurry", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 60 | {name: "Jack MacCann", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 61 | {name: "Jeff Riddle", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 62 | {name: "Jeff Rosenstock", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 63 | {name: "Jon Loudon", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 64 | {name: "Jordyn Occhipinti", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 65 | {name: "Kayleigh Goldsworthy", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 66 | {name: "Laura Stevenson", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 67 | {name: "Lifted Bells", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 68 | {name: "Lisa Prank", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 69 | {name: "Littler", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 70 | {name: "Lowercase Roses", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 71 | {name: "Lydia Loveless", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 72 | {name: "Manhattan Side Project", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 73 | {name: "Marisa Dabice", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 74 | {name: "Matt Schimelfenig", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 75 | {name: "Maxwell Stern", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 76 | {name: "Mimi Gallagher", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 77 | {name: "Molar", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 78 | {name: "No Thank You", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 79 | {name: "Perry Shall", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 80 | {name: "PJ Bond", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 81 | {name: "Rachel Dispenza", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 82 | {name: "Radiator Hospital", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 83 | {name: "Roger Harvey", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 84 | {name: "Ryan Azada", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 85 | {name: "Snowhore", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 86 | {name: "So Totally", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 87 | {name: "Soul Glo", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 88 | {name: "Strange Relations", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 89 | {name: "Swanning", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 90 | {name: "The Menzingers", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 91 | {name: "TJ King & the Atomic Bomb", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 92 | {name: "Worriers", location: "Philadelphia, PA", createdAt: new Date(), updatedAt: new Date() }, 93 | {name: "Death Cab for Cutie", location: "Seattle, WA", createdAt: new Date(), updatedAt: new Date() }, 94 | {name: "Charly Bliss", location: "New York, NY", createdAt: new Date(), updatedAt: new Date() }, 95 | {name: "Jason Isbell", location: "Nashville, TN", createdAt: new Date(), updatedAt: new Date() }, 96 | {name: "Julien Baker", location: "Nashville, TN", createdAt: new Date(), updatedAt: new Date() }, 97 | {name: "Lucy Dacus", location: "New York, NY", createdAt: new Date(), updatedAt: new Date() }, 98 | {name: "PUP", location: "Seattle, WA", createdAt: new Date(), updatedAt: new Date() }, 99 | {name: "The National", location: "Seattle, WA", createdAt: new Date(), updatedAt: new Date() }, 100 | {name: "Waxahatchee", location: "Kansas City, MO", createdAt: new Date(), updatedAt: new Date() }, 101 | {name: "Luke Lalonde", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, //95 102 | {name: "Julie Arsenault", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, //96 103 | {name: "The O'Pears", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, //97 104 | {name: "Nick McKinlay", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, //98 105 | {name: "Christo Graham", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, //99 106 | {name: "Peter Dreimanis", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, //100 107 | {name: "Fast Romantics", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, 108 | {name: "Matthew 'Doc' Dunn", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, 109 | {name: "Rogue Tenant + Steve Sladkowski", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, 110 | {name: "Ali Haberstroh + Cameron Brown", location: "Toronto", createdAt: new Date(), updatedAt: new Date() }, 111 | ], {}); 112 | 113 | }, 114 | 115 | down: (queryInterface, Sequelize) => { 116 | 117 | return queryInterface.bulkDelete('Artists', null, {}); 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /backend/db/seeders/20210521222709-Album-data.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | 6 | return queryInterface.bulkInsert('Albums', [ 7 | {title: "Say Goodbye To Pretty Boy", imgUrl: "https://f4.bcbits.com/img/a2944346038_16.jpg", description: "Say Goodbye to Pretty Boy is Bartees Strange’s debut EP. It reimagines the songs of The National, a genre-defining indie rock staple for people of Bartees' generation. Strange — a black artist from Mustang, Oklahoma — uses everything from the cover art to his reading of the lyrics to allude to how black artists can find room in white spaces. The EP is both a heart-felt homage and a political act of critique. And it sounds like it was made with energy, intelligence & love.", createdAt: new Date(), updatedAt: new Date() }, 8 | {title: "Emotion", imgUrl: "https://f4.bcbits.com/img/a2835557561_16.jpg", description: "EMO-TION is a collaboration by fifteen artists to pay tribute to the ineffable Carly Rae Jepsen's seminal album EMOTION. All proceeds from this compilation will go to Immigration Equality. Since 1994, Immigration Equality has been advocating for and representing lesbian, gay, bisexual, transgender, queer (LGBTQ), and HIV-positive immigrants seeking safety, fair treatment, and freedom.", createdAt: new Date(), updatedAt: new Date() }, 9 | {title: "ReRed", imgUrl: "https://f4.bcbits.com/img/a3907726339_16.jpg", description: "ReRed is a collaboration by 19 artists to pay tribute to the crossover hitmaker Taylor Swift's instant classic album Red. All proceeds will go straight to Equal Justice Initiative, an incredible organization working to end mass incarceration, excessive punishment, and racial inequality.", createdAt: new Date(), updatedAt: new Date() }, 10 | {title: "Don't Stop Now", imgUrl: "https://f4.bcbits.com/img/a0886591079_16.jpg", description: "This compilation is an expression of love, anger, hope and protest on inauguration day. Let it serve as a reminder that the fight for justice is not over, that the celebration of diversity is essential to progress, that we must work together for what is fair and good. Can’t stop. Won’t Stop. Don’t stop now. All proceeds from this compilation benefit the ACLU, an organization that defends individual freedoms in the face of government abuse, including speech and religion, a woman’s right to choose, the right to due process, and citizens rights to privacy. Each dollar donated will help protect the people of the United States, especially those most vulnerable, from the reckless authority of a Trump presidency.", createdAt: new Date(), updatedAt: new Date() }, 11 | {title: "Don't Stop Now: II", imgUrl: "https://f4.bcbits.com/img/a2755082232_16.jpg", description: "This is the second compilation in the Don't Stop Now series. It is still an expression of love, anger, hope and protest. Let it serve as a reminder that the fight for justice is not over, that the celebration of diversity is essential to progress, that we must work together for what is fair and good. Can’t stop. Won’t stop. Don’t stop now. This time around all of the proceeds from this compilation benefit RAICES (Refugee & Immigrant Center for Education & Legal Services) They promote justice by providing free and low-cost legal services to under served immigrant children, families, and refugees in Texas.", createdAt: new Date(), updatedAt: new Date() }, 12 | {title: "The Georgia EP", imgUrl: "https://f4.bcbits.com/img/a3267728222_10.jpg", description: "In celebration of the recent Georgia runoff election results, we have released THE GEORGIA E.P. across all streaming platforms (including re-releasing it here on Bandcamp) The fundraiser for this release has ended. Thank you to everyone who helped us raise over $100,000 for Fair Fight Action! To say that this exceeded our wildest expectations would be an understatement. Really.", createdAt: new Date(), updatedAt: new Date() }, 13 | {title: "Save Stereogum", imgUrl: "https://f4.bcbits.com/img/a3972692566_10.jpg", description: "To help support our newly independent publication, Stereogum produced this 55-track '00s covers album as a crowdfunding campaign reward.", createdAt: new Date(), updatedAt: new Date() }, 14 | {title: "At the End of Every Hard Earned Day People Find Some Reason to Believe", imgUrl: "https://f4.bcbits.com/img/a3569705703_10.jpg", description: "A buncha months ago, when the new normal felt temporary, I thought it would be fun to make a compilation with some artists & friends I play drums for/ worked with/ & admired over the years. Given the circumstances, loneliness, isolation, feeling & focus, Bruce Springsteen’s Nebraska felt like an appropriate cipher for the moment. When I listen to the songs on Nebraska I think about justice & justification; the absence of the former and the ever-churning of the latter, as present as the tape hiss on the Tascam the record was performed on. I think about complicated lives lived by complicated people in complicated situations. I think about peace & fear & hope & a lack of options...", createdAt: new Date(), updatedAt: new Date() }, 15 | {title: "The Old Friends EP", imgUrl: "https://f4.bcbits.com/img/a3138638062_16.jpg", description: "Over the past year, Worriers was asked to participate in a number of benefit livestreams, comps, and other projects that wouldn't normally be possible for us to do when we were on tour all the time. Living in different cities during quarantine, we took these opportunities to keep making music together - from home. Over the past few years, various arrangements of Worriers have played cover sets of bands like Fleetwood Mac, Minor Threat, Green Day, and The Lemonheads, so covers have always been a fun extra-curricular activity for us. This collection brings together songs from some of our favorite and most influential bands.", createdAt: new Date(), updatedAt: new Date() }, 16 | {title: "Don't Stop Now III: A Collection of Covers & More.", imgUrl: "https://f4.bcbits.com/img/a3817049920_10.jpg", description: null, createdAt: new Date(), updatedAt: new Date() }, 17 | ], {}); 18 | }, 19 | 20 | down: (queryInterface, Sequelize) => { 21 | 22 | return queryInterface.bulkDelete('Albums', null, {}); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /backend/db/seeders/20210521222719-Song-data.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | 6 | return queryInterface.bulkInsert('Songs', [ 7 | {title: "About Today", artistId: 1, originalArtist: "The National", albumId: 1, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy+-+01+About+Today.mp3", createdAt: new Date(), updatedAt: new Date() }, 8 | {title: "Lemonworld", artistId: 1, originalArtist: "The National", albumId: 1, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy+-+02+Lemonworld.mp3", createdAt: new Date(), updatedAt: new Date() }, 9 | {title: "Mr. November", artistId: 1, originalArtist: "The National", albumId: 1, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy+-+03+Mr.+November.mp3", createdAt: new Date(), updatedAt: new Date() }, 10 | {title: "The Geese of Beverly Road", artistId: 1, originalArtist: "The National", albumId: 1, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy+-+04+The+Geese+of+Beverly+Road.mp3", createdAt: new Date(), updatedAt: new Date() }, 11 | {title: "All the Wine", artistId: 1, originalArtist: "The National", albumId: 1, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy+-+05+All+The+Wine.mp3", createdAt: new Date(), updatedAt: new Date() }, 12 | {title: "A Reasonable Man (I Don't Mind)", artistId: 1, originalArtist: "The National", albumId: 1, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy+-+06+A+Reasonable+Man+(I+Don't+Mind).mp3", createdAt: new Date(), updatedAt: new Date() }, 13 | {title: "Looking for Astronauts", artistId: 1, originalArtist: "The National", albumId: 1, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy/Bartees+Strange+-+Say+Goodbye+to+Pretty+Boy+-+07+Looking+for+Astronauts.mp3", createdAt: new Date(), updatedAt: new Date() }, 14 | {title: "Run Away With Me", artistId: 2, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Future+Teens+-+EMO-TION+-+01+Run+Away+With+Me.mp3", createdAt: new Date(), updatedAt: new Date() }, 15 | {title: "Emotion", artistId: 3, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Lilith+-+EMO-TION+-+02+Emotion.mp3", createdAt: new Date(), updatedAt: new Date() }, 16 | {title: "I Really Like You", artistId: 4, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Mandancing+-+EMO-TION+-+03+I+Really+Like+You.mp3", createdAt: new Date(), updatedAt: new Date() }, 17 | {title: "Gimme Love", artistId: 5, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/The+Superweaks+-+EMO-TION+-+04+Gimme+Love.mp3", createdAt: new Date(), updatedAt: new Date() }, 18 | {title: "All That", artistId: 6, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Pushflowers+-+EMO-TION+-+05+All+That.mp3", createdAt: new Date(), updatedAt: new Date() }, 19 | {title: "Boy Problems", artistId: 7, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Cheer+Up+-+EMO-TION+-+06+Boy+Problems.mp3", createdAt: new Date(), updatedAt: new Date() }, 20 | {title: "Making the Most of the Night", artistId: 8, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/oldsoul+-+EMO-TION+-+07+Making+the+Most+of+the+Night.mp3", createdAt: new Date(), updatedAt: new Date() }, 21 | {title: "Your Type", artistId: 9, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Kiki+Maddog+-+EMO-TION+-+08+Your+Type.mp3", createdAt: new Date(), updatedAt: new Date() }, 22 | {title: "Let's Get Lost", artistId: 10, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/THE+AUX+-+EMO-TION+-+09+Let's+Get+Lost.mp3", createdAt: new Date(), updatedAt: new Date() }, 23 | {title: "LA Hallucinations", artistId: 11, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Happy+Just+to+See+You+-+EMO-TION+-+10+LA+Hallucinations.mp3", createdAt: new Date(), updatedAt: new Date() }, 24 | {title: "Warm Blood", artistId: 12, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Gabe+Goodman+-+EMO-TION+-+11+Warm+Blood.mp3", createdAt: new Date(), updatedAt: new Date() }, 25 | {title: "When I Needed You", artistId: 13, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Good+Looking+Friends+-+EMO-TION+-+12+When+I+Needed+You.mp3", createdAt: new Date(), updatedAt: new Date() }, 26 | {title: "Black Heart", artistId: 14, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Photocomfort+-+EMO-TION+-+13+Black+Heart.mp3", createdAt: new Date(), updatedAt: new Date() }, 27 | {title: "I Didn't Just Come Here To Dance", artistId: 15, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Tuft+-+EMO-TION+-+14+I+Didn't+Just+Come+Here+to+Dance.mp3", createdAt: new Date(), updatedAt: new Date() }, 28 | {title: "Favorite Color", artistId: 16, originalArtist: "Carly Rae Jepsen", albumId: 2, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+EMO-TION/Wild+Pink+-+EMO-TION+-+15+Favourite+Colour.mp3", createdAt: new Date(), updatedAt: new Date() }, 29 | {title: "State of Grace", artistId: 17, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Provide+-+ReRed+-+01+State+of+Grace.mp3", createdAt: new Date(), updatedAt: new Date() }, 30 | {title: "Red", artistId: 18, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/I+Wish+I+Could+Skateboard+-+ReRed+-+02+Red.mp3", createdAt: new Date(), updatedAt: new Date() }, 31 | {title: "Treacherous", artistId: 4, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/ManDancing+(feat.+Sun+Cycles)+-+ReRed+-+03+Treacherous.mp3", createdAt: new Date(), updatedAt: new Date() }, 32 | {title: "I Knew You Were Trouble", artistId: 12, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Gabe+Goodman+-+ReRed+-+04+I+Knew+You+Were+Trouble.mp3", createdAt: new Date(), updatedAt: new Date() }, 33 | {title: "All Too Well", artistId: 19, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Dan+Campbell+-+ReRed+-+05+All+Too+Well.mp3", createdAt: new Date(), updatedAt: new Date() }, 34 | {title: "22", artistId: 20, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Bad+Bad+Hats+-+ReRed+-+06+22.mp3", createdAt: new Date(), updatedAt: new Date() }, 35 | {title: "I Almost Do", artistId: 14, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Photocomfort+-+ReRed+-+07+I+Almost+Do.mp3", createdAt: new Date(), updatedAt: new Date() }, 36 | {title: "We Are Never Ever Getting Back Together", artistId: 16, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Wild+Pink+-+ReRed+-+08+We+Are+Never+Ever+Getting+Back+Together.mp3", createdAt: new Date(), updatedAt: new Date() }, 37 | {title: "Stay Stay Stay", artistId: 15, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Tuft+%26+Lake+Saint+Daniel+-+ReRed+-+09+Stay+Stay+Stay.mp3", createdAt: new Date(), updatedAt: new Date() }, 38 | {title: "The Last Time", artistId: 21, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Mint+Green+-+ReRed+-+10+The+Last+Time.mp3", createdAt: new Date(), updatedAt: new Date() }, 39 | {title: "Holy Ground", artistId: 22, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Chris+Farren+-+ReRed+-+11+Holy+Ground.mp3", createdAt: new Date(), updatedAt: new Date() }, 40 | {title: "Sad Beautiful Tragic", artistId: 11, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Happy+Just+to+See+You+-+ReRed+-+12+Sad+Beautiful+Tragic.mp3", createdAt: new Date(), updatedAt: new Date() }, 41 | {title: "The Lucky One", artistId: 23, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Weakened+Friends+-+ReRed+-+13+The+Lucky+One.mp3", createdAt: new Date(), updatedAt: new Date() }, 42 | {title: "Everything Has Changed", artistId: 13, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Hit+Like+a+Girl+%26+Good+Looking+Friends+-+ReRed+-+14+Everything+Has+Changed.mp3", createdAt: new Date(), updatedAt: new Date() }, 43 | {title: "Starlight", artistId: 24, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Adult+Mom+-+ReRed+-+15+Starlight.mp3", createdAt: new Date(), updatedAt: new Date() }, 44 | {title: "Begin Again", artistId: 2, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Future+Teens+-+ReRed+-+16+Begin+Again.mp3", createdAt: new Date(), updatedAt: new Date() }, 45 | {title: "The Moment I Knew", artistId: 25, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Paige+Chaplin+-+ReRed+-+17+The+Moment+I+Knew.mp3", createdAt: new Date(), updatedAt: new Date() }, 46 | {title: "Come Back... Be Here", artistId: 26, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Telethon+-+ReRed+-+18+Come+Back...Be+Here.mp3", createdAt: new Date(), updatedAt: new Date() }, 47 | {title: "Girl at Home", artistId: 27, originalArtist: "Taylor Swift", albumId: 3, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Something+Merry+-+ReRed/Calicoco+-+ReRed+-+19+Girl+at+Home.mp3", createdAt: new Date(), updatedAt: new Date() }, 48 | {title: "American Hearts", artistId: 78, originalArtist: "AA Bondy", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+07+RYAN+AZADA-+American+Hearts+by+A.A.+Bondy.mp3", createdAt: new Date(), updatedAt: new Date() }, 49 | {title: "Bamboo Bones", artistId: 22, originalArtist: "Against Me!", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+14+CHRIS+FARREN-+Bamboo+Bones+by+Against+Me!.mp3", createdAt: new Date(), updatedAt: new Date() }, 50 | {title: "Those Were The Days", artistId: 28, originalArtist: "Angel Olsen", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+05+ABI+REIMOLD-+Those+Were+the+Days+by+Angel+Olsen..mp3", createdAt: new Date(), updatedAt: new Date() }, 51 | {title: "Earth Death", artistId: 48, originalArtist: "Baths", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+31+FEMININE+ISSUES-+Earth+Death+by+Baths.mp3", createdAt: new Date(), updatedAt: new Date() }, 52 | {title: "Jet Ski", artistId: 56, originalArtist: "Bikini Kill", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+10+JEFF+ROSENSTOCK-+Jet+Ski+by+Bikini+Kill..mp3", createdAt: new Date(), updatedAt: new Date() }, 53 | {title: "Dark Moon", artistId: 30, originalArtist: "Bonnie Guitar", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+15+ANIKA+PYLE-+Dark+Moon+by+Bonnie+Guitar..mp3", createdAt: new Date(), updatedAt: new Date() }, 54 | {title: "Land Locked Blues", artistId: 57, originalArtist: "Bright Eyes", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+08+JON+LOUDON+(RESTORATIONS)-+Land+Locked+Blues+by+Bright+Eyes..mp3", createdAt: new Date(), updatedAt: new Date() }, 55 | {title: "Wolves at the Door", artistId: 39, originalArtist: "David Bazan", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+23+BROAD+CAST-+Wolves+At+The+Door+by+David+Bazan.mp3", createdAt: new Date(), updatedAt: new Date() }, 56 | {title: "Heroes", artistId: 52, originalArtist: "David Bowie", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+34+HEMMING-+Heros+by+David+Bowie..mp3", createdAt: new Date(), updatedAt: new Date() }, 57 | {title: "My Guitar", artistId: 67, originalArtist: "Dear Nora", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+37+MARISA+DABICE+(MANNEQUIN+PUSSY)+%26+PAT+CONABOY+(SPIRIT+OF+THE+BEEHIVE)-+My+guitar+by+Dear+No.mp3", createdAt: new Date(), updatedAt: new Date() }, 58 | {title: "A Distorted Reality Is Now a Necessity to Be Free", artistId: 44, originalArtist: "Elliott Smith", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+12+CONSTANT+COMPANION-+A+Distorted+Reality+Is+Now+a+Necessity+to+Be+Free+by+Elliott+Smith.mp3", createdAt: new Date(), updatedAt: new Date() }, 59 | {title: "Wet as a Cloud", artistId: 76, originalArtist: "Fred Thomas", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+20+RADIATOR+HOSPITAL-+Wet+as+a+Cloud+by+Fred+Thomas..mp3", createdAt: new Date(), updatedAt: new Date() }, 60 | {title: "Prosthetic Head", artistId: 70, originalArtist: "Green Day", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+25+MIMI+GALLAGHER-+Prosthetic+Head+by+Green+Day.mp3", createdAt: new Date(), updatedAt: new Date() }, 61 | {title: "I Want to Learn A Love Song", artistId: 74, originalArtist: "Harry Chapin", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+24+PJ+BOND-+I+want+to+learn+a+love+song+by+Harry+Chapin..mp3", createdAt: new Date(), updatedAt: new Date() }, 62 | {title: "24 Frames", artistId: 84, originalArtist: "Jason Isbell", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+36+THE+MENZINGERS-+24+Frames+by+Jason+Isbell.mp3", createdAt: new Date(), updatedAt: new Date() }, 63 | {title: "Rise Up With Fists", artistId: 34, originalArtist: "Jenny Lewis & the Watson Twins", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+35+BEN+HUGHES+(NIGHT+WINDOWS)-+Rise+up+with+Fists+by+Jenny+Lewis+%26+The+Watson+Twins..mp3", createdAt: new Date(), updatedAt: new Date() }, 64 | {title: "Imagine", artistId: 51, originalArtist: "John Lennon", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+22+HEART+HARBOR-+Imagine+by+John+Lennon.mp3", createdAt: new Date(), updatedAt: new Date() }, 65 | {title: "Important In Your Life", artistId: 73, originalArtist: "Jonathan Richman", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+33+PERRY+SHALL+%26+AMOS+PITSCH-+Important+In+Your+Life+by+Jonathan+Richman..mp3", createdAt: new Date(), updatedAt: new Date() }, 66 | {title: "All I Want", artistId: 61, originalArtist: "Joni Mitchell", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+29+LIFTED+BELLS-+All+I+want+by+Joni+Mitchell.mp3", createdAt: new Date(), updatedAt: new Date() }, 67 | {title: "Disorder", artistId: 82, originalArtist: "Joy Division", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+21+STRANGE+RELATIONS-+Disorder+by+Joy+Division..mp3", createdAt: new Date(), updatedAt: new Date() }, 68 | {title: "Hey, That's No Way To Say Goodbye", artistId: 41, originalArtist: "Leonard Cohen", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+18+CARLY+COMANDO+(SLINGSHOT+DAKOTA)-+Hey%2C+that's+no+way+to+say+goodbye+by+Leonard+Cohen.mp3", createdAt: new Date(), updatedAt: new Date() }, 69 | {title: "Passionate Kisses", artistId: 62, originalArtist: "Lucinda Williams", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+30+LISA+PRANK-+Passionate+Kisses+by+Lucinda+Williams..mp3", createdAt: new Date(), updatedAt: new Date() }, 70 | {title: "After the Gold Rush", artistId: 83, originalArtist: "Neil Young", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+02+SWANNING-+After+the+Gold+Rush+by+Neil+Young.mp3", createdAt: new Date(), updatedAt: new Date() }, 71 | {title: "Love Vigilantes", artistId: 53, originalArtist: "New Order", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+13+HURRY-+Love+Vigilantes+by+New+Order.mp3", createdAt: new Date(), updatedAt: new Date() }, 72 | {title: "Something In The Way", artistId: 66, originalArtist: "Nirvana", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+26+MANHATTAN+SIDE+PROJECT+(RESTORATIONS)-+Something+in+the+way+by+Nirvana.mp3", createdAt: new Date(), updatedAt: new Date() }, 73 | {title: "Back Against the Wall", artistId: 29, originalArtist: "PUP", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+09+ALLEGRA+ANKA+(CAYETANA)-+Back+Against+the+Wall+by+PUP..mp3", createdAt: new Date(), updatedAt: new Date() }, 74 | {title: "Revolver", artistId: 38, originalArtist: "Rage Against the Machine", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+03+BRITTANY+CORRIGAN+WITH+THE+SIDEKICKS-+Revolver+by+Rage+Against+the+Machine..mp3", createdAt: new Date(), updatedAt: new Date() }, 75 | {title: "What Kind of Monster Are You?", artistId: 71, originalArtist: "Slant 6", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+11+MOLAR-+What+Kind+of+Monster+Are+You-+by+Slant+6..mp3", createdAt: new Date(), updatedAt: new Date() }, 76 | {title: "I'm Only Sleeping", artistId: 68, originalArtist: "The Beatles", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+04+MATT+SCHIMELFENIG+(THREE+MAN+CANNON)-+I'm+Only+Sleeping+by+The+Beatles..mp3", createdAt: new Date(), updatedAt: new Date() }, 77 | {title: "We're Gonna Rise", artistId: 49, originalArtist: "The Breeders", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+17+GRACE+VONDERKUHN-+We're+Gonna+Rise+by+The+Breeders.mp3", createdAt: new Date(), updatedAt: new Date() }, 78 | {title: "Strangers", artistId: 33, originalArtist: "The Kinks", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+06+BARTLEY%2C+CLARKE%2C+HARRIS%2C+%26+HARVEY-+Strangers+by+The+Kinks.mp3", createdAt: new Date(), updatedAt: new Date() }, 79 | {title: "Love, Love, Love", artistId: 31, originalArtist: "The Mountain Goats", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+19+AUGUSTA+KOCH+(CAYETANA)-+Love%2C+love%2C+love+by+The+Mountain+Goats..mp3", createdAt: new Date(), updatedAt: new Date() }, 80 | {title: "Unsatisfied", artistId: 42, originalArtist: "The Replacements", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+16+CAVE+PEOPLE-+Unsatisfied+By+The+Replacements.mp3", createdAt: new Date(), updatedAt: new Date() }, 81 | {title: "This is the Day", artistId: 63, originalArtist: "The The", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+28+LITTLER-+This+is+the+day+by+The+The..mp3", createdAt: new Date(), updatedAt: new Date() }, 82 | {title: "Confessions of a Futon Revolutionist", artistId: 47, originalArtist: "The Weakerthans", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+32+EXPERT+TIMING-+Confessions+of+a+Futon+Revolutionist+by+The+Weakerthans..mp3", createdAt: new Date(), updatedAt: new Date() }, 83 | {title: "Dynamite Shovel", artistId: 54, originalArtist: "The Wonder Years", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+27+JACK+MACCANN-+Dynamite+Shovel+by+The+Wonder+Years.mp3", createdAt: new Date(), updatedAt: new Date() }, 84 | {title: "Tower Song", artistId: 60, originalArtist: "Townes Van Zandt", albumId: 4, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+stop+now-+a+collection+of+covers+-+01+LAURA+STEVENSON-+Tower+Song+by+Townes+Van+Zandt..mp3", createdAt: new Date(), updatedAt: new Date() }, 85 | {title: "Valerie", artistId: 64, originalArtist: "Amy Winehouse", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+23+LOWERCASE+ROSES-+Valerie+by+Amy+Winehouse.mp3", createdAt: new Date(), updatedAt: new Date() }, 86 | {title: "Creator, Destroyer", artistId: 58, originalArtist: "Angel Olsen", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+08+JORDYN+OCCHIPINTI-+Creator%2C+Destroyer+by+Angel+Olsen.mp3", createdAt: new Date(), updatedAt: new Date() }, 87 | {title: "Innocent Vigilant Ordinary", artistId: 37, originalArtist: "Appleseed Cast", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+19+BRACKISH-+Innocent+Vigilant+Ordinary+by+Appleseed+Cast.mp3", createdAt: new Date(), updatedAt: new Date() }, 88 | {title: "Rollercoaster", artistId: 86, originalArtist: "Bleachers", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+02+WORRIERS-+Rollercoaster+by+Bleachers.mp3", createdAt: new Date(), updatedAt: new Date() }, 89 | {title: "All I Really Wanta Do", artistId: 43, originalArtist: "Bob Dylan", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+22+CHERRY-+All+I+Really+Wanta+Do+by+Bob+Dylan.mp3", createdAt: new Date(), updatedAt: new Date() }, 90 | {title: "When You're Alone", artistId: 16, originalArtist: "Bruce Springsteen", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+04+WILD+PINK-+When+You're+Alone+by+Bruce+Springsteen.mp3", createdAt: new Date(), updatedAt: new Date() }, 91 | {title: "Raining in Baltimore", artistId: 59, originalArtist: "Counting Crows", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+11+KAYLEIGH+GOLDSWORTHY+%26+KEVIN+DEVINE-+Raining+in+Baltimore+by+Counting+Crows.mp3", createdAt: new Date(), updatedAt: new Date() }, 92 | {title: "The Ice Is Getting Thinner", artistId: 75, originalArtist: "Death Cab for Cutie", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+09+RACHEL+DISPENZA+(COPING+SKILLS)-+The+Ice+Is+Getting+Thinner+by+Death+Cab+for+Cutie.mp3", createdAt: new Date(), updatedAt: new Date() }, 93 | {title: "I'm So Tired", artistId: 50, originalArtist: "Fugazi", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+13+GREG+MENDEZ-+I'm+So+Tired+by+Fugazi.mp3", createdAt: new Date(), updatedAt: new Date() }, 94 | {title: "Wrecking Ball", artistId: 45, originalArtist: "Gillan Welch", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+01+CRAIG+FINN+%26+JOHN+K+SAMSON+AND+THE+UPTOWN+CONTROLLERS-+Wrecking+Ball+by+Gillian+Welch.mp3", createdAt: new Date(), updatedAt: new Date() }, 95 | {title: "I Feel It", artistId: 80, originalArtist: "I Heart Hiroshima", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+16+SO+TOTALLY-+I+Feel+It+by+I+Heart+Hiroshima.mp3", createdAt: new Date(), updatedAt: new Date() }, 96 | {title: "On A Good Day", artistId: 22, originalArtist: "Joanna Newsom", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+05+CHRIS+FARREN+-+On+A+Good+Day+by+Joanna+Newsom.mp3", createdAt: new Date(), updatedAt: new Date() }, 97 | {title: "Something", artistId: 32, originalArtist: "Julien Baker", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+29+awakebutstillinbed-+Something+by+Julien+Baker.mp3", createdAt: new Date(), updatedAt: new Date() }, 98 | {title: "Constant Craving", artistId: 40, originalArtist: "KD Lang", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+07+BROKEN+FIELD+RUNNER-+Constant+Craving+by+K.D.+Lang.mp3", createdAt: new Date(), updatedAt: new Date() }, 99 | {title: "Old Paint", artistId: 79, originalArtist: "Linda Ronstadt", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+12+SNOWHORE-+Old+Paint+by+Linda+Ronstadt.mp3", createdAt: new Date(), updatedAt: new Date() }, 100 | {title: "Buzzcut Season", artistId: 77, originalArtist: "Lorde", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+20+ROGER+HARVEY-+Buzzcut+Season+by+Lorde.mp3", createdAt: new Date(), updatedAt: new Date() }, 101 | {title: "Metal Firecracker", artistId: 30, originalArtist: "Lucinda Williams", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+17+ANIKA+PYLE-+Metal+Firecracker+by+Lucinda+Williams.mp3", createdAt: new Date(), updatedAt: new Date() }, 102 | {title: "Two Headed Boy", artistId: 55, originalArtist: "Neutral Milk Hotel", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+26+JEFF+RIDDLE-+Two+Headed+Boy+by+Neutral+Milk+Hotel.mp3", createdAt: new Date(), updatedAt: new Date() }, 103 | {title: "Options", artistId: 72, originalArtist: "Pedro the Lion", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+18+NO+THANK+YOU-+Options+by+Pedro+The+Lion.mp3", createdAt: new Date(), updatedAt: new Date() }, 104 | {title: "By My Side", artistId: 35, originalArtist: "Porches", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+21+BEN+WALSH+(TIGERS+JAW)-+By+My+Side+by+Porches.mp3", createdAt: new Date(), updatedAt: new Date() }, 105 | {title: "Samson", artistId: 84, originalArtist: "Regina Spektor", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+27+TOM+MAY+(THE+MENZINGERS)-+Samson+by+Regina+Specktor.mp3", createdAt: new Date(), updatedAt: new Date() }, 106 | {title: "Do What You Have To Do", artistId: 65, originalArtist: "Sarah McLachlan", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+15+LYDIA+LOVELESS-+Do+What+You+Have+To+Do+by+Sarah+McLachlan.mp3", createdAt: new Date(), updatedAt: new Date() }, 107 | {title: "Give Out", artistId: 31, originalArtist: "Sharon Van Etten", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+25+AUGUSTA+KOCH-+Give+Out+by+Sharon+Van+Etten.mp3", createdAt: new Date(), updatedAt: new Date() }, 108 | {title: "My Favorite Mistake", artistId: 36, originalArtist: "Sheryl Crow", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+28+BLACK+PAW-+My+Favorite+Mistake+by+Sheryl+Crow.mp3", createdAt: new Date(), updatedAt: new Date() }, 109 | {title: "Untitled", artistId: 46, originalArtist: "Social Distortion", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+14+DAN+ZIMMERMAN+(RESTORATIONS)-+Untitled+by+Social+Distortion.mp3", createdAt: new Date(), updatedAt: new Date() }, 110 | {title: "Black Hole Sun", artistId: 52, originalArtist: "Soundgarden", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+33+HEMMING-+Black+Hole+Sun+by+Soundgarden..mp3", createdAt: new Date(), updatedAt: new Date() }, 111 | {title: "I Don't Know", artistId: 70, originalArtist: "The Beastie Boys", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+31+MIMI+GALLAGHER-+I+Don't+Know+by+The+Beastie+Boys.mp3", createdAt: new Date(), updatedAt: new Date() }, 112 | {title: "Invisible Man", artistId: 68, originalArtist: "The Breeders", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+10+MATT+SCHIMELFENIG+(THREE+MAN+CANNON)-+Invisible+Man+by+The+Breeders.mp3", createdAt: new Date(), updatedAt: new Date() }, 113 | {title: "Color In Your Cheeks", artistId: 69, originalArtist: "The Mountain Goats", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+32+MAXWELL+STERN-+Color+in+Your+Cheeks+by+The+Mountain+Goats..mp3", createdAt: new Date(), updatedAt: new Date() }, 114 | {title: "Pressure's On My Knob", artistId: 81, originalArtist: "Three 6 Mafia", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+03+SOUL+GLO-+Pressure's+On+My+Knob+by+Three+6+Mafia+-+Red+C.mp3", createdAt: new Date(), updatedAt: new Date() }, 115 | {title: "The Waiting", artistId: 53, originalArtist: "Tom Petty", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+30+HURRY-+The+Waiting+by+Tom+Petty.mp3", createdAt: new Date(), updatedAt: new Date() }, 116 | {title: "Way Down In The Hole", artistId: 85, originalArtist: "Tom Waits", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+24+TJ+KONG+%26+THE+ATOMIC+BOMB-+Way+Down+In+the+Hole+by+Tom+Waits.mp3", createdAt: new Date(), updatedAt: new Date() }, 117 | {title: "Tears Are In Your Eyes", artistId: 42, originalArtist: "Yo La Tengo", albumId: 5, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers/Don't+Stop+Now-+A+Collection+of+Covers+-+Don't+Stop+Now+II-+A+Collection+of+Covers+-+06+CAVE+PEOPLE-+Tears+Are+In+Your+Eyes+by+Yo+La+Tengo.mp3", createdAt: new Date(), updatedAt: new Date() }, 118 | {title: "Waterfalls", artistId: 87, originalArtist: "TLC", albumId: 6, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Death+Cab+for+Cutie+-+The+Georgia+E.P/Death+Cab+for+Cutie+-+The+Georgia+E.P.+-+01+Waterfalls+(TLC+Cover).mp3", createdAt: new Date(), updatedAt: new Date() }, 119 | {title: "King of Carrot Flowers Pt. 1", artistId: 87, originalArtist: "Neutral Milk Hotel", albumId: 6, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Death+Cab+for+Cutie+-+The+Georgia+E.P/Death+Cab+for+Cutie+-+The+Georgia+E.P.+-+02+King+of+Carrot+Flowers+Pt.+1+(Neutral+Milk+Hotel+Cover).mp3", createdAt: new Date(), updatedAt: new Date() }, 120 | {title: "Fall On Me", artistId: 87, originalArtist: "REM", albumId: 6, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Death+Cab+for+Cutie+-+The+Georgia+E.P/Death+Cab+for+Cutie+-+The+Georgia+E.P.+-+03+Fall+On+Me+(R.E.M.+Cover).mp3", createdAt: new Date(), updatedAt: new Date() }, 121 | {title: "Flirted With You All My Life", artistId: 87, originalArtist: "Vic Chesnutt", albumId: 6, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Death+Cab+for+Cutie+-+The+Georgia+E.P/Death+Cab+for+Cutie+-+The+Georgia+E.P.+-+04+Flirted+With+You+All+My+Life+(Vic+Chesnutt+Cover).mp3", createdAt: new Date(), updatedAt: new Date() }, 122 | {title: "Hollaback Girl", artistId: 87, originalArtist: "Gwen Stefani", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/28+Charly+Bliss+-+-Hollaback+Girl-.mp3", createdAt: new Date(), updatedAt: new Date() }, 123 | {title: "I'm On Standby", artistId: 22, originalArtist: "Grandaddy", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/08+Chris+Farren+-+-I'm+On+Standby-.mp3", createdAt: new Date(), updatedAt: new Date() }, 124 | {title: "Aaron and Maria", artistId: 86, originalArtist: "American Analog Set", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/46+Death+Cab+For+Cutie+-+-Aaron+And+Maria-.mp3", createdAt: new Date(), updatedAt: new Date() }, 125 | {title: "Wrecking Ball", artistId: 88, originalArtist: "Gillian Welch", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/55+Jason+Isbell+%26+Amanda+Shires+-+-Wrecking+Ball-.mp3", createdAt: new Date(), updatedAt: new Date() }, 126 | {title: "Crossing With Switchblades", artistId: 56, originalArtist: "Scared of Chaka", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/42+Jeff+Rosenstock+-+-Crossing+With+Switchblades-.mp3", createdAt: new Date(), updatedAt: new Date() }, 127 | {title: "Anthem Part Two", artistId: 89, originalArtist: "Blink-182", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/38+Julien+Baker+-+-Anthem+Part+Two-.mp3", createdAt: new Date(), updatedAt: new Date() }, 128 | {title: "We Will Become Silhouettes", artistId: 60, originalArtist: "The Postal Service", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/47+Laura+Stevenson+-+-We+Will+Become+Silhouettes-.mp3", createdAt: new Date(), updatedAt: new Date() }, 129 | {title: "Lips of An Angel", artistId: 90, originalArtist: "Hinder", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/03+Lucy+Dacus+-+-Lips+Of+An+Angel-.mp3", createdAt: new Date(), updatedAt: new Date() }, 130 | {title: "Plea from a Cat Named Virtue", artistId: 92, originalArtist: "The Weakerthans", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/15+PUP+-+-Plea+From+A+Cat+Named+Virtute-.mp3", createdAt: new Date(), updatedAt: new Date() }, 131 | {title: "My Backwards Walk", artistId: 93, originalArtist: "Frightened Rabbit", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/51+The+National+-+-My+Backwards+Walk+(Live).mp3", createdAt: new Date(), updatedAt: new Date() }, 132 | {title: "You Said Something", artistId: 94, originalArtist: "PJ Harvey", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/05+Waxahatchee+-+-You+Said+Something-.mp3", createdAt: new Date(), updatedAt: new Date() }, 133 | {title: "Reign of Love", artistId: 16, originalArtist: "Coldplay", albumId: 7, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Save+Stereogum/24+Wild+Pink+-+-Reign+Of+Love-.mp3", createdAt: new Date(), updatedAt: new Date() }, 134 | {title: "Nebraska", artistId: 95, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/Luke+Lalonde+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+01+Nebraska.mp3", createdAt: new Date(), updatedAt: new Date() }, 135 | {title: "Atlantic City", artistId: 96, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/Julie+Arsenault+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+02+Atlantic+City.mp3", createdAt: new Date(), updatedAt: new Date() }, 136 | {title: "Mansion on the Hill", artistId: 97, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/The+O'Pears+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+03+Mansion+on+the+Hill.mp3", createdAt: new Date(), updatedAt: new Date() }, 137 | {title: "Johnny 99", artistId: 98, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/Nick+McKinlay+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+04+Johnny+99.mp3", createdAt: new Date(), updatedAt: new Date() }, 138 | {title: "Highway Patrolman", artistId: 99, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/Christo+Graham+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+05+Highway+Patrolman.mp3", createdAt: new Date(), updatedAt: new Date() }, 139 | {title: "State Trooper", artistId: 100, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/Peter+Dreimanis+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+06+State+Trooper.mp3", createdAt: new Date(), updatedAt: new Date() }, 140 | {title: "Used Cars", artistId: 101, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/Fast+Romantics+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+07+Used+Cars.mp3", createdAt: new Date(), updatedAt: new Date() }, 141 | {title: "Open All Night", artistId: 102, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/Matthew+-Doc-+Dunn+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+08+Open+All+Night.mp3", createdAt: new Date(), updatedAt: new Date() }, 142 | {title: "My Father's House", artistId: 103, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/Rogue+Tenant+%2B+Steve+Sladkowski+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+09+My+Father's+House.mp3", createdAt: new Date(), updatedAt: new Date() }, 143 | {title: "Reason to Believe", artistId: 104, originalArtist: "Bruce Springsteen", albumId: 8, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Toronto+Covers+Project+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe/Ali+Haberstroh+%2B+Cameron+Brown+-+At+the+End+of+Every+Hard+Earned+Day+People+Find+Some+Reason+to+Believe+-+10+Reason+to+Believe.mp3", createdAt: new Date(), updatedAt: new Date() }, 144 | {title: "Rollercoaster", artistId: 86, originalArtist: "Bleachers", albumId: 9, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Worriers+-+The+Old+Friends+EP/Worriers+-+The+Old+Friends+EP+-+01+Rollercoaster.mp3", createdAt: new Date(), updatedAt: new Date() }, 145 | {title: "Letter from an Occupant", artistId: 86, originalArtist: "The New Pornographers", albumId: 9, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Worriers+-+The+Old+Friends+EP/Worriers+-+The+Old+Friends+EP+-+02+Letter+From+An+Occupant.mp3", createdAt: new Date(), updatedAt: new Date() }, 146 | {title: "Keeping Me Alive", artistId: 86, originalArtist: "Tom Petty", albumId: 9, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Worriers+-+The+Old+Friends+EP/Worriers+-+The+Old+Friends+EP+-+03+Keeping+Me+Alive.mp3", createdAt: new Date(), updatedAt: new Date() }, 147 | {title: "Old Friend", artistId: 86, originalArtist: "Rancid", albumId: 9, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Worriers+-+The+Old+Friends+EP/Worriers+-+The+Old+Friends+EP+-+04+Old+Friend.mp3", createdAt: new Date(), updatedAt: new Date() }, 148 | {title: "That's How I Escaped My Certain Fate", artistId: 86, originalArtist: "Mission of Burma", albumId: 9, fileUrl: "https://barband-seeds.s3.us-east-2.amazonaws.com/Worriers+-+The+Old+Friends+EP/Worriers+-+The+Old+Friends+EP+-+05+That's+How+I+Escaped+my+Certain+Fate.mp3", createdAt: new Date(), updatedAt: new Date() }, 149 | ], {}); 150 | }, 151 | 152 | down: (queryInterface, Sequelize) => { 153 | /* 154 | Add reverting commands here. 155 | Return a promise to correctly handle asynchronicity. 156 | 157 | Example: 158 | */ 159 | return queryInterface.bulkDelete('Songs', null, {}); 160 | } 161 | }; 162 | -------------------------------------------------------------------------------- /backend/db/seeders/20210524013811-collection-data.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | 6 | return queryInterface.bulkInsert('Collections', [ 7 | {userId: 1, albumId: 1, createdAt: new Date(), updatedAt: new Date() }, 8 | {userId: 1, albumId: 8, createdAt: new Date(), updatedAt: new Date() }, 9 | {userId: 2, albumId: 4, createdAt: new Date(), updatedAt: new Date() }, 10 | {userId: 2, albumId: 5, createdAt: new Date(), updatedAt: new Date() }, 11 | {userId: 2, albumId: 7, createdAt: new Date(), updatedAt: new Date() }, 12 | ], {}); 13 | }, 14 | 15 | down: (queryInterface, Sequelize) => { 16 | return queryInterface.bulkDelete('Collections', null, {}); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /backend/db/seeders/20210524014129-comments-seeder.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | 6 | return queryInterface.bulkInsert('Comments', [ 7 | {body: "Wall to wall bops", userId: 1, albumId: 2, createdAt: new Date(), updatedAt: new Date()}, 8 | {body: "A lot of all time greats on here", userId: 1, albumId: 3, createdAt: new Date(), updatedAt: new Date()}, 9 | {body: "hell yeah", userId: 3, albumId: 1, createdAt: new Date(), updatedAt: new Date()}, 10 | {body: "Wall to wall bops", userId: 3, albumId: 3, createdAt: new Date(), updatedAt: new Date()}, 11 | {body: "a classic album", userId: 1, albumId: 1, createdAt: new Date(), updatedAt: new Date()} 12 | ], {}); 13 | }, 14 | 15 | down: (queryInterface, Sequelize) => { 16 | return queryInterface.bulkDelete('Comments', null, {}); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "sequelize": "sequelize", 8 | "sequelize-cli": "sequelize-cli", 9 | "start": "per-env", 10 | "start:development": "nodemon -r dotenv/config ./bin/www", 11 | "start:production": "node ./bin/www" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "aws-sdk": "^2.959.0", 18 | "bcryptjs": "^2.4.3", 19 | "cookie-parser": "^1.4.5", 20 | "cors": "^2.8.5", 21 | "csurf": "^1.11.0", 22 | "dotenv": "^9.0.2", 23 | "express": "^4.17.1", 24 | "express-async-handler": "^1.1.4", 25 | "express-validator": "^6.11.1", 26 | "faker": "^5.5.3", 27 | "helmet": "^4.6.0", 28 | "jsonwebtoken": "^8.5.1", 29 | "morgan": "^1.10.0", 30 | "multer": "^1.4.2", 31 | "per-env": "^1.0.2", 32 | "pg": "^8.6.0", 33 | "sequelize": "^5.22.4", 34 | "sequelize-cli": "^5.5.1" 35 | }, 36 | "devDependencies": { 37 | "dotenv-cli": "^4.0.0", 38 | "nodemon": "^2.0.7" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /backend/routes/api/albums.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const asyncHandler = require('express-async-handler'); 3 | 4 | const router = express.Router(); 5 | 6 | const { Album, Song, Collection, Comment, Artist, User } = require("../../db/models"); 7 | const { requireAuth } = require('../../utils/auth'); 8 | 9 | //route for /api/albums 10 | router.get("", asyncHandler(async (req, res) => { 11 | const albums = await Album.findAll({}); 12 | return res.json(albums); 13 | })); 14 | 15 | //route for individual album page 16 | router.get("/:id", asyncHandler(async (req, res) => { 17 | const album = await Album.findByPk(req.params.id, { 18 | include: [{ 19 | model: Song, 20 | include: Artist 21 | }, 22 | { 23 | model: Comment, 24 | include: User 25 | }] 26 | }); 27 | if (album) { 28 | return res.json(album); 29 | } 30 | })); 31 | 32 | //get comments for album 33 | router.get("/:id/comments", asyncHandler(async (req, res) => { 34 | const comments = await Comment.findAll({ 35 | where: { albumId: req.params.id }, 36 | include: User 37 | }); 38 | return res.json(comments); 39 | })); 40 | 41 | //add a comment for this album 42 | router.post("/:id/comments", requireAuth, asyncHandler(async (req, res) => { 43 | const { body } = req.body.comment; 44 | const userId = req.user.id; 45 | const albumId = req.params.id; 46 | 47 | const comment = await Comment.create({ body, userId, albumId }); 48 | return res.json(comment); 49 | }) ); 50 | 51 | //edit a comment 52 | router.put("/:id/comments/:commId", requireAuth, asyncHandler(async (req, res) => { 53 | const commentId = req.params.commId; 54 | const { body } = req.body; 55 | 56 | const comment = await Comment.findByPk(commentId); 57 | comment.update({ 58 | body: body 59 | }); 60 | return res.json(comment); 61 | })); 62 | 63 | router.delete("/:id/comments/:commId", requireAuth, asyncHandler(async (req, res) => { 64 | const albumId = req.params.id; 65 | const commentId = req.params.commId; 66 | 67 | const comment = await Comment.findByPk(commentId); 68 | await comment.destroy(); 69 | return res.json({success: "message deleted"}); 70 | })); 71 | 72 | module.exports = router; 73 | -------------------------------------------------------------------------------- /backend/routes/api/collectionRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const asyncHandler = require('express-async-handler'); 3 | const { check } = require('express-validator'); 4 | 5 | const { handleValidationErrors } = require('../../utils/validation'); 6 | const { requireAuth } = require('../../utils/auth'); 7 | const { Album, User, Collection } = require('../../db/models'); 8 | 9 | const router = express.Router(); 10 | 11 | //GET COLLECTION IS IN USER ROUTER 12 | 13 | //count collections 14 | router.post("/count", asyncHandler(async(req, res) => { 15 | const {albumId} = req.body; 16 | const otherCollections = await Collection.count({ 17 | where: { 18 | 'albumId': albumId, 19 | } 20 | }); 21 | const count = {albumId: albumId, count: otherCollections} 22 | 23 | return res.json(count); 24 | })); 25 | 26 | //requireAuth 27 | router.post("/", requireAuth, asyncHandler(async (req, res) => { 28 | const {userId, albumId} = req.body; 29 | 30 | let newCollect = await Collection.create({ userId, albumId }); 31 | return res.json(newCollect); 32 | })); 33 | 34 | router.delete("/", requireAuth, asyncHandler(async (req, res) => { 35 | const { collectionRelationship } = req.body; 36 | 37 | let deletedCollection = await Collection.findAll({ 38 | where: { 39 | "albumId": collectionRelationship.albumId, 40 | "userId": collectionRelationship.userId 41 | } 42 | }); 43 | 44 | deletedCollection.forEach(async (record) => { 45 | await record.destroy(); 46 | }); 47 | 48 | return res.json(collectionRelationship); 49 | })); 50 | 51 | 52 | module.exports = router; 53 | -------------------------------------------------------------------------------- /backend/routes/api/index.js: -------------------------------------------------------------------------------- 1 | const router = require('express').Router(); 2 | const sessionRouter = require('./session.js'); 3 | const usersRouter = require('./users.js'); 4 | const albumRouter = require('./albums.js'); 5 | const collectionRouter = require('./collectionRoutes.js'); 6 | const searchRouter = require('./search'); 7 | 8 | 9 | router.use('/session', sessionRouter); 10 | router.use('/users', usersRouter); 11 | router.use('/albums', albumRouter); 12 | router.use('/collection', collectionRouter); 13 | router.use('/search', searchRouter); 14 | 15 | 16 | 17 | 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /backend/routes/api/search.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const asyncHandler = require('express-async-handler'); 3 | 4 | const Sequelize = require('sequelize'); 5 | const Op = Sequelize.Op; 6 | const { Album, Song, Artist } = require('../../db/models'); 7 | 8 | const router = express.Router(); 9 | 10 | 11 | router.post('/', asyncHandler(async (req, res, next) => { 12 | const { searchTerm, searchBy } = req.body; 13 | 14 | let recording; 15 | 16 | if (searchBy === "album" && searchTerm) { 17 | recording = await Album.findAll({ 18 | include: { model: Song, include: Artist }, 19 | where: { title: { [Op.iLike]: '%' + searchTerm + '%' } } 20 | }) 21 | } else if (searchBy === "album" && !searchTerm) { 22 | recording = await Album.findAll({ 23 | include: { model: Song, include: Artist } 24 | }) 25 | } else if (searchBy === "song" && searchTerm) { 26 | recording = await Song.findAll({ 27 | include: {model: Artist}, 28 | where: { title: { [Op.iLike]: '%' + searchTerm + '%' } } 29 | }) 30 | } else if (searchBy === "song" && !searchTerm) { 31 | recording = await Song.findAll({ 32 | include: [{ model: Album }, {model: Artist}] 33 | }) 34 | } else if (searchBy === "original-artist" && searchTerm) { 35 | recording = await Song.findAll({ 36 | include: [{ model: Album }, {model: Artist}], 37 | where: { originalArtist: { [Op.iLike]: '%' + searchTerm + '%' } } 38 | }) 39 | } else if (searchBy === "original-artist" && !searchTerm) { 40 | recording = await Song.findAll({ 41 | include: [{ model: Album }, {model: Artist}], 42 | where: { originalArtist: { [Op.iLike]: '%' + searchTerm + '%' } } 43 | }) 44 | } else if (searchBy === "artist" && searchTerm) { 45 | recording = await Artist.findAll({ 46 | include: { model: Song, include: Album }, 47 | where: { name: { [Op.iLike]: '%' + searchTerm + '%' } } 48 | }) 49 | } else if (searchBy === "artist" && !searchTerm) { 50 | recording = await Artist.findAll({ 51 | include: Song 52 | }) 53 | } 54 | 55 | return res.json(recording); 56 | 57 | })); 58 | 59 | 60 | module.exports = router; 61 | -------------------------------------------------------------------------------- /backend/routes/api/session.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const asyncHandler = require('express-async-handler'); 3 | 4 | const { setTokenCookie, restoreUser } = require('../../utils/auth'); 5 | const { User } = require('../../db/models'); 6 | 7 | const { check } = require('express-validator'); 8 | const { handleValidationErrors } = require('../../utils/validation'); 9 | 10 | const router = express.Router(); 11 | 12 | const validateLogin = [ 13 | check('credential') 14 | .exists({ checkFalsy: true }) 15 | .notEmpty() 16 | .withMessage('Please provide a valid email or username.'), 17 | check('password') 18 | .exists({ checkFalsy: true }) 19 | .withMessage('Please provide a password.'), 20 | handleValidationErrors, 21 | ]; 22 | 23 | // Log in 24 | router.post('/', validateLogin, asyncHandler(async (req, res, next) => { 25 | const { credential, password } = req.body; 26 | 27 | const user = await User.login({ credential, password }); 28 | 29 | if (!user) { 30 | const err = new Error('Login failed'); 31 | err.status = 401; 32 | err.title = 'Login failed'; 33 | err.errors = ['The provided credentials were invalid.']; 34 | return next(err); 35 | } 36 | await setTokenCookie(res, user); 37 | 38 | return res.json({ 39 | user, 40 | }); 41 | }), 42 | ); 43 | 44 | //demo login 45 | router.post('/demo', asyncHandler(async (req, res) => { 46 | //lookup any user from your seed data 47 | const demoUser = await User.findByPk(2); 48 | await setTokenCookie(res, demoUser); 49 | 50 | return res.json({ 51 | demoUser, 52 | }); 53 | })); 54 | 55 | // Log out 56 | router.delete('/', (_req, res) => { 57 | res.clearCookie('token'); 58 | return res.json({ message: 'success' }); 59 | }); 60 | 61 | // Restore session user 62 | router.get('/', restoreUser, (req, res) => { 63 | const { user } = req; 64 | if (user) { 65 | return res.json({ 66 | user: user.toSafeObject() 67 | }); 68 | } else return res.json({}); 69 | }); 70 | 71 | 72 | module.exports = router; 73 | -------------------------------------------------------------------------------- /backend/routes/api/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const asyncHandler = require('express-async-handler'); 3 | const { check } = require('express-validator'); 4 | 5 | const { handleValidationErrors } = require('../../utils/validation'); 6 | const { setTokenCookie, requireAuth } = require('../../utils/auth'); 7 | const { singlePublicFileUpload, singleMulterUpload } = require('../../awsS3'); 8 | const { Album, User, Collection } = require('../../db/models'); 9 | 10 | const router = express.Router(); 11 | 12 | const validateSignup = [ 13 | check('email') 14 | .exists({ checkFalsy: true }) 15 | .isEmail() 16 | .withMessage('Please provide a valid email.'), 17 | check('username') 18 | .exists({ checkFalsy: true }) 19 | .isLength({ min: 4 }) 20 | .withMessage('Please provide a username with at least 4 characters.'), 21 | check('username') 22 | .not() 23 | .isEmail() 24 | .withMessage('Username cannot be an email.'), 25 | check('password') 26 | .exists({ checkFalsy: true }) 27 | .isLength({ min: 6 }) 28 | .withMessage('Password must be 6 characters or more.'), 29 | handleValidationErrors, 30 | ]; 31 | 32 | router.post('/', singleMulterUpload("image"), validateSignup, asyncHandler(async (req, res) => { 33 | const { username, email, location, bio, password } = req.body; 34 | const profileImage = await singlePublicFileUpload(req.file); 35 | const user = await User.signup({ username, email, location, bio, password, profileImage }); 36 | 37 | await setTokenCookie(res, user); 38 | 39 | return res.json({ 40 | user, 41 | }); 42 | }), 43 | ); 44 | 45 | router.post("/:id", singleMulterUpload("image"), asyncHandler(async (req, res) => { 46 | const { location, bio } = req.body; 47 | const profileImage = await singlePublicFileUpload(req.file); 48 | const user = await User.findByPk(req.params.id); 49 | 50 | await user.update({ 51 | location: location, 52 | bio: bio, 53 | profileImage: profileImage 54 | }); 55 | // if (profileImage) { 56 | // user.profileImage = profileImage; 57 | // await user.save; 58 | // }; 59 | console.log(user); 60 | return res.json({user}); 61 | })); 62 | 63 | //GET COLLECTIONS FOR USER 64 | router.get("/:id", asyncHandler(async (req, res) => { 65 | const collections = await Collection.findAll({ 66 | where: { 67 | 'userId': req.params.id 68 | }, 69 | include: [ 70 | {model: Album}, 71 | {model: User} 72 | ] 73 | }); 74 | res.json(collections); 75 | })); 76 | 77 | module.exports = router; 78 | -------------------------------------------------------------------------------- /backend/routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const apiRouter = require('./api'); 4 | 5 | router.use('/api', apiRouter) 6 | 7 | // Static routes 8 | // Serve React build files in production 9 | if (process.env.NODE_ENV === 'production') { 10 | const path = require('path'); 11 | // Serve the frontend's index.html file at the root route 12 | router.get('/', (req, res) => { 13 | res.cookie('XSRF-TOKEN', req.csrfToken()); 14 | return res.sendFile( 15 | path.resolve(__dirname, '../../frontend', 'build', 'index.html') 16 | ); 17 | }); 18 | 19 | // Serve the static assets in the frontend's build folder 20 | router.use(express.static(path.resolve("../frontend/build"))); 21 | 22 | // Serve the frontend's index.html file at all other routes NOT starting with /api 23 | router.get(/^(?!\/?api).*/, (req, res) => { 24 | res.cookie('XSRF-TOKEN', req.csrfToken()); 25 | return res.sendFile( 26 | path.resolve(__dirname, '../../frontend', 'build', 'index.html') 27 | ); 28 | }); 29 | } 30 | //to add XSRF token in development 31 | if (process.env.NODE_ENV !== 'production') { 32 | router.get('/api/csrf/restore', (req, res) => { 33 | res.cookie('XSRF-TOKEN', req.csrfToken()); 34 | res.status(201).json({}); 35 | }); 36 | } 37 | 38 | module.exports = router; 39 | -------------------------------------------------------------------------------- /backend/utils/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const { jwtConfig } = require('../config'); 3 | const { User } = require('../db/models'); 4 | 5 | const { secret, expiresIn } = jwtConfig; 6 | 7 | const setTokenCookie = (res, user) => { 8 | const token = jwt.sign( 9 | { data: user.toSafeObject() }, 10 | secret, 11 | { expiresIn: parseInt(expiresIn) }, // 604,800 seconds = 1 week 12 | ); 13 | 14 | const isProduction = process.env.NODE_ENV === "production"; 15 | 16 | // Set the token cookie 17 | res.cookie('token', token, { 18 | maxAge: expiresIn * 1000, 19 | httpOnly: true, 20 | secure: isProduction, 21 | sameSite: isProduction && "Lax", 22 | }); 23 | 24 | return token; 25 | }; 26 | 27 | const restoreUser = (req, res, next) => { 28 | const { token } = req.cookies; 29 | 30 | return jwt.verify(token, secret, null, async (err, jwtPayload) => { 31 | if (err) { 32 | return next(); 33 | } 34 | 35 | try { 36 | const { id } = jwtPayload.data; 37 | req.user = await User.scope('currentUser').findByPk(id); 38 | } catch (e) { 39 | res.clearCookie('token'); 40 | return next(); 41 | } 42 | 43 | if (!req.user) res.clearCookie('token'); 44 | 45 | return next(); 46 | }); 47 | }; 48 | 49 | const requireAuth = [ 50 | restoreUser, 51 | function (req, res, next) { 52 | if (req.user) return next(); 53 | 54 | const err = new Error('Unauthorized'); 55 | err.title = 'Unauthorized'; 56 | err.errors = ['Unauthorized']; 57 | err.status = 401; 58 | return next(err); 59 | }, 60 | ]; 61 | 62 | module.exports = { setTokenCookie, restoreUser, requireAuth }; 63 | -------------------------------------------------------------------------------- /backend/utils/validation.js: -------------------------------------------------------------------------------- 1 | const { validationResult } = require('express-validator'); 2 | 3 | const handleValidationErrors = (req, _res, next) => { 4 | const validationErrors = validationResult(req); 5 | 6 | if (!validationErrors.isEmpty()) { 7 | const errors = validationErrors 8 | .array() 9 | .map((error) => `${error.msg}`); 10 | 11 | const err = Error('Bad request.'); 12 | err.errors = errors; 13 | err.status = 400; 14 | err.title = 'Bad request.'; 15 | next(err); 16 | } 17 | next(); 18 | }; 19 | 20 | module.exports = { 21 | handleValidationErrors, 22 | }; 23 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | .eslintcache 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.12.0", 7 | "@testing-library/react": "^11.2.7", 8 | "@testing-library/user-event": "^12.8.3", 9 | "js-cookie": "^2.2.1", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-redux": "^7.2.4", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "redux": "^4.1.0", 16 | "redux-thunk": "^2.3.0" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | }, 39 | "devDependencies": { 40 | "redux-logger": "^3.0.6" 41 | }, 42 | "proxy": "http://localhost:5000" 43 | } 44 | -------------------------------------------------------------------------------- /frontend/public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boothjacobs/React-barBand/91e01286b544a859f6e4809d581bb1aef249db98/frontend/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /frontend/public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boothjacobs/React-barBand/91e01286b544a859f6e4809d581bb1aef249db98/frontend/public/favicon/favicon.ico -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 |❤️ You own this
) : () } 53 |{album?.description}
74 |{user.username} {comment.body}
38 |BarBand is a BandCamp clone populated by cover songs. We believe that re-recording a beloved song is an act of devotion.
19 |Create an account to build a collection of your own and leave comments on your favorite tribute albums.
20 |Search by song title, recording artist, original artist, or album, or browse by any of those categories.
21 |Some things you do for money, and some you do for love, love, love.
24 |{albums[1].description}
35 |{album.description}
25 |{song.Artist?.name}
38 |Originally recorded by {song.originalArtist}
39 | Appears On {song.Album.title} 40 |{artist.location}
53 |appears in {totalCollections[albumId] ? totalCollections[albumId] : 0} total collections
17 |{user?.location}
76 |{user?.bio}
77 |