diff --git a/front/src/components/audio/ChannelEntries.vue b/front/src/components/audio/ChannelEntries.vue
index 84e097a554d11ed6da8aa802847627a16e9f8f07..1e176ac36cfccee80bd6301ad4f7490925d65d01 100644
--- a/front/src/components/audio/ChannelEntries.vue
+++ b/front/src/components/audio/ChannelEntries.vue
@@ -5,8 +5,22 @@
     <div v-if="isLoading" class="ui inverted active dimmer">
       <div class="ui loader"></div>
     </div>
+    <podcast-table
+      v-if="isPodcast"
+      :is-podcast="isPodcast"
+      :show-art="true"
+      :show-position="false"
+      :tracks="objects"
+      :show-artist="false"
+      :show-album="false"
+      :paginate-results="true"
+      :total="count"
+      @page-changed="updatePage"
+      :page="page"
+      :paginate-by="limit"></podcast-table>
     <track-table
-      :is-podcast="true"
+      v-else
+      :is-podcast="isPodcast"
       :show-art="true"
       :show-position="false"
       :tracks="objects"
@@ -30,6 +44,7 @@
 <script>
 import _ from '@/lodash'
 import axios from 'axios'
+import PodcastTable from '@/components/audio/podcast/Table'
 import TrackTable from '@/components/audio/track/Table'
 
 export default {
@@ -37,9 +52,11 @@ export default {
     filters: {type: Object, required: true},
     limit: {type: Number, default: 10},
     defaultCover: {type: Object},
+    isPodcast: {type: Boolean, required: true},
   },
   components: {
-    TrackTable
+    PodcastTable,
+    TrackTable,
   },
   data () {
     return {
diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue
index 5ecdd8e5846ff6b9296f71f75e4c6c13f63e1e4a..dd0e651324a4f1d46200bc81f738f94c3408861a 100644
--- a/front/src/components/audio/PlayButton.vue
+++ b/front/src/components/audio/PlayButton.vue
@@ -33,7 +33,9 @@
           <translate translate-context="Sidebar/Player/Icon.Tooltip/Verb">Add to playlist…</translate>
         </button>
         <button v-if="track" class="item basic" @click.stop.prevent="$router.push(`/library/tracks/${track.id}/`)">
-          <i class="info icon"></i><translate translate-context="*/Queue/Dropdown/Button/Label/Short">Track details</translate>
+          <i class="info icon"></i>
+          <translate v-if="track.artist.content_category === 'podcast'" translate-context="*/Queue/Dropdown/Button/Label/Short">Episode details</translate>
+          <translate v-else translate-context="*/Queue/Dropdown/Button/Label/Short">Track details</translate>
         </button>
         <div class="divider"></div>
         <button v-if="filterableArtist" ref="filterArtist" data-ref="filterArtist" class="item basic" :disabled="!filterableArtist" @click.stop.prevent="filterArtist" :title="labels.hideArtist">
diff --git a/front/src/components/audio/podcast/MobileRow.vue b/front/src/components/audio/podcast/MobileRow.vue
new file mode 100644
index 0000000000000000000000000000000000000000..9e7236e7af14addd4aab55106e70b185bad0a6e4
--- /dev/null
+++ b/front/src/components/audio/podcast/MobileRow.vue
@@ -0,0 +1,195 @@
+<template>
+  <div
+    :class="[
+      { active: currentTrack && track.id === currentTrack.id },
+      'track-row row mobile',
+    ]"
+  >
+    <div
+      v-if="showArt"
+      @click.prevent.exact="activateTrack(track, index)"
+      class="image left floated column"
+    >
+      <img
+        alt=""
+        class="ui artist-track mini image"
+        v-if="
+          track.album && track.album.cover && track.album.cover.urls.original
+        "
+        v-lazy="
+          $store.getters['instance/absoluteUrl'](
+            track.album.cover.urls.medium_square_crop
+          )
+        "
+      />
+      <img
+        alt=""
+        class="ui artist-track mini image"
+        v-else-if="
+          track.cover
+        "
+        v-lazy="
+          $store.getters['instance/absoluteUrl'](
+            track.cover.urls.medium_square_crop
+          )
+        "
+      />
+      <img
+        alt=""
+        class="ui artist-track mini image"
+        v-else-if="
+          track.artist.cover
+        "
+        v-lazy="
+          $store.getters['instance/absoluteUrl'](
+            track.artist.cover.urls.medium_square_crop
+          )
+        "
+      />
+      <img
+        alt=""
+        class="ui artist-track mini image"
+        v-else
+        src="../../../assets/audio/default-cover.png"
+      />
+    </div>
+    <div
+      tabindex=0
+      @click="activateTrack(track, index)"
+      role="button"
+      class="content ellipsis left floated column"
+    >
+      <p
+        :class="[
+          'track-title',
+          'mobile',
+          { 'play-indicator': isPlaying && currentTrack && track.id === currentTrack.id },
+        ]"
+      >
+        {{ track.title }}
+      </p>
+      <p v-if="track.artist.content_category === 'podcast'" class="track-meta mobile">
+        <human-date class="really discrete" :date="track.creation_date"></human-date>
+        <span>&#183;</span>
+        <human-duration
+          v-if="track.uploads[0] && track.uploads[0].duration"
+          :duration="track.uploads[0].duration"
+        ></human-duration>
+      </p>
+      <p v-else class="track-meta mobile">
+        {{ track.artist.name }} <span>&#183;</span>
+        <human-duration
+          v-if="track.uploads[0] && track.uploads[0].duration"
+          :duration="track.uploads[0].duration"
+        ></human-duration>
+      </p>
+    </div>
+    <div
+      v-if="$store.state.auth.authenticated && this.track.artist.content_category !== 'podcast'"
+      :class="[
+        'meta',
+        'right',
+        'floated',
+        'column',
+        'mobile',
+        { 'with-art': showArt },
+      ]"
+      role="button"
+    >
+      <track-favorite-icon
+        class="tiny"
+        :border="false"
+        :track="track"
+      ></track-favorite-icon>
+    </div>
+    <div
+      role="button"
+      :aria-label="actionsButtonLabel"
+      @click.prevent.exact="showTrackModal = !showTrackModal"
+      :class="[
+        'modal-button',
+        'right',
+        'floated',
+        'column',
+        'mobile',
+        { 'with-art': showArt },
+      ]"
+    >
+      <i class="ellipsis large vertical icon" />
+    </div>
+    <track-modal
+      @update:show="showTrackModal = $event;"
+      :show="showTrackModal"
+      :track="track"
+      :index="index"
+      :is-artist="isArtist"
+      :is-album="isAlbum"
+    ></track-modal>
+  </div>
+</template>
+
+<script>
+import PlayIndicator from "@/components/audio/track/PlayIndicator";
+import { mapActions, mapGetters } from "vuex";
+import TrackFavoriteIcon from "@/components/favorites/TrackFavoriteIcon";
+import TrackModal from "@/components/audio/track/Modal";
+import PlayOptionsMixin from "@/components/mixins/PlayOptions"
+
+export default {
+  mixins: [PlayOptionsMixin],
+  data() {
+    return {
+      showTrackModal: false,
+    }
+  },
+  props: {
+    tracks: Array,
+    showAlbum: { type: Boolean, required: false, default: true },
+    showArtist: { type: Boolean, required: false, default: true },
+    showPosition: { type: Boolean, required: false, default: false },
+    showArt: { type: Boolean, required: false, default: true },
+    search: { type: Boolean, required: false, default: false },
+    filters: { type: Object, required: false, default: null },
+    nextUrl: { type: String, required: false, default: null },
+    displayActions: { type: Boolean, required: false, default: true },
+    showDuration: { type: Boolean, required: false, default: true },
+    index: { type: Number, required: true },
+    track: { type: Object, required: true },
+    isArtist: {type: Boolean, required: false, default: false},
+    isAlbum: {type: Boolean, required: false, default: false},
+  },
+
+  components: {
+    PlayIndicator,
+    TrackFavoriteIcon,
+    TrackModal,
+  },
+  computed: {
+    ...mapGetters({
+      currentTrack: "queue/currentTrack",
+    }),
+
+    isPlaying() {
+      return this.$store.state.player.playing;
+    },
+    actionsButtonLabel () {
+        return this.$pgettext('Content/Track/Icon.Tooltip/Verb', 'Show track actions')
+    },
+  },
+
+  methods: {
+    prettyPosition(position, size) {
+      var s = String(position);
+      while (s.length < (size || 2)) {
+        s = "0" + s;
+      }
+      return s;
+    },
+
+    ...mapActions({
+      resumePlayback: "player/resumePlayback",
+      pausePlayback: "player/pausePlayback",
+    }),
+  },
+};
+</script>
diff --git a/front/src/components/audio/podcast/Modal.vue b/front/src/components/audio/podcast/Modal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..dae3275ccc11a6527c42211ecdbe3d7a1605b570
--- /dev/null
+++ b/front/src/components/audio/podcast/Modal.vue
@@ -0,0 +1,304 @@
+<template>
+  <modal
+    @update:show="$emit('update:show', $event)"
+    :show="show"
+    :scrolling="true"
+    :additionalClasses="['scrolling-track-options']"
+  >
+    <div class="header">
+      <div class="ui large centered rounded image">
+        <img
+          alt=""
+          class="ui centered image"
+          v-if="
+            track.album && track.album.cover && track.album.cover.urls.original
+          "
+          v-lazy="
+            $store.getters['instance/absoluteUrl'](
+              track.album.cover.urls.medium_square_crop
+            )
+          "
+        />
+        <img
+          alt=""
+          class="ui centered image"
+          v-else-if="track.cover"
+          v-lazy="
+            $store.getters['instance/absoluteUrl'](
+              track.cover.urls.medium_square_crop
+            )
+          "
+        />
+        <img
+          alt=""
+          class="ui centered image"
+          v-else-if="track.artist.cover"
+          v-lazy="
+            $store.getters['instance/absoluteUrl'](
+              track.artist.cover.urls.medium_square_crop
+            )
+          "
+        />
+        <img
+          alt=""
+          class="ui centered image"
+          v-else
+          src="../../../assets/audio/default-cover.png"
+        />
+      </div>
+      <h3 class="track-modal-title">{{ track.title }}</h3>
+      <h4 class="track-modal-subtitle">{{ track.artist.name }}</h4>
+    </div>
+    <div class="ui hidden divider"></div>
+    <div class="content">
+      <div class="ui one column unstackable grid">
+        <div 
+          class="row"
+          v-if="$store.state.auth.authenticated && this.track.artist.content_category !== 'podcast'">
+          <div
+            tabindex="0"
+            class="column"
+            role="button"
+            :aria-label="favoriteButton"
+            @click.stop="$store.dispatch('favorites/toggle', track.id)"
+          >
+            <i
+              :class="[
+                'heart',
+                'favorite-icon',
+                { favorited: isFavorite },
+                { pink: isFavorite },
+                'icon',
+                'track-modal',
+                'list-icon',
+              ]"
+            />
+            <span class="track-modal list-item">{{ favoriteButton }}</span>
+          </div>
+        </div>
+        <div class="row">
+          <div
+            class="column"
+            role="button"
+            @click.stop.prevent="
+              add();
+              closeModal();
+            "
+            :aria-label="labels.addToQueue"
+          >
+            <i class="plus icon track-modal list-icon" />
+            <span class="track-modal list-item">{{ labels.addToQueue }}</span>
+          </div>
+        </div>
+        <div class="row">
+          <div
+            class="column"
+            role="button"
+            @click.stop.prevent="
+              addNext(true);
+              closeModal();
+            "
+            :aria-label="labels.playNext"
+          >
+            <i class="step forward icon track-modal list-icon" />
+            <span class="track-modal list-item">{{ labels.playNext }}</span>
+          </div>
+        </div>
+        <div class="row">
+          <div
+            class="column"
+            role="button"
+            @click.stop.prevent="
+              $store.dispatch('radios/start', {
+                type: 'similar',
+                objectId: track.id,
+              });
+              closeModal();
+            "
+            :aria-label="labels.startRadio"
+          >
+            <i class="rss icon track-modal list-icon" />
+            <span class="track-modal list-item">{{ labels.startRadio }}</span>
+          </div>
+        </div>
+        <div class="row">
+          <div
+            class="column"
+            role="button"
+            @click.stop="$store.commit('playlists/chooseTrack', track)"
+            :aria-label="labels.addToPlaylist"
+          >
+            <i class="list icon track-modal list-icon" />
+            <span class="track-modal list-item">{{
+              labels.addToPlaylist
+            }}</span>
+          </div>
+        </div>
+        <div class="ui divider"></div>
+        <div v-if="!isAlbum && track.album" class="row">
+          <div
+            class="column"
+            role="button"
+            :aria-label="albumDetailsButton"
+            @click.prevent.exact="
+              $router.push({
+                name: 'library.albums.detail',
+                params: { id: track.album.id },
+              })
+            "
+          >
+            <i class="compact disc icon track-modal list-icon" />
+            <span class="track-modal list-item">{{
+              albumDetailsButton
+            }}</span>
+          </div>
+        </div>
+        <div v-if="!isArtist" class="row">
+          <div
+            class="column"
+            role="button"
+            :aria-label="artistDetailsButton"
+            @click.prevent.exact="
+              $router.push({
+                name: 'library.artists.detail',
+                params: { id: track.artist.id },
+              })
+            "
+          >
+            <i class="user icon track-modal list-icon" />
+            <span class="track-modal list-item">{{
+              artistDetailsButton
+            }}</span>
+          </div>
+        </div>
+        <div class="row">
+          <div
+            class="column"
+            role="button"
+            :aria-label="trackDetailsButton"
+            @click.prevent.exact="
+              $router.push({
+                name: 'library.tracks.detail',
+                params: { id: track.id },
+              })
+            "
+          >
+            <i class="info icon track-modal list-icon" />
+            <span class="track-modal list-item">{{
+              trackDetailsButton
+            }}</span>
+          </div>
+        </div>
+        <div class="ui divider"></div>
+        <div
+          v-for="obj in getReportableObjs({
+            track,
+            album,
+            artist,
+          })"
+          :key="obj.target.type + obj.target.id"
+          class="row"
+          :ref="`report${obj.target.type}${obj.target.id}`"
+          :data-ref="`report${obj.target.type}${obj.target.id}`"
+          @click.stop.prevent="$store.dispatch('moderation/report', obj.target)"
+        >
+          <div class="column">
+            <i class="share icon track-modal list-icon" /><span
+              class="track-modal list-item"
+              >{{ obj.label }}</span
+            >
+          </div>
+        </div>
+      </div>
+    </div>
+  </modal>
+</template>
+
+<script>
+import Modal from "@/components/semantic/Modal";
+import TrackFavoriteIcon from "@/components/favorites/TrackFavoriteIcon";
+import ReportMixin from '@/components/mixins/Report'
+import PlayOptionsMixin from '@/components/mixins/PlayOptions'
+
+export default {
+  mixins: [ReportMixin, PlayOptionsMixin],
+  props: {
+    show: { type: Boolean, required: true, default: false },
+    track: { type: Object, required: true },
+    index: { type: Number, required: true },
+    isArtist: { type: Boolean, required: false, default: false },
+    isAlbum: { type: Boolean, required: false, default: false },
+  },
+  components: {
+    Modal,
+    TrackFavoriteIcon,
+  },
+  data() {
+    return {
+      isShowing: this.show,
+      tracks: [this.track],
+      album: this.track.album,
+      artist: this.track.artist,
+    };
+  },
+  computed: {
+    isFavorite() {
+      return this.$store.getters["favorites/isFavorite"](this.track.id);
+    },
+    favoriteButton() {
+      if (this.isFavorite) {
+        return this.$pgettext(
+          "Content/Track/Icon.Tooltip/Verb",
+          "Remove from favorites"
+        );
+      } else {
+        return this.$pgettext("Content/Track/*/Verb", "Add to favorites");
+      }
+    },
+    trackDetailsButton() {
+      if (this.track.artist.content_category === 'podcast') {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "Episode details")
+      } else {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "Track details")
+      }
+    },
+    albumDetailsButton() {
+      if (this.track.artist.content_category === 'podcast') {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "View series")
+      } else {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "View album")
+      }
+    },
+    artistDetailsButton() {
+      if (this.track.artist.content_category === 'podcast') {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "View channel")
+      } else {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "View artist")
+      }
+    },
+    labels() {
+      return {
+        startRadio: this.$pgettext(
+          "*/Queue/Dropdown/Button/Title",
+          "Play radio"
+        ),
+        playNow: this.$pgettext("*/Queue/Dropdown/Button/Title", "Play now"),
+        addToQueue: this.$pgettext(
+          "*/Queue/Dropdown/Button/Title",
+          "Add to queue"
+        ),
+        playNext: this.$pgettext("*/Queue/Dropdown/Button/Title", "Play next"),
+        addToPlaylist: this.$pgettext(
+          "Sidebar/Player/Icon.Tooltip/Verb",
+          "Add to playlist…"
+        ),
+      };
+    },
+  },
+  methods: {
+    closeModal() {
+      this.$emit("update:show", false);
+    },
+  },
+};
+</script>
diff --git a/front/src/components/audio/podcast/Row.vue b/front/src/components/audio/podcast/Row.vue
new file mode 100644
index 0000000000000000000000000000000000000000..3e6155b67ea56e26f9f913674331547e0ea6af85
--- /dev/null
+++ b/front/src/components/audio/podcast/Row.vue
@@ -0,0 +1,141 @@
+<template>
+  <div
+    :class="[
+      { active: currentTrack && track.id === currentTrack.id },
+      'track-row podcast row',
+    ]"
+    @mouseover="hover = track.id"
+    @mouseleave="hover = null"
+    @dblclick="activateTrack(track, index)"
+  >
+    <div
+      v-if="showArt"
+      class="image left floated column"
+      role="button"
+      @click.prevent.exact="activateTrack(track, index)"
+    >
+      <img
+        alt=""
+        class="ui artist-track mini image"
+        v-if="
+          track.album && track.album.cover && track.album.cover.urls.original
+        "
+        v-lazy="
+          $store.getters['instance/absoluteUrl'](
+            track.album.cover.urls.medium_square_crop
+          )
+        "
+      />
+      <img
+        alt=""
+        class="ui artist-track mini image"
+        v-else-if="
+          track.cover && track.cover.urls.original
+        "
+        v-lazy="
+          $store.getters['instance/absoluteUrl'](
+            track.cover.urls.medium_square_crop
+          )
+        "
+      />
+      <img
+        alt=""
+        class="ui artist-track mini image"
+        v-else-if="
+          track.artist && track.artist.cover && track.album.cover.urls.original
+        "
+        v-lazy="
+          $store.getters['instance/absoluteUrl'](
+            track.cover.urls.medium_square_crop
+          )
+        "
+      />
+      <img
+        alt=""
+        class="ui artist-track mini image"
+        v-else
+        src="../../../assets/audio/default-cover.png"
+      />
+    </div>
+    <div tabindex=0 class="content left floated column">
+      <p class="podcast-episode-title ellipsis">{{ track.title }}</p>
+      <p class="podcast-episode-meta">
+      An episode description, with all its twists and turns!
+      This episode focuses on something I'm sure, but nobody really knows what it's focusing on.</p>
+    </div>
+    <div v-if="displayActions" class="meta right floated column">
+      <play-button
+        id="playmenu"
+        class="play-button basic icon"
+        :dropdown-only="true"
+        :is-playable="track.is_playable"
+        :dropdown-icon-classes="[
+          'ellipsis',
+          'vertical',
+          'large really discrete',
+        ]"
+        :track="track"
+      ></play-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import PlayIndicator from "@/components/audio/track/PlayIndicator";
+import { mapActions, mapGetters } from "vuex";
+import PlayButton from "@/components/audio/PlayButton";
+
+export default {
+  props: {
+    tracks: Array,
+    showAlbum: { type: Boolean, required: false, default: true },
+    showArtist: { type: Boolean, required: false, default: true },
+    showPosition: { type: Boolean, required: false, default: false },
+    showArt: { type: Boolean, required: false, default: true },
+    search: { type: Boolean, required: false, default: false },
+    filters: { type: Object, required: false, default: null },
+    nextUrl: { type: String, required: false, default: null },
+    displayActions: { type: Boolean, required: false, default: true },
+    showDuration: { type: Boolean, required: false, default: true },
+    index: { type: Number, required: true },
+    track: { type: Object, required: true },
+  },
+
+  data() {
+    return {
+      hover: null,
+    }
+  },
+
+  components: {
+    PlayIndicator,
+    PlayButton,
+  },
+
+  computed: {
+    ...mapGetters({
+      currentTrack: "queue/currentTrack",
+    }),
+
+    isPlaying() {
+      return this.$store.state.player.playing;
+    },
+  },
+
+  methods: {
+
+    prettyPosition(position, size) {
+      var s = String(position);
+      while (s.length < (size || 2)) {
+        s = "0" + s;
+      }
+      return s;
+    },
+
+    ...mapActions({
+      resumePlayback: "player/resumePlayback",
+      pausePlayback: "player/pausePlayback",
+    }),
+  },
+};
+</script>
diff --git a/front/src/components/audio/podcast/Table.vue b/front/src/components/audio/podcast/Table.vue
new file mode 100644
index 0000000000000000000000000000000000000000..134a2432364103e6c5560e89f51e595723d6309b
--- /dev/null
+++ b/front/src/components/audio/podcast/Table.vue
@@ -0,0 +1,125 @@
+<template>
+  <div>
+    <div class="ui hidden divider"></div>
+
+    <!-- Add a header if needed -->
+
+    <slot name="header"></slot>
+
+    <div>
+      <div
+        :class="['track-table', 'ui', 'unstackable', 'grid', 'tablet-and-up']"
+      >
+        <!-- For each item, build a row -->
+        <podcast-row
+          v-for="(track, index) in tracks"
+          :track="track"
+          :key="track.id"
+          :index="index"
+          :tracks="tracks"
+          :display-actions="displayActions"
+          :show-duration="showDuration"
+          :is-podcast="isPodcast"
+        ></podcast-row>
+      </div>
+      <div v-if="paginateResults" class="ui center aligned basic segment desktop-and-up">
+        <pagination
+          :total="total"
+          :current="page"
+          :paginate-by="paginateBy"
+          v-on="$listeners">
+        </pagination>
+      </div>
+    </div>
+
+    <div
+      :class="['track-table', 'ui', 'unstackable', 'grid', 'tablet-and-below']"
+    >
+      <div v-if="isLoading" class="ui inverted active dimmer">
+        <div class="ui loader"></div>
+      </div>
+
+      <!-- For each item, build a row -->
+
+      <track-mobile-row
+        v-for="(track, index) in allTracks"
+        :track="track"
+        :key="track.id"
+        :index="index"
+        :tracks="allTracks"
+        :show-position="showPosition"
+        :show-art="showArt"
+        :show-duration="showDuration"
+        :is-artist="isArtist"
+        :is-album="isAlbum"
+        :is-podcast="isPodcast"
+      ></track-mobile-row>
+      <div v-if="paginateResults" class="ui center aligned basic segment tablet-and-below">
+        <pagination
+          v-if="paginateResults"
+          :total="total"
+          :current="page"
+          :compact="true"
+          v-on="$listeners"></pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import _ from "@/lodash";
+import TrackRow from "@/components/audio/track/Row";
+import PodcastRow from "@/components/audio/podcast/Row";
+import TrackMobileRow from "@/components/audio/track/MobileRow";
+import Pagination from "@/components/Pagination";
+
+export default {
+  components: {
+    TrackRow,
+    TrackMobileRow,
+    Pagination,
+    PodcastRow,
+  },
+
+  props: {
+    tracks: Array,
+    showAlbum: { type: Boolean, required: false, default: true },
+    showArtist: { type: Boolean, required: false, default: true },
+    showPosition: { type: Boolean, required: false, default: false },
+    showArt: { type: Boolean, required: false, default: true },
+    search: { type: Boolean, required: false, default: false },
+    filters: { type: Object, required: false, default: null },
+    nextUrl: { type: String, required: false, default: null },
+    displayActions: { type: Boolean, required: false, default: true },
+    showDuration: { type: Boolean, required: false, default: true },
+    isArtist: { type: Boolean, required: false, default: false },
+    isAlbum: { type: Boolean, required: false, default: false },
+    paginateResults: { type: Boolean, required: false, default: true},
+    total: { type: Number, required: false},
+    page: {type: Number, required: false, default: 1},
+    paginateBy: {type: Number, required: false, default: 25},
+    isPodcast: {type: Boolean, required: true},
+  },
+
+  data() {
+    return {
+      isLoading: false,
+    };
+  },
+
+  computed: {
+    labels() {
+      return {
+        title: this.$pgettext("*/*/*/Noun", "Title"),
+        album: this.$pgettext("*/*/*/Noun", "Album"),
+        artist: this.$pgettext("*/*/*/Noun", "Artist"),
+      };
+    },
+  },
+  methods: {
+    updatePage: function(page) {
+      this.$emit('page-changed', page)
+    }
+  },
+};
+</script>
diff --git a/front/src/components/audio/track/MobileRow.vue b/front/src/components/audio/track/MobileRow.vue
index 1d24cbda7ee4f3594b98a04447a30a73603d1760..da2b1f3d28033397692f3f7d9da6307d64a5b3ee 100644
--- a/front/src/components/audio/track/MobileRow.vue
+++ b/front/src/components/audio/track/MobileRow.vue
@@ -5,36 +5,6 @@
       'track-row row mobile',
     ]"
   >
-    <!--
-    <div 
-      class="actions one wide left floated column"
-      role="button"
-      @click.prevent.exact="activateTrack(track, index)"
-      v-if="!showArt">
-      <play-indicator
-        v-if="
-          !$store.state.player.isLoadingAudio &&
-          currentTrack &&
-          isPlaying &&
-          track.id === currentTrack.id
-        "
-      >
-      </play-indicator>
-      <button
-        v-else-if="
-          currentTrack &&
-          !isPlaying &&
-          track.id === currentTrack.id
-        "
-        class="ui really tiny basic icon button play-button paused"
-      >
-        <i class="pause icon" />
-      </button>
-      <span class="track-position" v-else-if="showPosition">
-        {{ prettyPosition(track.position) }}
-      </span>
-    </div>
-        -->
     <div
       v-if="showArt"
       @click.prevent.exact="activateTrack(track, index)"
@@ -126,7 +96,7 @@
     </div>
     <div
       role="button"
-      aria-label="Show Details"
+      :aria-label="actionsButtonLabel"
       @click.prevent.exact="showTrackModal = !showTrackModal"
       :class="[
         'modal-button',
@@ -168,7 +138,6 @@ export default {
     tracks: Array,
     showAlbum: { type: Boolean, required: false, default: true },
     showArtist: { type: Boolean, required: false, default: true },
-    trackOnly: { type: Boolean, required: false, default: false },
     showPosition: { type: Boolean, required: false, default: false },
     showArt: { type: Boolean, required: false, default: true },
     search: { type: Boolean, required: false, default: false },
@@ -195,6 +164,9 @@ export default {
     isPlaying() {
       return this.$store.state.player.playing;
     },
+    actionsButtonLabel () {
+        return this.$pgettext('Content/Track/Icon.Tooltip/Verb', 'Show track actions')
+    },
   },
 
   methods: {
diff --git a/front/src/components/audio/track/Modal.vue b/front/src/components/audio/track/Modal.vue
index 1c3e17440cd15ac65f6dd77174be16abdd466080..dae3275ccc11a6527c42211ecdbe3d7a1605b570 100644
--- a/front/src/components/audio/track/Modal.vue
+++ b/front/src/components/audio/track/Modal.vue
@@ -52,11 +52,14 @@
     <div class="ui hidden divider"></div>
     <div class="content">
       <div class="ui one column unstackable grid">
-        <div class="row">
+        <div 
+          class="row"
+          v-if="$store.state.auth.authenticated && this.track.artist.content_category !== 'podcast'">
           <div
             tabindex="0"
             class="column"
             role="button"
+            :aria-label="favoriteButton"
             @click.stop="$store.dispatch('favorites/toggle', track.id)"
           >
             <i
@@ -132,11 +135,11 @@
           </div>
         </div>
         <div class="ui divider"></div>
-        <div v-if="!isAlbum" class="row">
+        <div v-if="!isAlbum && track.album" class="row">
           <div
             class="column"
             role="button"
-            :aria-label="labels.albumDetailsButton"
+            :aria-label="albumDetailsButton"
             @click.prevent.exact="
               $router.push({
                 name: 'library.albums.detail',
@@ -146,7 +149,7 @@
           >
             <i class="compact disc icon track-modal list-icon" />
             <span class="track-modal list-item">{{
-              labels.albumDetailsButton
+              albumDetailsButton
             }}</span>
           </div>
         </div>
@@ -154,7 +157,7 @@
           <div
             class="column"
             role="button"
-            :aria-label="labels.artistDetailsButton"
+            :aria-label="artistDetailsButton"
             @click.prevent.exact="
               $router.push({
                 name: 'library.artists.detail',
@@ -164,7 +167,7 @@
           >
             <i class="user icon track-modal list-icon" />
             <span class="track-modal list-item">{{
-              labels.artistDetailsButton
+              artistDetailsButton
             }}</span>
           </div>
         </div>
@@ -172,7 +175,7 @@
           <div
             class="column"
             role="button"
-            :aria-label="labels.trackDetailsButton"
+            :aria-label="trackDetailsButton"
             @click.prevent.exact="
               $router.push({
                 name: 'library.tracks.detail',
@@ -182,7 +185,7 @@
           >
             <i class="info icon track-modal list-icon" />
             <span class="track-modal list-item">{{
-              labels.trackDetailsButton
+              trackDetailsButton
             }}</span>
           </div>
         </div>
@@ -252,20 +255,29 @@ export default {
         return this.$pgettext("Content/Track/*/Verb", "Add to favorites");
       }
     },
+    trackDetailsButton() {
+      if (this.track.artist.content_category === 'podcast') {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "Episode details")
+      } else {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "Track details")
+      }
+    },
+    albumDetailsButton() {
+      if (this.track.artist.content_category === 'podcast') {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "View series")
+      } else {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "View album")
+      }
+    },
+    artistDetailsButton() {
+      if (this.track.artist.content_category === 'podcast') {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "View channel")
+      } else {
+        return this.$pgettext("*/Queue/Dropdown/Button/Label/Short", "View artist")
+      }
+    },
     labels() {
       return {
-        trackDetailsButton: this.$pgettext(
-          "*/Queue/Dropdown/Button/Label/Short",
-          "Track details"
-        ),
-        albumDetailsButton: this.$pgettext(
-          "*/Queue/Dropdown/Button/Label/Short",
-          "View album"
-        ),
-        artistDetailsButton: this.$pgettext(
-          "*/Queue/Dropdown/Button/Label/Short",
-          "View artist"
-        ),
         startRadio: this.$pgettext(
           "*/Queue/Dropdown/Button/Title",
           "Play radio"
@@ -280,10 +292,6 @@ export default {
           "Sidebar/Player/Icon.Tooltip/Verb",
           "Add to playlist…"
         ),
-        addToQueue: this.$pgettext(
-          "*/Queue/Dropdown/Button/Title",
-          "Add to queue"
-        ),
       };
     },
   },
diff --git a/front/src/components/audio/track/Row.vue b/front/src/components/audio/track/Row.vue
index 87d2724bd040a80ea3aec567f6da7dd328fb93fe..2dbae9435fcd582efc9e1edef9299f33836dc152 100644
--- a/front/src/components/audio/track/Row.vue
+++ b/front/src/components/audio/track/Row.vue
@@ -106,20 +106,10 @@
     </div>
     <div tabindex=0 class="content ellipsis left floated column">
       <a
-        v-if="currentTrack && !isPlaying && track.id === currentTrack.id"
-        @click="resumePlayback"
+        @click="activateTrack(track, index)"
       >
         {{ track.title }}
       </a>
-      <a
-        v-else-if="currentTrack && isPlaying && track.id === currentTrack.id"
-        @click="pausePlayback"
-      >
-        {{ track.title }}
-      </a>
-      <a v-else @click.prevent.exact="replacePlay(tracks, index)">
-        {{ track.title }}
-      </a>
     </div>
     <div v-if="showAlbum" class="content ellipsis left floated column">
       <router-link
@@ -175,13 +165,14 @@ import PlayIndicator from "@/components/audio/track/PlayIndicator";
 import { mapActions, mapGetters } from "vuex";
 import TrackFavoriteIcon from "@/components/favorites/TrackFavoriteIcon";
 import PlayButton from "@/components/audio/PlayButton";
+import PlayOptions from "@/components/mixins/PlayOptions"
 
 export default {
+  mixins: [PlayOptions],
   props: {
     tracks: Array,
     showAlbum: { type: Boolean, required: false, default: true },
     showArtist: { type: Boolean, required: false, default: true },
-    trackOnly: { type: Boolean, required: false, default: false },
     showPosition: { type: Boolean, required: false, default: false },
     showArt: { type: Boolean, required: false, default: true },
     search: { type: Boolean, required: false, default: false },
@@ -216,12 +207,6 @@ export default {
   },
 
   methods: {
-    replacePlay(tracks, trackIndex) {
-      this.$store.dispatch("queue/clean");
-      this.$store.dispatch("queue/appendMany", { tracks: tracks }).then(() => {
-        this.$store.dispatch("queue/currentIndex", trackIndex);
-      });
-    },
 
     prettyPosition(position, size) {
       var s = String(position);
@@ -230,25 +215,7 @@ export default {
       }
       return s;
     },
-
-    activateTrack(track, index) {
-      if (
-        this.currentTrack &&
-        this.isPlaying &&
-        track.id === this.currentTrack.id
-      ) {
-        this.pausePlayback();
-      } else if (
-        this.currentTrack &&
-        !this.isPlaying &&
-        track.id === this.currentTrack.id
-      ) {
-        this.resumePlayback();
-      } else {
-        this.replacePlay(this.tracks, index);
-      }
-    },
-
+    
     ...mapActions({
       resumePlayback: "player/resumePlayback",
       pausePlayback: "player/pausePlayback",
diff --git a/front/src/components/library/AlbumDetail.vue b/front/src/components/library/AlbumDetail.vue
index 5bd30b73e65bda2ba528b0f375f7332e7b715210..a584a372a63bc9d0b6ccfc46be7798c8467c995a 100644
--- a/front/src/components/library/AlbumDetail.vue
+++ b/front/src/components/library/AlbumDetail.vue
@@ -4,7 +4,7 @@
       <translate key="1" v-if="isSerie" translate-context="Content/Channels/*">Episodes</translate>
       <translate key="2" v-else translate-context="*/*/*">Tracks</translate>
     </h2>
-    <channel-entries v-if="artist.channel && isSerie" :limit="50" :filters="{channel: artist.channel.uuid, album: object.id, ordering: '-creation_date'}">
+    <channel-entries v-if="artist.channel && isSerie" :is-podcast="isSerie" :limit="50" :filters="{channel: artist.channel.uuid, album: object.id, ordering: '-creation_date'}">
     </channel-entries>
     <template v-else-if="discs && discs.length > 1">
       <div v-for="tracks in discs" :key="tracks.disc_number">
diff --git a/front/src/style/components/_track_table.scss b/front/src/style/components/_track_table.scss
index 6d8f8a3a96ae2827c6695463ad94406c040eb028..7dfde12462a0e1c25e6f9f2586c9e707ff812ef4 100644
--- a/front/src/style/components/_track_table.scss
+++ b/front/src/style/components/_track_table.scss
@@ -209,4 +209,35 @@
     width: 0px;
     background: transparent;
   }
+}
+
+.track-table.podcast.row,
+.track-row.podcast.row {
+  height: 20vh;
+  align-items: center;
+  display: flex;
+  .ui.artist-track.mini.image {
+    height: 15vh;
+    width: auto;
+    top: auto;
+    bottom: auto;
+  }
+  .image.left.floated.column {
+    width: (15vh);
+    display: flex !important;
+    justify-content: center;
+    flex-direction: column;
+  }
+  .content.left.floated.column {
+    margin-left: 26px;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;  
+    overflow: hidden;
+
+    .podcast-episode-title {
+      font-weight: bold;
+      font-size: medium;
+    }
+  }
 }
\ No newline at end of file
diff --git a/front/src/views/channels/DetailOverview.vue b/front/src/views/channels/DetailOverview.vue
index 7c37fdbd23e103490ca92527f012f2f3b9071ece..54c53a9b4cf03c93175c2dc29e08681c3d7509a4 100644
--- a/front/src/views/channels/DetailOverview.vue
+++ b/front/src/views/channels/DetailOverview.vue
@@ -49,7 +49,7 @@
         :can-update="false"></rendered-description>
         <div class="ui hidden divider"></div>
     </div>
-    <channel-entries :key="String(episodesKey) + 'entries'" :default-cover='object.artist.cover' :limit='25' :filters="{channel: object.uuid, ordering: '-creation_date', page_size: '25'}">
+    <channel-entries :is-podcast="isPodcast" :key="String(episodesKey) + 'entries'" :default-cover='object.artist.cover' :limit='25' :filters="{channel: object.uuid, ordering: '-creation_date', page_size: '25'}">
       <h2 class="ui header">
         <translate key="1" v-if="isPodcast" translate-context="Content/Channel/Paragraph">Latest episodes</translate>
         <translate key="2" v-else translate-context="Content/Channel/Paragraph">Latest tracks</translate>