summaryrefslogtreecommitdiff
path: root/modules/quick-video-capture.el
blob: 100cf04afa1ef424079cb718963946a231f35133 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
;;; quick-video-capture.el --- Video Capturing with Org Capture -*- coding: utf-8; lexical-binding: t; -*-

;;; Commentary:

;; This package provides a seamless "fire-and-forget" workflow for downloading
;; videos from the browser to your local system using yt-dlp and task-spooler.
;;
;; Features:
;; - Browser bookmarklet integration via org-protocol
;; - Automatic queueing of downloads through task-spooler
;; - Works with any yt-dlp supported site
;; - Can be triggered manually via org-capture with URL prompt
;;
;; Setup:
;; 1. Load this file - it will auto-initialize after Emacs startup
;; 2. Add the bookmarklet from `cj/video-download-bookmarklet' to your browser
;; 3. Click the bookmarklet on any video page to queue a download
;;
;; Alternatively, trigger manually with C-c c v and enter a URL

;;; Code:

;; Declare external functions to avoid warnings
(declare-function org-capture "org-capture" (&optional goto keys))
(declare-function org-protocol-check-filename-for-protocol "org-protocol" (fname restoffiles client))
(declare-function cj/yt-dl-it "media-utils" (url))

(defvar org-capture-templates)
(defvar org-protocol-protocol-alist)

(defconst cj/video-download-bookmarklet
  "javascript:location.href='org-protocol://video-download?url='+encodeURIComponent(location.href);void(0);"
  "JavaScript bookmarklet for triggering video downloads from the browser.
Add this as a bookmark in your browser to enable one-click video downloads.")

(defvar cj/video-download-current-url nil
  "Temporary storage for URL passed via org-protocol.")

(defvar cj/video-download-initialized nil
  "Flag to track if video download has been initialized.")

(defun cj/org-protocol-video-download (info)
  "Process org-protocol video download requests.
INFO is a plist containing :url from the org-protocol call."
  ;; Ensure we're initialized when called via protocol
  (cj/ensure-video-download-initialized)
  (let ((url (plist-get info :url)))
    (when url
      ;; Store the URL for the capture template to use
      (setq cj/video-download-current-url url))
    ;; Trigger the capture
    (org-capture nil "v")
    nil))  ; Return nil to indicate we handled it

(defun cj/video-download-capture-handler ()
  "Handle video download during org-capture.
This function is called from the capture template."
  ;; Ensure media-utils is loaded when we actually need to download
  (require 'media-utils)
  (let ((url (or cj/video-download-current-url
                 (read-string "Video URL: "))))
    ;; Clear the stored URL after using it
    (setq cj/video-download-current-url nil)
    (if (string-empty-p url)
        (error "No URL provided for download")
      (cj/yt-dl-it url)
      ;; Return empty string to prevent capture from saving anything
      "")))

(defun cj/ensure-video-download-initialized ()
  "Ensure video download is initialized, loading dependencies if needed."
  (unless cj/video-download-initialized
    (cj/setup-video-download)))

(defun cj/setup-video-download ()
  "Initialize video download functionality.
This function sets up org-protocol handlers and capture templates.
It's designed to be idempotent - safe to call multiple times."
  (when (not cj/video-download-initialized)
    ;; Load required packages
    (require 'org-protocol)
    (require 'org-capture)
    
    ;; Register the org-protocol handler if not already registered
    (unless (assoc "video-download" org-protocol-protocol-alist)
      (add-to-list 'org-protocol-protocol-alist
                   '("video-download"
                     :protocol "video-download"
                     :function cj/org-protocol-video-download
                     :kill-client t)))
    
    ;; Add the capture template if not already added
    (unless (assoc "v" org-capture-templates)
      (add-to-list 'org-capture-templates
                   '("v" "Video Download" entry
                     (file "")  ; No file needed since we're not saving
                     "%(cj/video-download-capture-handler)"
                     :immediate-finish t
                     :jump-to-captured nil)))
    
    (setq cj/video-download-initialized t)
    (message "Video download functionality initialized")))

(defun cj/video-download-bookmarklet-instructions ()
  "Display instructions for setting up the browser bookmarklet."
  (interactive)
  (let ((buf (get-buffer-create "*Video Download Bookmarklet Setup*")))
    (with-current-buffer buf
      (erase-buffer)
      (insert "Video Download Bookmarklet Setup\n")
      (insert "=================================\n\n")
      (insert "1. Create a new bookmark in your browser\n")
      (insert "2. Set the name to: Download Video (or your preference)\n")
      (insert "3. Set the URL to the following JavaScript code:\n\n")
      (insert cj/video-download-bookmarklet)
      (insert "\n\n")
      (insert "4. Save the bookmark to your bookmarks bar\n")
      (insert "5. Click the bookmark when viewing a video to download it\n\n")
      (insert "Note: Make sure Emacs server is running (M-x server-start)\n")
      (insert "and emacsclient is properly configured for org-protocol.\n"))
    (switch-to-buffer buf)))

;; Deferred initialization strategy:
;; 1. Try to load shortly after Emacs is idle following init
;; 2. Fallback timer ensures loading within 2 seconds regardless
(add-hook 'after-init-hook
          (lambda ()
            (run-with-idle-timer 0.5 nil #'cj/setup-video-download)))

;; Fallback: ensure initialization within 2 seconds of loading this file
(run-with-timer 2 nil #'cj/setup-video-download)

;; If someone manually triggers capture before initialization
(with-eval-after-load 'org-capture
  (add-hook 'org-capture-mode-hook #'cj/ensure-video-download-initialized))

;;; Commentary for bookmarklet:
;;
;; To use the browser bookmarklet, add this JavaScript as a bookmark URL:
;; javascript:location.href='org-protocol://video-download?url='+encodeURIComponent(location.href);void(0);
;;
;; This will send the current page URL to Emacs via org-protocol, triggering
;; the download through yt-dlp and task-spooler.

(provide 'quick-video-capture)
;;; quick-video-capture.el ends here