From c5ac835b56123b1496da968f111e2a566351df6b Mon Sep 17 00:00:00 2001
From: Hugo Vieilledent <hugo.vieilledent@gmail.com>
Date: Tue, 15 Jan 2019 19:27:16 +0100
Subject: [PATCH] add bandcamp parser, refactor naming, add type to parse
 answer

---
 main.js                        | 85 +++++++++++++++++++++++-----------
 src/providers.js               |  3 +-
 test.js                        | 57 +++++++++++++++++------
 tests/provider-dictionaries.js | 13 ++++++
 4 files changed, 118 insertions(+), 40 deletions(-)

diff --git a/main.js b/main.js
index c3aeca6..9ad0931 100644
--- a/main.js
+++ b/main.js
@@ -1,41 +1,77 @@
 import youtubeRegex from 'youtube-regex'
 import providersList from './src/providers'
 
-const youtubeUrlToId = (url) => {
+/*
+	Media provider URL parsers
+*/
+
+const parseFileUrl = (url) => {
+	let result = new URL(url)
+	let s = result.pathname.split('/')
+	let id = s[s.length - 1]
+	return {
+		id,
+		type: 'audio'
+	}
+}
+
+const parseYoutubeUrl = (url) => {
 	const results = youtubeRegex().exec(url)
 	if (!results) {
 		return false
 	}
-	return results[1]
-}
-
-const fileUrlToId = (url) => {
-	let result = new URL(url)
-	let s = result.pathname.split('/')
-	return s[s.length - 1]
+	const id = results[1];
+	return {
+		id,
+		type: 'video'
+	}
 }
 
-const discogsUrlToId = (url) => {
+const parseDiscogsUrl = (url) => {
   // https://regexr.com/3i5fa
   let discogsReleaseRegex = /([0-9]+(?:$|(?=\?)|(?=\/$)))/gm
   let result = discogsReleaseRegex.exec(url)
   if (!result) {
 		throw new Error('Could not find id from Discogs URL')
   }
-  return result[0]
+  return {
+		id: result[0],
+		type: 'release'
+	}
 }
 
-const findId = (url, provider) => {
+const parseBandcampUrl = (url) => {
+	// https://regexr.com/46f84
+	let bandcampRegex = /(album|track)(?:\/)([^\/\s\?]+)/gm
+  let result = bandcampRegex.exec(url)
+  if (!result) {
+		throw new Error('Could not find informations from Bandcamp URL')
+  }
+  return {
+		type: result[1],
+		id: result[2]
+	}
+}
+
+const parseUrl = (url) => {
+	let provider = findProvider(url)
   let methods = {
-		youtube: (url) => youtubeUrlToId(url),
-		file: (url) => fileUrlToId(url),
-		discogs: (url) => discogsUrlToId(url)
+		youtube: (url) => parseYoutubeUrl(url),
+		file: (url) => parseFileUrl(url),
+		discogs: (url) => parseDiscogsUrl(url),
+		bandcamp: (url) => parseBandcampUrl(url)
   }
-	let extractId = methods[provider]
-	if (typeof extractId !== 'function') {
-		throw new Error('Could not find provider method from: ' + extractId)
-	} else {
-		return extractId(url)
+	let providerUrlParser = methods[provider]
+
+	if (typeof providerUrlParser !== 'function') {
+		throw new Error(`Could not find a url parser for the provider: ${provider}, with url: ${url}`)
+	}
+
+	const {id, type} = providerUrlParser(url)
+	return {
+		id,
+		type,
+		provider
 	}
 }
 
@@ -74,18 +110,15 @@ const mediaUrlParser = (inputUrl) => {
 	// 0. normalize url, so it can be parsed homogenously
 	const url = normalizeUrl(inputUrl)
 
-	// 1. detect which provider's url it is
-	let provider = findProvider(url)
-
-	// 2. in this provider url, find a media `id`
-	let id = findId(url, provider)
+	// Parse the media provider url, to find: provider id, media id, media type
+	let {provider, id, type} = parseUrl(url)
 
 	if (!id) {
 		throw new Error('Could not detect id from: ' + url)
 	}
 
-	// 3. return a result object
-	return { url, provider, id }
+	// 4. return a result object
+	return { url, provider, id, type }
 }
 
 export {
diff --git a/src/providers.js b/src/providers.js
index c5b1588..614b7ab 100644
--- a/src/providers.js
+++ b/src/providers.js
@@ -1,7 +1,8 @@
 const providersList =  {
 	'youtube.com': 'youtube',
 	'youtu.be': 'youtube',
-	'discogs.com': 'discogs'
+	'discogs.com': 'discogs',
+	'bandcamp.com': 'bandcamp'
 }
 
 
diff --git a/test.js b/test.js
index 99eb1a3..264f743 100644
--- a/test.js
+++ b/test.js
@@ -1,12 +1,18 @@
 import test from 'ava'
 import {mediaUrlParser} from './index.js'
-import {youtubeDict, fileDict, discogsDict} from './tests/provider-dictionaries'
+import {youtubeDict, fileDict, discogsDict, bandcampDict} from './tests/provider-dictionaries'
 
 /*
   Providers, check if they are correctly discovered in a url
 */
 
-test('Youtube URL correctly parse the provider', t => {
+test('It throws when it cannot detect a provider', t => {
+	const error = t.throws(() => {
+		mediaUrlParser('notanurl')
+	})
+})
+
+test('Youtube URL correctly parses the provider', t => {
 	t.plan(youtubeDict.length)
 
 	youtubeDict.forEach(item => {
@@ -15,7 +21,7 @@ test('Youtube URL correctly parse the provider', t => {
 	})
 })
 
-test('object returned includes a normalized url property', t => {
+test('Object returned includes a normalized url property', t => {
 	youtubeDict.forEach(item => {
 		let r = mediaUrlParser(item[0])
 		t.truthy(typeof r.url, 'string')
@@ -23,7 +29,7 @@ test('object returned includes a normalized url property', t => {
 	})
 })
 
-test('File URL correctly parse the provider', async t => {
+test('File URL correctly parses the provider', async t => {
 	t.plan(fileDict.length)
 
 	fileDict.forEach(item => {
@@ -32,7 +38,7 @@ test('File URL correctly parse the provider', async t => {
 	})
 })
 
-test('Discogs URL correctly parse the provider', async t => {
+test('Discogs URL correctly parses the provider', async t => {
 	t.plan(discogsDict.length)
 
 	discogsDict.forEach(item => {
@@ -41,11 +47,20 @@ test('Discogs URL correctly parse the provider', async t => {
 	})
 })
 
+test('Bandcamp URL correctly parses the provider', async t => {
+	t.plan(bandcampDict.length)
+
+	bandcampDict.forEach(item => {
+		let r = mediaUrlParser(item[0])
+		t.is(r.provider, 'bandcamp')
+	})
+})
+
 /*
   ID, check if the if is found in a url of a specific provider
 */
 
-test('Youtube URL correctly parse the id correctly', t => {
+test('Youtube URL correctly parses the id', t => {
 	t.plan(youtubeDict.length)
 
 	youtubeDict.forEach(item => {
@@ -54,7 +69,7 @@ test('Youtube URL correctly parse the id correctly', t => {
 	})
 })
 
-test('File URL correctly parse the id correctly', t => {
+test('File URL correctly parses the id', t => {
 	t.plan(fileDict.length)
 
 	fileDict.forEach(item => {
@@ -63,17 +78,33 @@ test('File URL correctly parse the id correctly', t => {
 	})
 })
 
-test('It throws when it cant detect a provider', t => {
-	const error = t.throws(() => {
-		mediaUrlParser('notanurl')
+test('Discogs URL correctly parses the id', t => {
+	t.plan(discogsDict.length)
+
+	discogsDict.forEach(item => {
+		let r = mediaUrlParser(item[0])
+		t.is(r.id, item[1])
 	})
 })
 
-test('Discogs URL correctly parse the id correctly', t => {
-	t.plan(discogsDict.length)
+test('Bandcamp URL correctly parses the id ', t => {
+	t.plan(bandcampDict.length)
 
-	discogsDict.forEach(item => {
+	bandcampDict.forEach(item => {
 		let r = mediaUrlParser(item[0])
 		t.is(r.id, item[1])
 	})
 })
+
+/*
+  Type, check if the type is found in a url of a specific provider
+*/
+
+test('Bandcamp URL correctly parses the media type', async t => {
+	t.plan(bandcampDict.length)
+
+	bandcampDict.forEach(item => {
+		let r = mediaUrlParser(item[0])
+		t.is(r.type, item[2])
+	})
+})
diff --git a/tests/provider-dictionaries.js b/tests/provider-dictionaries.js
index 1af32db..53bcdfa 100644
--- a/tests/provider-dictionaries.js
+++ b/tests/provider-dictionaries.js
@@ -110,3 +110,16 @@ exports.discogsDict = [
 		'7983975'
 	]
 ]
+
+exports.bandcampDict = [
+	[
+		'https://iliantape.bandcamp.com/album/it039-andrea-forse',
+		'it039-andrea-forse',
+		'album'
+	],
+	[
+		'https://iliantape.bandcamp.com/track/future-atmo',
+		'future-atmo',
+		'track'
+	]
+]