1+ import { ItemView , Notice , Plugin , TFile , WorkspaceLeaf } from "obsidian" ;
2+ import { get } from "svelte/store" ;
13import {
4+ episodeCache ,
25 currentEpisode ,
36 downloadedEpisodes ,
47 favorites ,
58 localFiles ,
69 playedEpisodes ,
710 playlists ,
11+ plugin ,
812 queue ,
913 savedFeeds ,
10- } from "src/store" ;
11- import { Plugin , type WorkspaceLeaf } from "obsidian" ;
12- import { API } from "src/API/API" ;
13- import type { IAPI } from "src/API/IAPI" ;
14- import { DEFAULT_SETTINGS , VIEW_TYPE } from "src/constants" ;
15- import { PodNotesSettingsTab } from "src/ui/settings/PodNotesSettingsTab" ;
16- import { MainView } from "src/ui/PodcastView" ;
17- import type { IPodNotesSettings } from "./types/IPodNotesSettings" ;
18- import { plugin } from "./store" ;
14+ } from "./store/index" ;
15+ import PodcastView from "./ui/PodcastView/PodcastView.svelte" ;
16+ import {
17+ DEFAULT_SETTINGS ,
18+ type PodNotesSettings ,
19+ } from "./settings/PodNotesSettings" ;
20+ import {
21+ downloadEpisode ,
22+ type DownloadedEpisode ,
23+ } from "./downloadEpisode" ;
24+ import { PodNotesSettingsTab } from "./ui/settings/PodNotesSettingsTab" ;
25+ import { API } from "./API" ;
26+ import type { Episode } from "./types/Episode" ;
1927import type { IPodNotes } from "./types/IPodNotes" ;
20- import { EpisodeStatusController } from "./store_controllers/EpisodeStatusController " ;
21- import type { StoreController } from "./types/StoreController " ;
22- import type { PlayedEpisode } from "./types/PlayedEpisode " ;
23- import type { PodcastFeed } from "./types/PodcastFeed " ;
28+ import { getContextMenuHandler } from "./getContextMenuHandler " ;
29+ import { podNotesURIHandler } from "./podNotesURIHandler " ;
30+ import type { Playlist } from "./types/Playlists " ;
31+ import { StoreController } from "./types/StoreController " ;
2432import { SavedFeedsController } from "./store_controllers/SavedFeedsController" ;
25- import type { Playlist } from "./types/Playlist" ;
2633import { PlaylistController } from "./store_controllers/PlaylistController" ;
2734import { QueueController } from "./store_controllers/QueueController" ;
2835import { FavoritesController } from "./store_controllers/FavoritesController" ;
29- import type { Episode } from "./types/Episode" ;
30- import CurrentEpisodeController from "./store_controllers/CurrentEpisodeController" ;
31- import { TimestampTemplateEngine } from "./TemplateEngine" ;
32- import createPodcastNote from "./createPodcastNote" ;
33- import downloadEpisodeWithNotice from "./downloadEpisode" ;
34- import type DownloadedEpisode from "./types/DownloadedEpisode" ;
35- import DownloadedEpisodesController from "./store_controllers/DownloadedEpisodesController" ;
3636import { LocalFilesController } from "./store_controllers/LocalFilesController" ;
37- import type PartialAppExtension from "./global" ;
38- import podNotesURIHandler from "./URIHandler" ;
39- import getContextMenuHandler from "./getContextMenuHandler" ;
40- import getUniversalPodcastLink from "./getUniversalPodcastLink" ;
41- import type { IconType } from "./types/IconType" ;
37+ import { DownloadedEpisodesController } from "./store_controllers/DownloadedEpisodesController" ;
38+ import { CurrentEpisodeController } from "./store_controllers/CurrentEpisodeController" ;
39+ import { EpisodeStatusController } from "./store_controllers/EpisodeStatusController" ;
4240import { TranscriptionService } from "./services/TranscriptionService" ;
41+ import type { IconType } from "./types/IconType" ;
4342
44- export default class PodNotes extends Plugin implements IPodNotes {
45- public api ! : IAPI ;
46- public settings ! : IPodNotesSettings ;
47- public override app ! : PartialAppExtension ;
43+ export const VIEW_TYPE = "pod-notes-view" ;
4844
49- private view : MainView | null = null ;
45+ class MainView extends ItemView {
46+ view : PodcastView ;
47+ plugin : PodNotes ;
5048
51- private playedEpisodeController ?: StoreController < {
52- [ episodeName : string ] : PlayedEpisode ;
53- } > ;
54- private savedFeedsController ?: StoreController < {
55- [ podcastName : string ] : PodcastFeed ;
56- } > ;
57- private playlistController ?: StoreController < {
58- [ playlistName : string ] : Playlist ;
59- } > ;
49+ constructor ( leaf : WorkspaceLeaf , plugin : PodNotes ) {
50+ super ( leaf ) ;
51+ this . plugin = plugin ;
52+ }
53+
54+ override getViewType ( ) : string {
55+ return VIEW_TYPE ;
56+ }
57+
58+ override getDisplayText ( ) : string {
59+ return "PodNotes" ;
60+ }
61+
62+ override async onOpen ( ) : Promise < void > {
63+ this . view = new PodcastView ( {
64+ target : this . contentEl ,
65+ props : { } ,
66+ } ) ;
67+ }
68+ }
69+
70+ export default class PodNotes extends Plugin implements IPodNotes {
71+ settings ! : PodNotesSettings ;
72+ view ?: MainView ;
73+ api ! : API ;
74+
75+ private playedEpisodeController ?: StoreController < Episode [ ] > ;
76+ private savedFeedsController ?: StoreController < { [ podcast : string ] : string } > ;
77+ private playlistController ?: StoreController < { [ key : string ] : Playlist } > ;
6078 private queueController ?: StoreController < Playlist > ;
6179 private favoritesController ?: StoreController < Playlist > ;
6280 private localFilesController ?: StoreController < Playlist > ;
6381 private currentEpisodeController ?: StoreController < Episode > ;
6482 private downloadedEpisodesController ?: StoreController < {
6583 [ podcastName : string ] : DownloadedEpisode [ ] ;
6684 } > ;
67- private transcriptionService ! : TranscriptionService ;
85+ private transcriptionService ? : TranscriptionService ;
6886
6987 private maxLayoutReadyAttempts = 10 ;
7088 private layoutReadyAttempts = 0 ;
@@ -103,27 +121,25 @@ export default class PodNotes extends Plugin implements IPodNotes {
103121 this ,
104122 ) . on ( ) ;
105123
106- this . transcriptionService = new TranscriptionService ( this ) ;
107-
108124 this . api = new API ( ) ;
109125
110126 this . addCommand ( {
111127 id : "podnotes-show-leaf" ,
112128 name : "Show PodNotes" ,
113129 icon : "podcast" as IconType ,
114- checkCallback : function ( this : PodNotes , checking : boolean ) {
115- if ( checking ) {
116- return ! this . app . workspace . getLeavesOfType ( VIEW_TYPE ) . length ;
117- }
130+ checkCallback : function ( this : PodNotes , checking : boolean ) {
131+ if ( checking ) {
132+ return ! this . app . workspace . getLeavesOfType ( VIEW_TYPE ) . length ;
133+ }
118134
119- const leaf = this . app . workspace . getRightLeaf ( false ) ;
120- if ( leaf ) {
121- leaf . setViewState ( {
122- type : VIEW_TYPE ,
123- } ) ;
124- }
125- } . bind ( this ) ,
126- } ) ;
135+ const leaf = this . app . workspace . getRightLeaf ( false ) ;
136+ if ( leaf ) {
137+ leaf . setViewState ( {
138+ type : VIEW_TYPE ,
139+ } ) ;
140+ }
141+ } . bind ( this ) ,
142+ } ) ;
127143
128144 this . addCommand ( {
129145 id : "start-playing" ,
@@ -140,137 +156,129 @@ export default class PodNotes extends Plugin implements IPodNotes {
140156
141157 this . addCommand ( {
142158 id : "stop-playing" ,
143- name : "Stop Podcast" ,
159+ name : "Stop playing Podcast" ,
144160 icon : "stop-circle" as IconType ,
145161 checkCallback : ( checking ) => {
146162 if ( checking ) {
147- return this . api . isPlaying && ! ! this . api . podcast ;
163+ return this . api . isPlaying ;
148164 }
149165
150166 this . api . stop ( ) ;
151167 } ,
152168 } ) ;
153169
154170 this . addCommand ( {
155- id : "skip-backward" ,
156- name : "Skip Backward" ,
157- icon : "skip-back" as IconType ,
158- checkCallback : ( checking ) => {
159- if ( checking ) {
160- return this . api . isPlaying && ! ! this . api . podcast ;
161- }
171+ id : "podnotes-add-timestamp" ,
172+ name : "Add Timestamp" ,
173+ callback : ( ) => this . api . addTimestamp ( ) ,
174+ } ) ;
162175
163- this . api . skipBackward ( ) ;
176+ this . addCommand ( {
177+ id : "podnotes-paste-timestamp-template" ,
178+ name : "Paste Timestamp Template" ,
179+ editorCallback : ( editor ) => {
180+ const cursor = editor . getCursor ( ) ;
181+ editor . replaceRange ( "{{timestamp}}" , cursor , cursor ) ;
164182 } ,
165183 } ) ;
166184
167185 this . addCommand ( {
168- id : "skip-forward" ,
169- name : "Skip Forward" ,
170- icon : "skip-forward" as IconType ,
186+ id : "podnotes-playlist-clear" ,
187+ name : "Clear current playlist" ,
171188 checkCallback : ( checking ) => {
172189 if ( checking ) {
173- return this . api . isPlaying && ! ! this . api . podcast ;
190+ return this . api . playlist . length > 0 ;
174191 }
175192
176- this . api . skipForward ( ) ;
193+ this . api . clearPlaylist ( ) ;
177194 } ,
178195 } ) ;
179196
180197 this . addCommand ( {
181- id : "download-playing-episode" ,
182- name : "Download Playing Episode" ,
183- icon : "download" as IconType ,
198+ id : "podnotes-playlist-shuffle" ,
199+ name : "Shuffle current playlist" ,
184200 checkCallback : ( checking ) => {
185201 if ( checking ) {
186- return ! ! this . api . podcast ;
202+ return this . api . playlist . length > 1 ;
187203 }
188204
189- const episode = this . api . podcast ;
190- downloadEpisodeWithNotice ( episode , this . settings . download . path ) ;
191- } ,
192- } ) ;
193-
194- this . addCommand ( {
195- id : "hrpn" ,
196- name : "Reload PodNotes" ,
197- callback : ( ) => {
198- const id = this . manifest . id ;
199-
200- this . app . plugins
201- . disablePlugin ( id )
202- . then ( ( ) => this . app . plugins . enablePlugin ( id ) ) ;
205+ this . api . shufflePlaylist ( ) ;
203206 } ,
204207 } ) ;
205208
206209 this . addCommand ( {
207- id : "capture-timestamp" ,
208- name : "Capture Timestamp" ,
209- icon : "clock" as IconType ,
210- editorCheckCallback : ( checking , editor , view ) => {
210+ id : "podnotes-playlist-save-local" ,
211+ name : "Save local playlist" ,
212+ checkCallback : ( checking ) => {
211213 if ( checking ) {
212- return ! ! this . api . podcast && ! ! this . settings . timestamp . template ;
214+ return this . api . playlist . length > 0 ;
213215 }
214216
215- const cursorPos = editor . getCursor ( ) ;
216- const capture = TimestampTemplateEngine (
217- this . settings . timestamp . template ,
218- ) ;
219-
220- editor . replaceRange ( capture , cursorPos ) ;
221- editor . setCursor ( cursorPos . line , cursorPos . ch + capture . length ) ;
217+ this . api . saveLocalPlaylist ( ) ;
222218 } ,
223219 } ) ;
224220
225221 this . addCommand ( {
226- id : "create-podcast-note" ,
227- name : "Create Podcast Note" ,
228- icon : "file-plus" as IconType ,
222+ id : "podnotes-playlist-save-favorites" ,
223+ name : "Save favorites playlist" ,
229224 checkCallback : ( checking ) => {
230225 if ( checking ) {
231- return (
232- ! ! this . api . podcast &&
233- ! ! this . settings . note . path &&
234- ! ! this . settings . note . template
235- ) ;
226+ return this . api . playlist . length > 0 ;
236227 }
237228
238- createPodcastNote ( this . api . podcast ) ;
229+ this . api . saveFavoritesPlaylist ( ) ;
239230 } ,
240231 } ) ;
241232
242233 this . addCommand ( {
243- id : "get-share-link-episode" ,
244- name : "Copy universal episode link to clipboard" ,
245- icon : "share" as IconType ,
234+ id : "podnotes-download-episode" ,
235+ name : "Download Episode" ,
246236 checkCallback : ( checking ) => {
237+ const episode = this . api . podcast ;
247238 if ( checking ) {
248- return ! ! this . api . podcast ;
239+ return ! ! episode ;
249240 }
250241
251- getUniversalPodcastLink ( this . api ) ;
242+ if ( ! episode ) {
243+ return ;
244+ }
245+
246+ const episodeFileName = episode . episodeFilename || episode . title ;
247+ const episodePath = `${ this . settings . downloadPath } /${ episode . podcastName } /${ episodeFileName } .mp3` ;
248+
249+ this . app . vault . adapter . exists ( episodePath ) . then ( async ( exists ) => {
250+ if ( exists ) {
251+ new Notice (
252+ `Episode ${ episode . title } already exists. Skipping.` ,
253+ ) ;
254+ } else {
255+ new Notice ( `Downloading episode ${ episode . title } ...` ) ;
256+ const file = await downloadEpisode ( episode , this ) ;
257+ new Notice (
258+ `Episode ${ episode . title } downloaded to ${ file . path } ` ,
259+ ) ;
260+ }
261+ } ) ;
252262 } ,
253263 } ) ;
254264
255265 this . addCommand ( {
256- id : "podnotes-toggle-playback" ,
257- name : "Toggle playback" ,
258- icon : "play" as IconType ,
266+ id : "podnotes-transcribe" ,
267+ name : "Transcribe current episode" ,
259268 checkCallback : ( checking ) => {
269+ const canTranscribe =
270+ ! ! this . api . podcast && ! ! this . settings . openAIApiKey ?. trim ( ) ;
271+
260272 if ( checking ) {
261- return ! ! this . api . podcast ;
273+ return canTranscribe ;
262274 }
263275
264- this . api . togglePlayback ( ) ;
276+ if ( canTranscribe ) {
277+ void this . getTranscriptionService ( ) . transcribeCurrentEpisode ( ) ;
278+ }
265279 } ,
266280 } ) ;
267281
268- this . addCommand ( {
269- id : "podnotes-transcribe" ,
270- name : "Transcribe current episode" ,
271- callback : ( ) => this . transcriptionService . transcribeCurrentEpisode ( ) ,
272- } ) ;
273-
274282 this . addSettingTab ( new PodNotesSettingsTab ( this . app , this ) ) ;
275283
276284 this . registerView ( VIEW_TYPE , ( leaf : WorkspaceLeaf ) => {
@@ -314,6 +322,14 @@ export default class PodNotes extends Plugin implements IPodNotes {
314322 }
315323 }
316324
325+ private getTranscriptionService ( ) : TranscriptionService {
326+ if ( ! this . transcriptionService ) {
327+ this . transcriptionService = new TranscriptionService ( this ) ;
328+ }
329+
330+ return this . transcriptionService ;
331+ }
332+
317333 override onunload ( ) {
318334 this . playedEpisodeController ?. off ( ) ;
319335 this . savedFeedsController ?. off ( ) ;
0 commit comments