forked from watsonbox/exportify
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor PlaylistExporter in preparation for adding more data
(Includes migration of PlaylistExporter and helpers to TypeScript)
- Loading branch information
Showing
11 changed files
with
218 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { saveAs } from "file-saver" | ||
|
||
import TracksData from "components/tracks_data/TracksData" | ||
import TracksBaseData from "components/tracks_data/TracksBaseData" | ||
|
||
class TracksCsvFile { | ||
playlist: any | ||
columnNames: string[] | ||
lineData: Map<string, string[]> | ||
|
||
constructor(playlist: any) { | ||
this.playlist = playlist | ||
this.columnNames = [] | ||
this.lineData = new Map() | ||
} | ||
|
||
async addData(tracksData: TracksData) { | ||
this.columnNames.push(...tracksData.dataLabels()) | ||
|
||
const data: Map<string, string[]> = await tracksData.data() | ||
|
||
data.forEach((value: string[], key: string) => { | ||
if (this.lineData.has(key)) { | ||
this.lineData.get(key)!.push(...value) | ||
} else { | ||
this.lineData.set(key, value) | ||
} | ||
}) | ||
} | ||
|
||
content(): string { | ||
let csvContent = '' | ||
|
||
csvContent += this.columnNames.map(this.sanitize).join() + "\n" | ||
|
||
this.lineData.forEach((lineData, trackId) => { | ||
csvContent += lineData.map(this.sanitize).join(",") + "\n" | ||
}) | ||
|
||
return csvContent | ||
} | ||
|
||
sanitize(string: string): string { | ||
return '"' + String(string).replace(/"/g, '""') + '"' | ||
} | ||
} | ||
|
||
// Handles exporting a single playlist as a CSV file | ||
class PlaylistExporter { | ||
accessToken: string | ||
playlist: any | ||
|
||
constructor(accessToken: string, playlist: any) { | ||
this.accessToken = accessToken | ||
this.playlist = playlist | ||
} | ||
|
||
async export() { | ||
return this.csvData().then((data) => { | ||
var blob = new Blob([ data ], { type: "text/csv;charset=utf-8" }) | ||
saveAs(blob, this.fileName(), true) | ||
}) | ||
} | ||
|
||
async csvData() { | ||
const tracksCsvFile = new TracksCsvFile(this.playlist) | ||
const tracksBaseData = new TracksBaseData(this.accessToken, this.playlist) | ||
|
||
await tracksCsvFile.addData(tracksBaseData) | ||
|
||
tracksBaseData.tracks() | ||
|
||
return tracksCsvFile.content() | ||
} | ||
|
||
fileName(): string { | ||
return this.playlist.name.replace(/[\x00-\x1F\x7F/\\<>:;"|=,.?*[\] ]+/g, "_").toLowerCase() + ".csv" // eslint-disable-line no-control-regex | ||
} | ||
} | ||
|
||
export default PlaylistExporter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import TracksData from "./TracksData" | ||
import { apiCall } from "helpers" | ||
|
||
class TracksBaseData extends TracksData { | ||
playlist: any | ||
|
||
constructor(accessToken: string, playlist: any) { | ||
super(accessToken) | ||
this.playlist = playlist | ||
} | ||
|
||
dataLabels() { | ||
return [ | ||
"Track URI", | ||
"Track Name", | ||
"Artist URI", | ||
"Artist Name", | ||
"Album URI", | ||
"Album Name", | ||
"Disc Number", | ||
"Track Number", | ||
"Track Duration (ms)", | ||
"Added By", | ||
"Added At" | ||
] | ||
} | ||
|
||
async tracks() { | ||
await this.getPlaylistItems() | ||
|
||
return this.playlistItems.map(i => i.track) | ||
} | ||
|
||
async data() { | ||
await this.getPlaylistItems() | ||
|
||
return new Map(this.playlistItems.map(item => { | ||
return [ | ||
item.track.id, | ||
[ | ||
item.track.uri, | ||
item.track.name, | ||
item.track.artists.map((a: any) => { return a.uri }).join(', '), | ||
item.track.artists.map((a: any) => { return String(a.name).replace(/,/g, "\\,") }).join(', '), | ||
item.track.album.uri, | ||
item.track.album.name, | ||
item.track.disc_number, | ||
item.track.track_number, | ||
item.track.duration_ms, | ||
item.added_by == null ? '' : item.added_by.uri, | ||
item.added_at | ||
] | ||
] | ||
})) | ||
} | ||
|
||
// Memoization supporting multiple calls | ||
private playlistItems: any[] = [] | ||
private async getPlaylistItems() { | ||
if (this.playlistItems.length > 0) { | ||
return this.playlistItems | ||
} | ||
|
||
var requests = [] | ||
var limit = this.playlist.tracks.limit || 100 | ||
|
||
for (var offset = 0; offset < this.playlist.tracks.total; offset = offset + limit) { | ||
requests.push(`${this.playlist.tracks.href.split('?')[0]}?offset=${offset}&limit=${limit}`) | ||
} | ||
|
||
const trackPromises = requests.map(request => { return apiCall(request, this.accessToken) }) | ||
const trackResponses = await Promise.all(trackPromises) | ||
|
||
this.playlistItems = trackResponses.flatMap(response => { | ||
return response.data.items.filter((i: any) => i.track) // Exclude null track attributes | ||
}) | ||
} | ||
} | ||
|
||
export default TracksBaseData |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
abstract class TracksData { | ||
accessToken: string | ||
|
||
constructor(accessToken: string) { | ||
this.accessToken = accessToken | ||
} | ||
|
||
abstract dataLabels(): string[] | ||
abstract data(): Promise<Map<string, string[]>> | ||
} | ||
|
||
export default TracksData |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.