diff --git a/src/components/Main.vue b/src/components/Main.vue
deleted file mode 100644
index 52f78c8adaa65ac136a3597b48e3f7187e0fce92..0000000000000000000000000000000000000000
--- a/src/components/Main.vue
+++ /dev/null
@@ -1,461 +0,0 @@
-<!--
-  - @copyright Copyright (c) 2020 Jonas Heinrich
-  -
-  - @author Jonas Heinrich <onny@project-insanity.org>
-  -
-  - @license GNU AGPL version 3 or any later version
-  -
-  - This program is free software: you can redistribute it and/or modify
-  - it under the terms of the GNU Affero General Public License as
-  - published by the Free Software Foundation, either version 3 of the
-  - License, or (at your option) any later version.
-  -
-  - This program is distributed in the hope that it will be useful,
-  - but WITHOUT ANY WARRANTY; without even the implied warranty of
-  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  - GNU Affero General Public License for more details.
-  -
-  - You should have received a copy of the GNU Affero General Public License
-  - along with this program. If not, see <http://www.gnu.org/licenses/>.
-  -
-  -->
-
-<template>
-	<Content app-name="podcast">
-		<Navigation
-			:station-data="tableData" />
-		<AppContent>
-			<Table
-				v-show="!pageLoading && tableData.length > 0"
-				v-resize="onResize"
-				:station-data="tableData"
-				:favorites="favorites"
-				@doPlay="doPlay"
-				@doFavor="doFavor" />
-			<EmptyContent
-				v-if="pageLoading"
-				icon="icon-loading" />
-			<EmptyContent
-				v-if="tableData.length === 0 && !pageLoading"
-				:icon="emptyContentIcon">
-				{{ emptyContentMessage }}
-				<template #desc>
-					{{ emptyContentDesc }}
-				</template>
-			</EmptyContent>
-		</AppContent>
-	</Content>
-</template>
-
-<script>
-import Content from '@nextcloud/vue/dist/Components/Content'
-import AppContent from '@nextcloud/vue/dist/Components/AppContent'
-import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
-import Navigation from './Navigation'
-import Table from './Table'
-import { Howl, Howler } from 'howler'
-
-import { generateUrl } from '@nextcloud/router'
-import { showError } from '@nextcloud/dialogs'
-import axios from '@nextcloud/axios'
-
-let audioPlayer = null
-const requesttoken = axios.defaults.headers.requesttoken
-
-export default {
-	name: 'Main',
-	components: {
-		Navigation,
-		Content,
-		AppContent,
-		Table,
-		EmptyContent,
-	},
-	data: () => ({
-		tableData: [],
-		pageLoading: false,
-		favorites: [],
-		queryParams: {},
-	}),
-	computed: {
-		player() {
-			return this.$store.state.player
-		},
-		emptyContentMessage() {
-			if (this.$route.name === 'FAVORITES') {
-				return t('podcast', 'No favorites yet')
-			} else if (this.$route.name === 'RECENT') {
-				return t('podcast', 'No recent stations yet')
-			} else if (this.$route.name === 'SEARCH') {
-				return t('podcast', 'No search results')
-			}
-			return 'No stations here'
-		},
-		emptyContentIcon() {
-			if (this.$route.name === 'FAVORITES') {
-				return 'icon-star'
-			} else if (this.$route.name === 'RECENT') {
-				return 'icon-recent'
-			} else if (this.$route.name === 'SEARCH') {
-				return 'icon-search'
-			}
-			return 'icon-podcast'
-		},
-		emptyContentDesc() {
-			if (this.$route.name === 'FAVORITES') {
-				return t('podcast', 'Stations you mark as favorite will show up here')
-			} else if (this.$route.name === 'RECENT') {
-				return t('podcast', 'Stations you recently played will show up here')
-			} else if (this.$route.name === 'SEARCH') {
-				return t('podcast', 'No stations were found matching your search term')
-			}
-			return t('podcast', 'No stations here')
-		},
-	},
-	watch: {
-		$route: 'onRoute',
-		'player.volume'(newVolume, oldVolume) {
-			if (audioPlayer !== null) {
-				audioPlayer.volume(newVolume)
-			}
-		},
-		'player.isPaused'(newState, oldState) {
-			if (newState === true && audioPlayer !== null) {
-				audioPlayer.pause()
-			} else if (newState === false && audioPlayer !== null) {
-				audioPlayer.play()
-			}
-		},
-	},
-	created() {
-		this.loadSettings()
-		this.loadFavorites()
-	},
-	mounted() {
-		this.onRoute()
-		this.scroll()
-	},
-	methods: {
-
-		onResize({ width, height }) {
-			const contentHeight = document.getElementById('app-content-vue').scrollHeight
-			const tableHeight = height
-			if (tableHeight < contentHeight) {
-				this.preFill()
-			}
-		},
-
-		preFill() {
-			const route = this.$route
-			this.loadStations(route.name)
-		},
-
-		async onRoute() {
-			this.tableData = []
-			this.pageLoading = true
-			const route = this.$route
-			this.loadStations(route.name)
-		},
-
-		/**
-			 * Favor a new station by sending the information to the server
-			 * @param {Object} station Station object
-			 */
-		async doFavor(station) {
-			if (this.favorites.flat().includes(station.stationuuid)) {
-				let stationid = null
-				try {
-					for (let i = 0, len = this.favorites.length; i < len; i++) {
-						if (station.stationuuid === this.favorites[i][1]) {
-							stationid = this.favorites[i][0]
-						}
-					}
-					axios.defaults.headers.requesttoken = requesttoken
-					await axios
-						.delete(generateUrl(`/apps/podcast/api/favorites/${stationid}`))
-						.then(response => {
-							this.favorites = this.favorites.filter(item => item[1] !== station.stationuuid)
-						})
-				} catch (error) {
-					showError(t('podcast', 'Could not remove station from favorites'))
-				}
-			} else {
-				try {
-					let stationSrc = ''
-					if (!station.url_resolved) {
-						stationSrc = station.urlresolved
-					} else {
-						stationSrc = station.url_resolved
-					}
-					const stationMap = {
-						id: -1,
-						name: station.name.toString(),
-						urlresolved: stationSrc.toString(),
-						favicon: station.favicon.toString(),
-						stationuuid: station.stationuuid.toString(),
-						bitrate: station.bitrate.toString(),
-						country: station.country.toString(),
-						language: station.language.toString(),
-						homepage: station.homepage.toString(),
-						codec: station.codec.toString(),
-						tags: station.tags.toString(),
-					}
-					axios.defaults.headers.requesttoken = requesttoken
-					await axios
-						.post(generateUrl('/apps/podcast/api/favorites'), stationMap)
-						.then(response => {
-							this.favorites.push([response.data.id, station.stationuuid])
-						})
-				} catch (error) {
-					showError(t('podcast', 'Could not favor station'))
-				}
-			}
-		},
-
-		/**
-			 * Start playing a podcast station and counting the playback
-			 * @param {Object} station Station object
-			 */
-		async doPlay(station) {
-			const vm = this
-
-			vm.$store.dispatch('isBuffering', true)
-
-			if (audioPlayer !== null) {
-				audioPlayer.fade(vm.player.volume, 0, 500)
-			}
-			vm.$store.dispatch('setTitle', station.name)
-
-			let stationSrc = ''
-			if (!station.url_resolved) {
-				stationSrc = station.urlresolved
-			} else {
-				stationSrc = station.url_resolved
-			}
-			Howler.unload()
-			audioPlayer = new Howl({
-				src: stationSrc,
-				html5: true,
-				volume: vm.player.volume,
-				onplay() {
-					vm.$store.dispatch('isPlaying', true)
-					vm.$store.dispatch('isBuffering', false)
-				},
-				onpause() {
-					vm.$store.dispatch('isPlaying', false)
-					vm.$store.dispatch('isBuffering', false)
-				},
-				onend() {
-					showError(t('podcast', 'Lost connection to podcast episode stream, retrying ...'))
-					vm.$store.dispatch('isPlaying', false)
-					vm.$store.dispatch('isBuffering', true)
-				},
-			})
-			audioPlayer.unload()
-			audioPlayer.play()
-			audioPlayer.fade(0, vm.player.volume, 500)
-
-			/* Count click */
-			try {
-				delete axios.defaults.headers.requesttoken
-				axios.get(this.$apiUrl + '/json/url/' + station.stationuuid)
-			} catch (error) {
-				showError(t('podcast', 'Unable to count play on remote API'))
-			}
-
-			/* Put into recent stations */
-			try {
-				let stationSrc = ''
-				if (!station.url_resolved) {
-					stationSrc = station.urlresolved
-				} else {
-					stationSrc = station.url_resolved
-				}
-				const stationMap = {
-					id: -1,
-					name: station.name.toString(),
-					urlresolved: stationSrc.toString(),
-					favicon: station.favicon.toString(),
-					stationuuid: station.stationuuid.toString(),
-					bitrate: station.bitrate.toString(),
-					country: station.country.toString(),
-					language: station.language.toString(),
-					homepage: station.homepage.toString(),
-					codec: station.codec.toString(),
-					tags: station.tags.toString(),
-				}
-				axios.defaults.headers.requesttoken = requesttoken
-				await axios
-					.post(generateUrl('/apps/podcast/api/recent'), stationMap)
-			} catch (error) {
-				showError(t('podcast', 'Could not add station to recent list'))
-			}
-
-		},
-
-		async loadStations(menuState = 'TOP') {
-
-			const vm = this
-			const queryBase = this.$apiUrl + '/json/stations'
-			let queryURI = queryBase
-			let sortBy = 'clickcount'
-
-			if (vm.$route.name === 'CATEGORIES') {
-				if (vm.$route.path === '/categories') {
-					vm.tableData = [
-						{
-							name: t('podcast', 'Countries'),
-							type: 'folder',
-							path: '/categories/countries',
-						},
-						{
-							name: t('podcast', 'States'),
-							type: 'folder',
-							path: '/categories/states',
-						},
-						{
-							name: t('podcast', 'Languages'),
-							type: 'folder',
-							path: '/categories/languages',
-						},
-						{
-							name: t('podcast', 'Tags'),
-							type: 'folder',
-							path: '/categories/tags',
-						},
-					]
-					vm.pageLoading = false
-					return true
-				} else if (vm.$route.params.category === 'tags') {
-					if (vm.$route.params.query) {
-						queryURI = this.$apiUrl + '/json/stations/search?tag=' + vm.$route.params.query + '&tagExact=true'
-					} else {
-						queryURI = this.$apiUrl + '/json/tags'
-					}
-				} else if (vm.$route.params.category === 'countries') {
-					if (vm.$route.params.query) {
-						queryURI = this.$apiUrl + '/json/stations/search?country=' + vm.$route.params.query + '&countryExact=true'
-					} else {
-						queryURI = this.$apiUrl + '/json/countries'
-					}
-				} else if (vm.$route.params.category === 'states') {
-					if (vm.$route.params.query) {
-						queryURI = this.$apiUrl + '/json/stations/search?state=' + vm.$route.params.query + '&stateExact=true'
-					} else {
-						queryURI = this.$apiUrl + '/json/states'
-					}
-				} else if (vm.$route.params.category === 'languages') {
-					if (vm.$route.params.query) {
-						queryURI = this.$apiUrl + '/json/stations/search?language=' + vm.$route.params.query + '&languageExact=true'
-					} else {
-						queryURI = this.$apiUrl + '/json/languages'
-					}
-				}
-			}
-
-			// Skip loading more stations on certain sites
-			if (vm.tableData.length > 0
-				&& (vm.$route.name === 'FAVORITES'
-				|| vm.$route.name === 'RECENT'
-				|| vm.$route.name === 'CATEGORIES')) {
-				return true
-			}
-
-			if (menuState === 'TOP') {
-				sortBy = 'clickcount'
-			} else if (menuState === 'NEW') {
-				sortBy = 'lastchangetime'
-			} else if (menuState === 'SEARCH') {
-				const searchQuery = vm.$route.params.query
-				queryURI = queryBase + '/byname/' + searchQuery
-			} else if (menuState === 'FAVORITES') {
-				queryURI = generateUrl('/apps/podcast/api/favorites')
-			} else if (menuState === 'RECENT') {
-				queryURI = generateUrl('/apps/podcast/api/recent')
-			}
-
-			if (menuState !== 'CATEGORIES') {
-				vm.queryParams = {
-					limit: 20,
-					order: sortBy,
-					reverse: true,
-					offset: vm.tableData.length,
-				}
-			} else {
-				vm.queryParams = {}
-			}
-
-			try {
-				if (menuState === 'FAVORITES' || menuState === 'RECENT') {
-					axios.defaults.headers.requesttoken = requesttoken
-				} else {
-					delete axios.defaults.headers.requesttoken
-				}
-				await axios.get(queryURI, {
-					params: vm.queryParams,
-				})
-					.then(function(response) {
-						for (let i = 0; i < response.data.length; i++) {
-							const obj = response.data[i]
-							if (!obj.stationuuid) {
-								response.data[i].type = 'folder'
-								response.data[i].path = vm.$route.path + '/' + obj.name
-							}
-						}
-						vm.tableData = vm.tableData.concat(response.data)
-						vm.pageLoading = false
-					})
-			} catch (error) {
-				showError(t('podcast', 'Could not fetch stations from remote API'))
-			}
-		},
-
-		/**
-		 * On scroll event, load more stations if bottom reached
-		 */
-		scroll() {
-			window.onscroll = () => {
-				if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
-					const route = this.$route
-					this.loadStations(route.name)
-		    }
-			}
-		},
-		loadSettings() {
-
-			// axios.defaults.headers.common = {
-			// 'User-Agent': 'Nextcloud Podcast App/' + this.$version,
-			// }
-			this.$store.dispatch('getVolumeState')
-
-		},
-
-		async loadFavorites() {
-			const vm = this
-			try {
-				axios.defaults.headers.requesttoken = requesttoken
-				await axios.get(generateUrl('/apps/podcast/api/favorites'))
-					.then(function(response) {
-						const favorites = []
-						for (let i = 0, len = response.data.length; i < len; i++) {
-							favorites.push([response.data[i].id, response.data[i].stationuuid])
-						}
-						vm.favorites = favorites
-					})
-			} catch (error) {
-				showError(t('podcast', 'Unable to load favorites'))
-			}
-		},
-	},
-}
-</script>
-
-<style>
-
-@media only screen and (min-width: 1024px) {
-	.app-navigation-toggle {
-		display: none;
-	}
-}
-
-</style>
diff --git a/src/router.js b/src/router.js
index e600e9692f3d2a01ccf6341c7a2f6f30019757a2..6d40e876e6b4149601546a3957dc02e22ea4c97e 100644
--- a/src/router.js
+++ b/src/router.js
@@ -24,11 +24,11 @@ import Vue from 'vue'
 import Router from 'vue-router'
 import { generateUrl } from '@nextcloud/router'
 
-import Main from './components/Main'
+import Library from './views/Library'
+import BrowseAll from './views/BrowseAll'
 import Show from './views/Show'
 import Episode from './views/Episode'
 import Browse from './views/Browse'
-import BrowseAll from './views/BrowseAll'
 import NotImplemented from './views/NotImplemented'
 
 Vue.use(Router)
@@ -48,7 +48,7 @@ const router = new Router({
 		},
 		{
 			path: '/library',
-			component: NotImplemented,
+			component: Library,
 			name: 'LIBRARY',
 		},
 		{
@@ -73,12 +73,6 @@ const router = new Router({
 			name: 'EPISODE',
 			props: {},
 		},
-		{
-			path: '/search/:query',
-			component: Main,
-			name: 'SEARCH',
-			props: {},
-		},
 	],
 })
 
diff --git a/src/services/ShowApi.js b/src/services/ShowApi.js
index 500918dc8e17d0c64c9b1b573930e0f6a0bd3ed4..e1b7cf601de58bfde33d1e6d1aadad924c5283aa 100644
--- a/src/services/ShowApi.js
+++ b/src/services/ShowApi.js
@@ -70,4 +70,20 @@ export class ShowApi {
 			})
 	}
 
+	queryLibrary(show) {
+		axios.defaults.headers.requesttoken = requesttoken
+		return axios.get(this.url('/shows'))
+			.then(
+				(response) => {
+					return Promise.resolve(response.data)
+				},
+				(err) => {
+					return Promise.reject(err)
+				}
+			)
+			.catch((err) => {
+				return Promise.reject(err)
+			})
+	}
+
 }
diff --git a/src/views/Library.vue b/src/views/Library.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e79ab90b519830e513976c0b058c72b113f0bf88
--- /dev/null
+++ b/src/views/Library.vue
@@ -0,0 +1,154 @@
+<!--
+  - @copyright Copyright (c) 2020 Jonas Heinrich
+  -
+  - @author Jonas Heinrich <onny@project-insanity.org>
+  -
+  - @license GNU AGPL version 3 or any later version
+  -
+  - This program is free software: you can redistribute it and/or modify
+  - it under the terms of the GNU Affero General Public License as
+  - published by the Free Software Foundation, either version 3 of the
+  - License, or (at your option) any later version.
+  -
+  - This program is distributed in the hope that it will be useful,
+  - but WITHOUT ANY WARRANTY; without even the implied warranty of
+  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  - GNU Affero General Public License for more details.
+  -
+  - You should have received a copy of the GNU Affero General Public License
+  - along with this program. If not, see <http://www.gnu.org/licenses/>.
+  -
+  -->
+<template>
+	<div class="mainContent">
+		<div class="podcastSection">
+			<div class="podcastSectionHeader">
+				<h1>Library</h1>
+			</div>
+			<div class="podcastSliderAll">
+				<div
+					v-for="(podcast, idx) in podcasts"
+					:key="idx"
+					class="podcastCard">
+					<router-link :to="{ path: `/browse/show/${podcast.id}`}">
+						<div
+							class="podcastImage"
+							:style="{ backgroundImage: `url(${podcast.smallImageURL})` }" />
+						<span class="title">
+							{{ podcast.title }}
+						</span>
+						<span class="subtitle">
+							{{ podcast.author }}
+						</span>
+					</router-link>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+import { ShowApi } from './../services/ShowApi'
+const showClient = new ShowApi()
+
+export default {
+	name: 'Library',
+	data: () => ({
+		podcasts: {},
+	}),
+	mounted() {
+		this.queryPodcasts()
+	},
+	methods: {
+
+		async queryPodcasts() {
+
+			const library = await showClient.queryLibrary()
+			this.podcasts = library
+
+		},
+
+	},
+}
+</script>
+
+<style lang="scss">
+.mainContent {
+	padding: 30px;
+}
+
+.podcastSection {
+	margin-bottom: 30px;
+}
+
+.podcastSectionHeader {
+	padding: 10px 0px;
+	z-index: 60;
+	position: sticky;
+	top: 50px;
+	background: white;
+	display: flex;
+	align-items: center;
+	margin-bottom: 10px;
+
+	h1 {
+		flex-grow: 1;
+		font-size: 1.6em;
+		margin-bottom: 0px;
+	}
+	a {
+		color: #1976d2;
+		cursor: pointer;
+	}
+}
+
+.podcastSliderAll {
+	width: 100%;
+	display: flex;
+	flex-wrap: wrap;
+}
+
+.podcastCard {
+	width: 170px;
+	height: 220px;
+	margin-right: 15px;
+	flex-shrink: 0;
+	background: rgba(241, 241, 241, 0.6);
+	border-radius: 3px;
+	padding: 15px;
+	transition: all 0.2s ease-in-out;
+
+	* {
+		cursor: pointer;
+	}
+
+	.podcastImage {
+		background-size: cover;
+		background-position: center;
+		box-shadow: 1px 1px 2px rgba(0,0,0,.5);
+		border: 1px solid rgba(0,0,0,.5);
+		border-radius: 5px;
+		width: 140px;
+		height: 140px;
+		margin-bottom: 5px;
+	}
+
+	span {
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+		display: block;
+	}
+	span.title {
+		font-size: 1em;
+	}
+	span.subtitle {
+		font-size: 0.9em;
+		color: #b5b1b1;
+	}
+}
+
+.podcastCard:hover {
+	background: rgb(236, 236, 236);
+}
+</style>
diff --git a/src/views/Show.vue b/src/views/Show.vue
index 5d931bc4a6d8fa97732b798f6f04c8c388f32587..78f25835fb23c57628a78d9f6c30bc54e26aa338 100644
--- a/src/views/Show.vue
+++ b/src/views/Show.vue
@@ -73,11 +73,9 @@ export default {
 		page: 0,
 		podcastId: null,
 		issubscribed: false,
+		loading: true,
 	}),
 	computed: {
-		loading() {
-			return this.episodes.length === 0 // FIXME: also consider podcast object
-		},
 		player() {
 			return this.$store.state.player
 		},
@@ -101,14 +99,14 @@ export default {
 		},
 	},
 	mounted() {
-		this.init()
+		this.initPage()
 	},
 	destroyed() {
 	  window.removeEventListener('scroll', this.handleScroll)
 	},
 	methods: {
 
-		init() {
+		initPage() {
 			this.podcast = {}
 			this.episodes = []
 			this.page = 0
@@ -169,6 +167,7 @@ export default {
 		processPodcast(podcast) {
 			this.podcast = podcast
 			document.title = podcast.title + ' - Podcast - Nextcloud'
+			this.loading = false
 		},
 
 		/**