Skip to content

Commit

Permalink
feat: add playlists, queue, and favorites
Browse files Browse the repository at this point in the history
  • Loading branch information
chhoumann committed Jul 19, 2022
1 parent 420a0af commit c758477
Show file tree
Hide file tree
Showing 18 changed files with 516 additions and 13 deletions.
24 changes: 24 additions & 0 deletions src/FavoritesController.ts
@@ -0,0 +1,24 @@
import { Writable } from "svelte/store";
import { FAVORITES_SETTINGS } from "./constants";
import { IPodNotes } from "./types/IPodNotes";
import { Playlist } from "./types/Playlist";
import { StoreController } from "./types/StoreController";

export class FavoritesController extends StoreController<Playlist> {
private plugin: IPodNotes;

constructor(store: Writable<Playlist>, plugin: IPodNotes) {
super(store)
this.plugin = plugin;
}

protected onChange(value: Playlist) {
this.plugin.settings.favorites = {
...value,
// To ensure we always keep the correct playlist name
...FAVORITES_SETTINGS
};

this.plugin.saveSettings();
}
}
19 changes: 19 additions & 0 deletions src/PlaylistController.ts
@@ -0,0 +1,19 @@
import { Writable } from "svelte/store";
import { IPodNotes } from "./types/IPodNotes";
import { Playlist } from "./types/Playlist";
import { StoreController } from "./types/StoreController";

export class PlaylistController extends StoreController<{[playlistName: string]: Playlist}> {
private plugin: IPodNotes;

constructor(store: Writable<{[playlistName: string]: Playlist}>, plugin: IPodNotes) {
super(store)
this.plugin = plugin;
}

protected onChange(value: {[playlistName: string]: Playlist}) {
this.plugin.settings.playlists = value;

this.plugin.saveSettings();
}
}
24 changes: 24 additions & 0 deletions src/QueueController.ts
@@ -0,0 +1,24 @@
import { Writable } from "svelte/store";
import { QUEUE_SETTINGS } from "./constants";
import { IPodNotes } from "./types/IPodNotes";
import { Playlist } from "./types/Playlist";
import { StoreController } from "./types/StoreController";

export class QueueController extends StoreController<Playlist> {
private plugin: IPodNotes;

constructor(store: Writable<Playlist>, plugin: IPodNotes) {
super(store)
this.plugin = plugin;
}

protected onChange(value: Playlist) {
this.plugin.settings.queue = {
...value,
// To ensure we always keep the correct playlist name
...QUEUE_SETTINGS
};

this.plugin.saveSettings();
}
}
27 changes: 27 additions & 0 deletions src/constants.ts
@@ -1,12 +1,39 @@
import { IPodNotesSettings } from "src/types/IPodNotesSettings";
import { Playlist } from "./types/Playlist";

export const VIEW_TYPE = "podcast_player_view";

type PlaylistSettings = Pick<Playlist, "icon" | "name" | "shouldEpisodeRemoveAfterPlay" | "shouldRepeat">;

export const FAVORITES_SETTINGS: PlaylistSettings = {
icon: "lucide-star",
name: "Favorites",
shouldEpisodeRemoveAfterPlay: false,
shouldRepeat: false,
}

export const QUEUE_SETTINGS: PlaylistSettings = {
icon: "list-ordered",
name: "Queue",
shouldEpisodeRemoveAfterPlay: true,
shouldRepeat: false,
}

export const DEFAULT_SETTINGS: IPodNotesSettings = {
savedFeeds: {},
podNotes: {},
defaultPlaybackRate: 1,
playedEpisodes: {},
favorites: {
...FAVORITES_SETTINGS,
episodes: [],
},
queue: {
...QUEUE_SETTINGS,
episodes: [],
},
playlists: {},
skipBackwardLength: 15,
skipForwardLength: 15,
}

18 changes: 17 additions & 1 deletion src/main.ts
@@ -1,5 +1,5 @@
import FeedParser from 'src/parser/feedParser';
import { currentEpisode, playedEpisodes, savedFeeds } from 'src/store';
import { currentEpisode, favorites, playedEpisodes, playlists, queue, savedFeeds } from 'src/store';
import { Notice, Plugin, WorkspaceLeaf } from 'obsidian';
import { API } from 'src/API/API';
import { IAPI } from 'src/API/IAPI';
Expand All @@ -15,6 +15,10 @@ import { StoreController } from './types/StoreController';
import { PlayedEpisode } from './types/PlayedEpisode';
import { PodcastFeed } from './types/PodcastFeed';
import { SavedFeedsController } from './SavedFeedsController';
import { Playlist } from './types/Playlist';
import { PlaylistController } from './PlaylistController';
import { QueueController } from './QueueController';
import { FavoritesController } from './FavoritesController';

export default class PodNotes extends Plugin implements IPodNotes {
public api: IAPI;
Expand All @@ -24,6 +28,9 @@ export default class PodNotes extends Plugin implements IPodNotes {

private playedEpisodeController: StoreController<{[episodeName: string]: PlayedEpisode}>;
private savedFeedsController: StoreController<{[podcastName: string]: PodcastFeed}>;
private playlistController: StoreController<{ [playlistName: string]: Playlist }>;
private queueController: StoreController<Playlist>;
private favoritesController: StoreController<Playlist>;

async onload() {
plugin.set(this);
Expand All @@ -32,9 +39,15 @@ export default class PodNotes extends Plugin implements IPodNotes {

playedEpisodes.set(this.settings.playedEpisodes);
savedFeeds.set(this.settings.savedFeeds);
playlists.set(this.settings.playlists);
queue.set(this.settings.queue);
favorites.set(this.settings.favorites);

this.playedEpisodeController = new EpisodeStatusController(playedEpisodes, this).on();
this.savedFeedsController = new SavedFeedsController(savedFeeds, this).on();
this.playlistController = new PlaylistController(playlists, this).on();
this.queueController = new QueueController(queue, this).on();
this.favoritesController = new FavoritesController(favorites, this).on();

this.addCommand({
id: 'start-playing',
Expand Down Expand Up @@ -141,6 +154,9 @@ export default class PodNotes extends Plugin implements IPodNotes {
onunload() {
this?.playedEpisodeController.off();
this?.savedFeedsController.off();
this?.playlistController.off();
this?.queueController.off();
this?.favoritesController.off();
}

async loadSettings() {
Expand Down
21 changes: 20 additions & 1 deletion src/store/index.ts
Expand Up @@ -3,6 +3,7 @@ import type PodNotes from 'src/main';
import { Episode } from 'src/types/Episode';
import { PlayedEpisode } from 'src/types/PlayedEpisode';
import { PodcastFeed } from 'src/types/PodcastFeed';
import { Playlist } from 'src/types/Playlist';

export const plugin = writable<PodNotes>();
export const currentTime = writable<number>(0);
Expand All @@ -14,4 +15,22 @@ export const playedEpisodes = writable<{
}>({});
export const savedFeeds = writable<{[podcastName: string]: PodcastFeed}>({});

export const episodeCache = writable<{[podcastName: string]: Episode[]}>({});
export const episodeCache = writable<{[podcastName: string]: Episode[]}>({});

export const queue = writable<Playlist>({
icon: 'list-ordered',
name: 'Queue',
episodes: [],
shouldEpisodeRemoveAfterPlay: true,
shouldRepeat: false,
});

export const favorites = writable<Playlist>({
icon: 'lucide-star',
name: 'Favorites',
episodes: [],
shouldEpisodeRemoveAfterPlay: false,
shouldRepeat: false,
});

export const playlists = writable<{ [name: string]: Playlist }>({});
4 changes: 4 additions & 0 deletions src/types/IPodNotesSettings.ts
@@ -1,6 +1,7 @@
import { PodNote } from './PodNotes';
import { PodcastFeed } from "./PodcastFeed";
import { PlayedEpisode } from './PlayedEpisode';
import { Playlist } from './Playlist';

export interface IPodNotesSettings {
savedFeeds: { [podcastName: string]: PodcastFeed };
Expand All @@ -9,4 +10,7 @@ export interface IPodNotesSettings {
playedEpisodes: { [episodeName: string]: PlayedEpisode }
skipBackwardLength: number;
skipForwardLength: number;
playlists: { [playlistName: string]: Playlist }
queue: Playlist,
favorites: Playlist,
}
13 changes: 13 additions & 0 deletions src/types/Playlist.ts
@@ -0,0 +1,13 @@
import { Episode } from "./Episode";
import { IconType } from "./IconType";

export type Playlist = {
icon: IconType,
name: string;
episodes: Episode[];

currentEpisode?: Episode;

shouldEpisodeRemoveAfterPlay: boolean;
shouldRepeat: boolean;
}
5 changes: 5 additions & 0 deletions src/ui/PodcastView/EpisodeList.svelte
Expand Up @@ -17,6 +17,10 @@
dispatch("clickEpisode", { episode: event.detail.episode });
}
function forwardContextMenuEpisode(event: CustomEvent<{ episode: Episode, event: MouseEvent }>) {
dispatch("contextMenuEpisode", { episode: event.detail.episode, event: event.detail.event });
}
function forwardSearchInput(event: CustomEvent<{ value: string }>) {
dispatch("search", { query: event.detail.value });
}
Expand Down Expand Up @@ -60,6 +64,7 @@
episodeFinished={episodePlayed}
showEpisodeImage={showThumbnails}
on:clickEpisode={forwardClickEpisode}
on:contextMenu={forwardContextMenuEpisode}
/>
{/if}
{/each}
Expand Down
5 changes: 5 additions & 0 deletions src/ui/PodcastView/EpisodeListItem.svelte
Expand Up @@ -12,13 +12,18 @@
dispatch("clickEpisode", { episode });
}
function onContextMenu(event: MouseEvent) {
dispatch("contextMenu", { episode, event });
}
const _date = new Date(episode.episodeDate || "");
const date = window.moment(_date).format("DD MMMM YYYY");
</script>

<div
class="podcast-episode-item"
on:click={onClickEpisode}
on:contextmenu={onContextMenu}
>
{#if showEpisodeImage && episode?.artworkUrl}
<div class="podcast-episode-thumbnail-container">
Expand Down
32 changes: 32 additions & 0 deletions src/ui/PodcastView/PlaylistCard.svelte
@@ -0,0 +1,32 @@
<script lang="ts">
import { Playlist } from "src/types/Playlist";
import Icon from "../obsidian/Icon.svelte";
export let playlist: Playlist;
</script>

<div class="playlist-card" aria-label={playlist.name}>
<Icon icon={playlist.icon} size={40} clickable={true}/>
<span>
({playlist.episodes.length})
</span>
</div>

<style>
.playlist-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
cursor: pointer;
border: 1px solid var(--background-modifier-border);
text-align: center;
overflow: hidden;
}
.playlist-card:hover {
background-color: var(--background-modifier-border);
}
</style>
13 changes: 12 additions & 1 deletion src/ui/PodcastView/PodcastGrid.svelte
@@ -1,11 +1,20 @@
<script lang="ts">
import { Playlist } from "src/types/Playlist";
import { PodcastFeed } from "src/types/PodcastFeed";
import PlaylistCard from "./PlaylistCard.svelte";
import PodcastGridCard from "./PodcastGridCard.svelte";
export let feeds: PodcastFeed[] = [];
export let playlists: Playlist[] = [];
</script>

<div class="podcast-grid">
{#if playlists.length > 0}
{#each playlists as playlist}
<PlaylistCard playlist={playlist} />
{/each}
{/if}

{#if feeds.length > 0}
{#each feeds as feed}
<PodcastGridCard
Expand All @@ -23,7 +32,9 @@
<style>
.podcast-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-auto-flow: row;
grid-auto-rows: 1fr;
grid-gap: 0rem;
}
</style>
1 change: 1 addition & 0 deletions src/ui/PodcastView/PodcastGridCard.svelte
Expand Up @@ -34,5 +34,6 @@
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border: 1px solid var(--background-modifier-border);
}
</style>

0 comments on commit c758477

Please sign in to comment.