Skip to content

Commit

Permalink
Fix exporting of duplicate tracks [Fixes watsonbox#94]
Browse files Browse the repository at this point in the history
  • Loading branch information
watsonbox committed Apr 20, 2021
1 parent d1247f7 commit 770c045
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 31 deletions.
61 changes: 39 additions & 22 deletions src/components/PlaylistExporter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,45 @@ import TracksAlbumData from "components/data/TracksAlbumData"

class TracksCsvFile {
playlist: any
trackItems: any
columnNames: string[]
lineData: Map<string, string[]>

constructor(playlist: any) {
lineTrackUris: string[]
lineTrackData: string[][]

constructor(playlist: any, trackItems: any) {
this.playlist = playlist
this.columnNames = []
this.trackItems = trackItems
this.columnNames = [
"Added By",
"Added At"
]

this.lineData = new Map()
this.lineTrackUris = trackItems.map((i: any) => i.track.uri)
this.lineTrackData = trackItems.map((i: any) => [
i.added_by == null ? '' : i.added_by.uri,
i.added_at
])
}

async addData(tracksData: TracksData) {
this.columnNames.push(...tracksData.dataLabels())
async addData(tracksData: TracksData, before = false) {
if (before) {
this.columnNames.unshift(...tracksData.dataLabels())
} else {
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)
this.lineTrackUris.forEach((uri: string, index: number) => {
if (data.has(uri)) {
if (before) {
this.lineTrackData[index].unshift(...data.get(uri)!)
} else {
this.lineTrackData[index].push(...data.get(uri)!)
}
}
})
}
Expand All @@ -36,8 +56,8 @@ class TracksCsvFile {

csvContent += this.columnNames.map(this.sanitize).join() + "\n"

this.lineData.forEach((lineData, trackId) => {
csvContent += lineData.map(this.sanitize).join(",") + "\n"
this.lineTrackData.forEach((lineTrackData, trackId) => {
csvContent += lineTrackData.map(this.sanitize).join(",") + "\n"
})

return csvContent
Expand Down Expand Up @@ -68,29 +88,26 @@ class PlaylistExporter {
}

async csvData() {
const tracksCsvFile = new TracksCsvFile(this.playlist)
const tracksBaseData = new TracksBaseData(this.accessToken, this.playlist)
const items = await tracksBaseData.trackItems()
const tracks = items.map(i => i.track)
const tracksCsvFile = new TracksCsvFile(this.playlist, items)

await tracksCsvFile.addData(tracksBaseData)
const tracks = await tracksBaseData.tracks()
// Add base data before existing (item) data, for backward compatibility
await tracksCsvFile.addData(tracksBaseData, true)

if (this.config.includeArtistsData) {
const tracksArtistsData = new TracksArtistsData(this.accessToken, tracks)
await tracksCsvFile.addData(tracksArtistsData)
await tracksCsvFile.addData(new TracksArtistsData(this.accessToken, tracks))
}

if (this.config.includeAudioFeaturesData) {
const tracksAudioFeaturesData = new TracksAudioFeaturesData(this.accessToken, tracks)
await tracksCsvFile.addData(tracksAudioFeaturesData)
await tracksCsvFile.addData(new TracksAudioFeaturesData(this.accessToken, tracks))
}

if (this.config.includeAlbumData) {
const tracksAlbumData = new TracksAlbumData(this.accessToken, tracks)
await tracksCsvFile.addData(tracksAlbumData)
await tracksCsvFile.addData(new TracksAlbumData(this.accessToken, tracks))
}

tracksBaseData.tracks()

return tracksCsvFile.content()
}

Expand Down
36 changes: 35 additions & 1 deletion src/components/PlaylistTable.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import JSZip from "jszip"
import PlaylistTable from "./PlaylistTable"

import "../icons"
import { handlerCalled, handlers, nullTrackHandlers, localTrackHandlers } from "../mocks/handlers"
import { handlerCalled, handlers, nullTrackHandlers, localTrackHandlers, duplicateTrackHandlers } from "../mocks/handlers"

const server = setupServer(...handlers)

Expand Down Expand Up @@ -293,6 +293,40 @@ describe("single playlist exporting", () => {
true
)
})

test("playlist with duplicate tracks includes them", async () => {
server.use(...duplicateTrackHandlers)

const saveAsMock = jest.spyOn(FileSaver, "saveAs")
saveAsMock.mockImplementation(jest.fn())

render(<PlaylistTable accessToken="TEST_ACCESS_TOKEN" />);

expect(await screen.findByText(/Export All/)).toBeInTheDocument()

const linkElement = screen.getAllByText("Export")[1]

expect(linkElement).toBeInTheDocument()

userEvent.click(linkElement)

await waitFor(() => {
expect(saveAsMock).toHaveBeenCalledTimes(1)
})

expect(saveAsMock).toHaveBeenCalledWith(
{
content: [
`${baseTrackHeaders}\n` +
'"spotify:track:7ATyvp3TmYBmGW7YuC8DJ3","One Twos / Run Run Run","spotify:artist:69lEbRQRe29JdyLrewNAvD","Ghostpoet","spotify:album:6jiLkuSnhzDvzsHJlweoGh","Peanut Butter Blues and Melancholy Jam","spotify:artist:69lEbRQRe29JdyLrewNAvD","Ghostpoet","2011","https://i.scdn.co/image/ab67616d0000b273306e7640be17c5b3468e6e80","1","1","241346","https://p.scdn.co/mp3-preview/137d431ad0cf987b147dccea6304aca756e923c1?cid=9950ac751e34487dbbe027c4fd7f8e99","false","22","spotify:user:watsonbox","2020-11-03T15:19:04Z"\n' +
'"spotify:track:7ATyvp3TmYBmGW7YuC8DJ3","One Twos / Run Run Run","spotify:artist:69lEbRQRe29JdyLrewNAvD","Ghostpoet","spotify:album:6jiLkuSnhzDvzsHJlweoGh","Peanut Butter Blues and Melancholy Jam","spotify:artist:69lEbRQRe29JdyLrewNAvD","Ghostpoet","2011","https://i.scdn.co/image/ab67616d0000b273306e7640be17c5b3468e6e80","1","1","241346","https://p.scdn.co/mp3-preview/137d431ad0cf987b147dccea6304aca756e923c1?cid=9950ac751e34487dbbe027c4fd7f8e99","false","22","spotify:user:watsonbox","2020-11-20T15:19:04Z"\n'
],
options: { type: 'text/csv;charset=utf-8' }
},
'ghostpoet_–_peanut_butter_blues_and_melancholy_jam.csv',
true
)
})
})

describe("searching playlists", () => {
Expand Down
12 changes: 4 additions & 8 deletions src/components/data/TracksBaseData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,14 @@ class TracksBaseData extends TracksData {
"Track Duration (ms)",
"Track Preview URL",
"Explicit",
"Popularity",
"Added By",
"Added At"
"Popularity"
]
}

async tracks() {
async trackItems() {
await this.getPlaylistItems()

return this.playlistItems.map(i => i.track)
return this.playlistItems
}

async data() {
Expand All @@ -60,9 +58,7 @@ class TracksBaseData extends TracksData {
item.track.duration_ms,
item.track.preview_url == null ? '' : item.track.preview_url,
item.track.explicit,
item.track.popularity,
item.added_by == null ? '' : item.added_by.uri,
item.added_at
item.track.popularity
]
]
}))
Expand Down
194 changes: 194 additions & 0 deletions src/mocks/handlers.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1136,3 +1136,197 @@ export const localTrackHandlers = [
))
})
]

export const duplicateTrackHandlers = [
rest.get('https://api.spotify.com/v1/playlists/4XOGDpHMrVoH33uJEwHWU5/tracks?offset=0&limit=10', (req, res, ctx) => {
handlerCalled(req.url.toString())

if (req.headers.get("Authorization") !== "Bearer TEST_ACCESS_TOKEN") {
return res(ctx.status(401), ctx.json({ message: 'Not authorized' }))
}

return res(ctx.json(
{
"href" : "https://api.spotify.com/v1/playlists/4XOGDpHMrVoH33uJEwHWU5/tracks?offset=0&limit=100",
"items" : [
{
"added_at" : "2020-11-03T15:19:04Z",
"added_by" : {
"external_urls" : {
"spotify" : "https://open.spotify.com/user/watsonbox"
},
"href" : "https://api.spotify.com/v1/users/watsonbox",
"id" : "watsonbox",
"type" : "user",
"uri" : "spotify:user:watsonbox"
},
"is_local" : false,
"primary_color" : null,
"track" : {
"album" : {
"album_type" : "album",
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/69lEbRQRe29JdyLrewNAvD"
},
"href" : "https://api.spotify.com/v1/artists/69lEbRQRe29JdyLrewNAvD",
"id" : "69lEbRQRe29JdyLrewNAvD",
"name" : "Ghostpoet",
"type" : "artist",
"uri" : "spotify:artist:69lEbRQRe29JdyLrewNAvD"
} ],
"available_markets" : [ "AD", "AE", "AL", "AR", "AT", "AU", "BA", "BE", "BG", "BH", "BO", "BR", "BY", "CA", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "DZ", "EC", "EE", "EG", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HR", "HU", "ID", "IE", "IL", "IN", "IS", "IT", "JO", "KW", "KZ", "LB", "LI", "LT", "LU", "LV", "MA", "MC", "MD", "MK", "MT", "MX", "MY", "NI", "NL", "NO", "NZ", "OM", "PA", "PE", "PH", "PL", "PS", "PT", "PY", "QA", "RO", "RU", "SA", "SE", "SG", "SI", "SK", "SV", "TH", "TN", "TR", "TW", "UA", "UY", "VN", "ZA" ],
"external_urls" : {
"spotify" : "https://open.spotify.com/album/6jiLkuSnhzDvzsHJlweoGh"
},
"href" : "https://api.spotify.com/v1/albums/6jiLkuSnhzDvzsHJlweoGh",
"id" : "6jiLkuSnhzDvzsHJlweoGh",
"images" : [ {
"height" : 640,
"url" : "https://i.scdn.co/image/ab67616d0000b273306e7640be17c5b3468e6e80",
"width" : 640
}, {
"height" : 300,
"url" : "https://i.scdn.co/image/ab67616d00001e02306e7640be17c5b3468e6e80",
"width" : 300
}, {
"height" : 64,
"url" : "https://i.scdn.co/image/ab67616d00004851306e7640be17c5b3468e6e80",
"width" : 64
} ],
"name" : "Peanut Butter Blues and Melancholy Jam",
"release_date" : "2011",
"release_date_precision" : "year",
"total_tracks" : 10,
"type" : "album",
"uri" : "spotify:album:6jiLkuSnhzDvzsHJlweoGh"
},
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/69lEbRQRe29JdyLrewNAvD"
},
"href" : "https://api.spotify.com/v1/artists/69lEbRQRe29JdyLrewNAvD",
"id" : "69lEbRQRe29JdyLrewNAvD",
"name" : "Ghostpoet",
"type" : "artist",
"uri" : "spotify:artist:69lEbRQRe29JdyLrewNAvD"
} ],
"available_markets" : [ "AD", "AE", "AL", "AR", "AT", "AU", "BA", "BE", "BG", "BH", "BO", "BR", "BY", "CA", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "DZ", "EC", "EE", "EG", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HR", "HU", "ID", "IE", "IL", "IN", "IS", "IT", "JO", "KW", "KZ", "LB", "LI", "LT", "LU", "LV", "MA", "MC", "MD", "MK", "MT", "MX", "MY", "NI", "NL", "NO", "NZ", "OM", "PA", "PE", "PH", "PL", "PS", "PT", "PY", "QA", "RO", "RU", "SA", "SE", "SG", "SI", "SK", "SV", "TH", "TN", "TR", "TW", "UA", "UY", "VN", "ZA" ],
"disc_number" : 1,
"duration_ms" : 241346,
"episode" : false,
"explicit" : false,
"external_ids" : {
"isrc" : "GBMEF1100339"
},
"external_urls" : {
"spotify" : "https://open.spotify.com/track/7ATyvp3TmYBmGW7YuC8DJ3"
},
"href" : "https://api.spotify.com/v1/tracks/7ATyvp3TmYBmGW7YuC8DJ3",
"id" : "7ATyvp3TmYBmGW7YuC8DJ3",
"is_local" : false,
"name" : "One Twos / Run Run Run",
"popularity" : 22,
"preview_url" : "https://p.scdn.co/mp3-preview/137d431ad0cf987b147dccea6304aca756e923c1?cid=9950ac751e34487dbbe027c4fd7f8e99",
"track" : true,
"track_number" : 1,
"type" : "track",
"uri" : "spotify:track:7ATyvp3TmYBmGW7YuC8DJ3"
},
"video_thumbnail" : {
"url" : null
}
},
{
"added_at" : "2020-11-20T15:19:04Z",
"added_by" : {
"external_urls" : {
"spotify" : "https://open.spotify.com/user/watsonbox"
},
"href" : "https://api.spotify.com/v1/users/watsonbox",
"id" : "watsonbox",
"type" : "user",
"uri" : "spotify:user:watsonbox"
},
"is_local" : false,
"primary_color" : null,
"track" : {
"album" : {
"album_type" : "album",
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/69lEbRQRe29JdyLrewNAvD"
},
"href" : "https://api.spotify.com/v1/artists/69lEbRQRe29JdyLrewNAvD",
"id" : "69lEbRQRe29JdyLrewNAvD",
"name" : "Ghostpoet",
"type" : "artist",
"uri" : "spotify:artist:69lEbRQRe29JdyLrewNAvD"
} ],
"available_markets" : [ "AD", "AE", "AL", "AR", "AT", "AU", "BA", "BE", "BG", "BH", "BO", "BR", "BY", "CA", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "DZ", "EC", "EE", "EG", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HR", "HU", "ID", "IE", "IL", "IN", "IS", "IT", "JO", "KW", "KZ", "LB", "LI", "LT", "LU", "LV", "MA", "MC", "MD", "MK", "MT", "MX", "MY", "NI", "NL", "NO", "NZ", "OM", "PA", "PE", "PH", "PL", "PS", "PT", "PY", "QA", "RO", "RU", "SA", "SE", "SG", "SI", "SK", "SV", "TH", "TN", "TR", "TW", "UA", "UY", "VN", "ZA" ],
"external_urls" : {
"spotify" : "https://open.spotify.com/album/6jiLkuSnhzDvzsHJlweoGh"
},
"href" : "https://api.spotify.com/v1/albums/6jiLkuSnhzDvzsHJlweoGh",
"id" : "6jiLkuSnhzDvzsHJlweoGh",
"images" : [ {
"height" : 640,
"url" : "https://i.scdn.co/image/ab67616d0000b273306e7640be17c5b3468e6e80",
"width" : 640
}, {
"height" : 300,
"url" : "https://i.scdn.co/image/ab67616d00001e02306e7640be17c5b3468e6e80",
"width" : 300
}, {
"height" : 64,
"url" : "https://i.scdn.co/image/ab67616d00004851306e7640be17c5b3468e6e80",
"width" : 64
} ],
"name" : "Peanut Butter Blues and Melancholy Jam",
"release_date" : "2011",
"release_date_precision" : "year",
"total_tracks" : 10,
"type" : "album",
"uri" : "spotify:album:6jiLkuSnhzDvzsHJlweoGh"
},
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/69lEbRQRe29JdyLrewNAvD"
},
"href" : "https://api.spotify.com/v1/artists/69lEbRQRe29JdyLrewNAvD",
"id" : "69lEbRQRe29JdyLrewNAvD",
"name" : "Ghostpoet",
"type" : "artist",
"uri" : "spotify:artist:69lEbRQRe29JdyLrewNAvD"
} ],
"available_markets" : [ "AD", "AE", "AL", "AR", "AT", "AU", "BA", "BE", "BG", "BH", "BO", "BR", "BY", "CA", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "DZ", "EC", "EE", "EG", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HR", "HU", "ID", "IE", "IL", "IN", "IS", "IT", "JO", "KW", "KZ", "LB", "LI", "LT", "LU", "LV", "MA", "MC", "MD", "MK", "MT", "MX", "MY", "NI", "NL", "NO", "NZ", "OM", "PA", "PE", "PH", "PL", "PS", "PT", "PY", "QA", "RO", "RU", "SA", "SE", "SG", "SI", "SK", "SV", "TH", "TN", "TR", "TW", "UA", "UY", "VN", "ZA" ],
"disc_number" : 1,
"duration_ms" : 241346,
"episode" : false,
"explicit" : false,
"external_ids" : {
"isrc" : "GBMEF1100339"
},
"external_urls" : {
"spotify" : "https://open.spotify.com/track/7ATyvp3TmYBmGW7YuC8DJ3"
},
"href" : "https://api.spotify.com/v1/tracks/7ATyvp3TmYBmGW7YuC8DJ3",
"id" : "7ATyvp3TmYBmGW7YuC8DJ3",
"is_local" : false,
"name" : "One Twos / Run Run Run",
"popularity" : 22,
"preview_url" : "https://p.scdn.co/mp3-preview/137d431ad0cf987b147dccea6304aca756e923c1?cid=9950ac751e34487dbbe027c4fd7f8e99",
"track" : true,
"track_number" : 1,
"type" : "track",
"uri" : "spotify:track:7ATyvp3TmYBmGW7YuC8DJ3"
},
"video_thumbnail" : {
"url" : null
}
}
]
}
))
})
]

0 comments on commit 770c045

Please sign in to comment.