-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathemms-cue.el
158 lines (138 loc) · 6.57 KB
/
emms-cue.el
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
147
148
149
150
151
152
153
154
155
156
157
158
;;; emms-cue.el --- Recognize cue sheet file -*- lexical-binding: t; -*-
;; Copyright (C) 2009 Free Software Foundation, Inc.
;; Author: William Xu <[email protected]>
;; This file is part of EMMS.
;; EMMS is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 3
;; of the License, or (at your option) any later version.
;; EMMS 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 General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with EMMS; if not, write to the Free Software Foundation,
;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
;;; Commentary:
;; By parsing cue file, we will be able to jump to arbitary track or
;; play next/previous track from a single .ape or .flac file.
;;; Code:
(require 'emms-playing-time)
(require 'emms-info)
(defun emms-cue-next ()
"Play next track from .cue file."
(interactive)
(let ((cue-track (emms-cue-next-track)))
(if (cdr cue-track)
(progn
(emms-seek-to (cdr cue-track))
(message "Will play: %s" (car cue-track)))
(message "Nothing to seek or missing .cue file?"))))
(defun emms-cue-previous ()
"Play previous track from .cue file."
(interactive)
(let ((cue-track (emms-cue-previous-track)))
(if (cdr cue-track)
(progn
(emms-seek-to (cdr cue-track))
(message "Will play: %s" (car cue-track)))
(message "Nothing to seek or missing .cue file?"))))
(defun emms-cue-jump ()
"Select a track from .cue file to play using completion."
(interactive)
(let ((cue-track (emms-cue-select-track)))
(if (cdr cue-track)
(progn
(emms-seek-to (cdr cue-track))
(message "Will play: %s" (car cue-track)))
(message "Nothing to seek or missing .cue file?"))))
(defun emms-cue-next-track (&optional previous-p)
"Get title and offset of next track from .cue file.
When PREVIOUS-P is t, get previous track info instead."
(let* ((track (emms-playlist-current-selected-track))
(name (emms-track-get track 'name))
(cue (concat (file-name-sans-extension name)".cue")))
(when (file-exists-p cue)
(with-temp-buffer
(emms-insert-file-contents cue)
(save-excursion
(if previous-p
(goto-char (point-max))
(goto-char (point-min)))
(let ((offset nil)
(title "")
;; We should search one more track far when getting previous
;; track.
(one-more-track previous-p))
(while (and (not offset)
(funcall
(if previous-p 'search-backward-regexp 'search-forward-regexp)
"INDEX 01 \\([0-9][0-9]\\):\\([0-9][0-9]\\):\\([0-9][0-9]\\)" nil t 1))
(let* ((min (string-to-number (match-string-no-properties 1)))
(sec (string-to-number (match-string-no-properties 2)))
(msec (string-to-number (match-string-no-properties 3)))
(total-sec (+ (* min 60) sec (/ msec 100.0))))
(when (funcall (if previous-p '> '<) emms-playing-time total-sec)
(if (not one-more-track)
(progn
(setq offset total-sec)
(when (search-backward-regexp "TITLE \"\\(.*\\)\"" nil t 1)
(setq title (match-string-no-properties 1))))
(setq one-more-track nil)))))
(cons title offset)))))))
(defun emms-cue-previous-track ()
"See `emms-cue-next-track'."
(emms-cue-next-track t))
(defun emms-cue-select-track ()
"Get a list of title and offset of tracks from .cue file and call
completing-read to select one"
(let* ((track (emms-playlist-current-selected-track))
(name (emms-track-get track 'name))
(cue (concat (file-name-sans-extension name)".cue"))
(tracks-found '()))
(when (file-exists-p cue)
(with-temp-buffer
(emms-insert-file-contents cue)
(save-excursion
(goto-char (point-max)) ; search backwards
(while (search-backward-regexp "INDEX 01 \\([0-9][0-9]\\):\\([0-9][0-9]\\):\\([0-9][0-9]\\)" nil t 1)
(let* ((min (string-to-number (match-string-no-properties 1)))
(sec (string-to-number (match-string-no-properties 2)))
(msec (string-to-number (match-string-no-properties 3)))
(total-sec (+ (* min 60) sec (/ msec 100.0)))
(title ""))
(when (search-backward-regexp "TITLE \"\\(.*\\)\"" nil t 1)
(setq title (match-string-no-properties 1)))
(push (cons title total-sec) tracks-found)))))
(let* ((tracks-complete-table (lambda (string pred action)
(if (eq action 'metadata)
`(metadata (display-sort-function . ,#'identity)) ; don't sort
(complete-with-action action (mapcar #'car tracks-found) string pred))))
(selection (completing-read "Select a track to play: " tracks-complete-table nil t)))
(assoc selection tracks-found)))))
(defun emms-info-cueinfo (track)
"Add track information to TRACK.
This is a useful element for `emms-info-functions'."
(when (and (emms-track-file-p track)
(string-match "\\.\\(ape\\|flac\\)\\'" (emms-track-name track)))
(let ((cue (concat (file-name-sans-extension (emms-track-name track))
".cue")))
(when (file-exists-p cue)
(with-temp-buffer
(emms-insert-file-contents cue)
(save-excursion
(mapc (lambda (i)
(goto-char (point-min))
(when (let ((case-fold-search t))
(search-forward-regexp
(concat (car i) " \\(.*\\)") nil t 1))
(emms-track-set track
(cdr i)
(replace-regexp-in-string
"\\`\"\\|\"\\'" "" (match-string 1)))))
'(("performer" . info-artist)
("title" . info-album)
("title" . info-title)
("rem date" . info-year)))))))))
(provide 'emms-cue)
;;; emms-cue.el ends here