ruby-freedb 0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/ext/freedb_cdrom/extconf.rb +9 -0
- data/ext/freedb_cdrom/freedb_cdrom.c +207 -0
- data/lib/freedb.rb +638 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ac0470c2d99f0522088f36660bbc98a407674bea
|
4
|
+
data.tar.gz: 05b89f019dfa43587b5c49a82f59e83db746e859
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d779275beaf433f832ac0af4fb6bb531cbd36fa0da8fc4a6882ab7d37ff0eac1a9f43e15ef7f90170a3d5b7860ed9dad6902edc75712e7aa3ade66bad3255f90
|
7
|
+
data.tar.gz: 1987cfe6889b95dac68a282e3799040741425a003b55496e148856a75a273928314062e1da233bf65cd82bc8334f81e71fec01915990485ecc3ed788b2dc433e
|
@@ -0,0 +1,207 @@
|
|
1
|
+
/* $Id: freedb_cdrom.c,v 1.5 2003/02/07 14:16:16 moumar Exp $ */
|
2
|
+
/*
|
3
|
+
* Copyright (c) 1999,2001 Robert Woodcock <rcw@debian.org>
|
4
|
+
* This code is hereby licensed for public consumption under either the
|
5
|
+
* GNU GPL v2 or greater, or Larry Wall's Artistic license - your choice.
|
6
|
+
|
7
|
+
* You should have received a copy of the GNU General Public License
|
8
|
+
* along with this program; if not, write to the Free Software
|
9
|
+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
10
|
+
*/
|
11
|
+
#include <stdio.h>
|
12
|
+
#include <stdlib.h>
|
13
|
+
#include <fcntl.h>
|
14
|
+
#include <sys/ioctl.h>
|
15
|
+
#include <unistd.h>
|
16
|
+
|
17
|
+
#include <ruby.h>
|
18
|
+
|
19
|
+
/* Porting credits:
|
20
|
+
* Solaris: David Champion <dgc@uchicago.edu>
|
21
|
+
* FreeBSD: Niels Bakker <niels@bakker.net>
|
22
|
+
* OpenBSD: Marcus Daniel <danielm@uni-muenster.de>
|
23
|
+
* NetBSD: Chris Gilbert <chris@NetBSD.org>
|
24
|
+
*/
|
25
|
+
|
26
|
+
/* Modified for ruby/freedb by Guillaume Pierronnet <moumar@netcourrier.com> */
|
27
|
+
|
28
|
+
#if defined(OS_LINUX)
|
29
|
+
# include <linux/cdrom.h>
|
30
|
+
|
31
|
+
#elif defined(OS_SOLARIS)
|
32
|
+
# include <sys/cdio.h>
|
33
|
+
# define CD_MSF_OFFSET 150
|
34
|
+
# define CD_FRAMES 75
|
35
|
+
|
36
|
+
#elif defined(OS_FREEBSD)
|
37
|
+
#include <sys/cdio.h>
|
38
|
+
#define CDROM_LBA CD_LBA_FORMAT /* first frame is 0 */
|
39
|
+
#define CD_MSF_OFFSET 150 /* MSF offset of first frame */
|
40
|
+
#define CD_FRAMES 75 /* per second */
|
41
|
+
#define CDROM_LEADOUT 0xAA /* leadout track */
|
42
|
+
#define CDROMREADTOCHDR CDIOREADTOCHEADER
|
43
|
+
#define CDROMREADTOCENTRY CDIOREADTOCENTRY
|
44
|
+
#define cdrom_tochdr ioc_toc_header
|
45
|
+
#define cdth_trk0 starting_track
|
46
|
+
#define cdth_trk1 ending_track
|
47
|
+
#define cdrom_tocentry ioc_read_toc_single_entry
|
48
|
+
#define cdte_track track
|
49
|
+
#define cdte_format address_format
|
50
|
+
#define cdte_addr entry.addr
|
51
|
+
|
52
|
+
#elif defined(OS_OPENBSD) || defined(OS_NETBSD)
|
53
|
+
#include <sys/cdio.h>
|
54
|
+
#define CDROM_LBA CD_LBA_FORMAT /* first frame is 0 */
|
55
|
+
#define CD_MSF_OFFSET 150 /* MSF offset of first frame */
|
56
|
+
#define CD_FRAMES 75 /* per second */
|
57
|
+
#define CDROM_LEADOUT 0xAA /* leadout track */
|
58
|
+
#define CDROMREADTOCHDR CDIOREADTOCHEADER
|
59
|
+
#define cdrom_tochdr ioc_toc_header
|
60
|
+
#define cdth_trk0 starting_track
|
61
|
+
#define cdth_trk1 ending_track
|
62
|
+
#define cdrom_tocentry cd_toc_entry
|
63
|
+
#define cdte_track track
|
64
|
+
#define cdte_addr addr
|
65
|
+
|
66
|
+
#endif
|
67
|
+
|
68
|
+
VALUE cFreedb;
|
69
|
+
|
70
|
+
int cddb_sum (int n)
|
71
|
+
{
|
72
|
+
/* a number like 2344 becomes 2+3+4+4 (13) */
|
73
|
+
int ret=0;
|
74
|
+
|
75
|
+
while (n > 0) {
|
76
|
+
ret = ret + (n % 10);
|
77
|
+
n = n / 10;
|
78
|
+
}
|
79
|
+
|
80
|
+
return ret;
|
81
|
+
}
|
82
|
+
|
83
|
+
/*
|
84
|
+
* Returns a valid DISCID for the CD in (device)
|
85
|
+
*/
|
86
|
+
static VALUE fdb_get_cdrom(VALUE self, VALUE device) {
|
87
|
+
char str[1201];
|
88
|
+
#if defined(OS_OPENBSD) || defined(OS_NETBSD)
|
89
|
+
struct ioc_read_toc_entry t;
|
90
|
+
#endif
|
91
|
+
int len;
|
92
|
+
int drive, i, totaltime;
|
93
|
+
long int cksum=0;
|
94
|
+
unsigned char last=1;
|
95
|
+
struct cdrom_tochdr hdr;
|
96
|
+
struct cdrom_tocentry *TocEntry;
|
97
|
+
|
98
|
+
|
99
|
+
char offsets[1089] = "", buff[255];
|
100
|
+
|
101
|
+
SafeStringValue(device);
|
102
|
+
drive = open(RSTRING_PTR(device), O_RDONLY | O_NONBLOCK);
|
103
|
+
|
104
|
+
if (drive < 0) {
|
105
|
+
rb_sys_fail(RSTRING_PTR(device));
|
106
|
+
}
|
107
|
+
|
108
|
+
if (ioctl(drive,CDROMREADTOCHDR,&hdr) < 0) {
|
109
|
+
close(drive);
|
110
|
+
rb_sys_fail("Failed to read TOC entry");
|
111
|
+
}
|
112
|
+
|
113
|
+
last=hdr.cdth_trk1;
|
114
|
+
len = (last + 1) * sizeof (struct cdrom_tocentry);
|
115
|
+
/*
|
116
|
+
if (TocEntry) {
|
117
|
+
free(TocEntry);
|
118
|
+
TocEntry = 0;
|
119
|
+
}
|
120
|
+
*/
|
121
|
+
TocEntry = malloc(len);
|
122
|
+
if (!TocEntry) {
|
123
|
+
close(drive);
|
124
|
+
rb_sys_fail("Can't allocate memory for TOC entries");
|
125
|
+
}
|
126
|
+
#if defined(OS_OPENBSD)
|
127
|
+
t.address_format = CDROM_LBA;
|
128
|
+
t.starting_track = 0;
|
129
|
+
t.data_len = len;
|
130
|
+
t.data = TocEntry;
|
131
|
+
|
132
|
+
if (ioctl(drive, CDIOREADTOCENTRYS, (char *) &t) < 0)
|
133
|
+
free(TocEntry);
|
134
|
+
close(drive);
|
135
|
+
rb_sys_fail("Failed to read TOC entry");
|
136
|
+
}
|
137
|
+
#elif defined(OS_NETBSD)
|
138
|
+
t.address_format = CDROM_LBA;
|
139
|
+
t.starting_track = 1;
|
140
|
+
t.data_len = len;
|
141
|
+
t.data = TocEntry;
|
142
|
+
memset(TocEntry, 0, len);
|
143
|
+
|
144
|
+
if(ioctl(drive, CDIOREADTOCENTRYS, (char *) &t) < 0) {
|
145
|
+
free(TocEntry);
|
146
|
+
close(drive);
|
147
|
+
rb_sys_fail("Failed to read TOC entry");
|
148
|
+
}
|
149
|
+
#else
|
150
|
+
|
151
|
+
for (i=0; i < last; i++) {
|
152
|
+
TocEntry[i].cdte_track = i + 1; /* tracks start with 1, but i must start with 0 on OpenBSD */
|
153
|
+
TocEntry[i].cdte_format = CDROM_LBA;
|
154
|
+
if (ioctl(drive, CDROMREADTOCENTRY, &TocEntry[i]) < 0) {
|
155
|
+
free(TocEntry);
|
156
|
+
close(drive);
|
157
|
+
rb_sys_fail("Failed to read TOC entry");
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
TocEntry[last].cdte_track = CDROM_LEADOUT;
|
162
|
+
TocEntry[last].cdte_format = CDROM_LBA;
|
163
|
+
if (ioctl(drive, CDROMREADTOCENTRY, &TocEntry[i]) < 0) {
|
164
|
+
free(TocEntry);
|
165
|
+
close(drive);
|
166
|
+
rb_sys_fail("Failed to read TOC entry");
|
167
|
+
}
|
168
|
+
#endif
|
169
|
+
close(drive);
|
170
|
+
|
171
|
+
#if defined(OS_FREEBSD)
|
172
|
+
TocEntry[i].cdte_addr.lba = ntohl(TocEntry[i].cdte_addr.lba);
|
173
|
+
#endif
|
174
|
+
|
175
|
+
for (i=0; i < last; i++) {
|
176
|
+
#if defined(OS_FREEBSD)
|
177
|
+
TocEntry[i].cdte_addr.lba = ntohl(TocEntry[i].cdte_addr.lba);
|
178
|
+
#endif
|
179
|
+
cksum += cddb_sum((TocEntry[i].cdte_addr.lba + CD_MSF_OFFSET) / CD_FRAMES);
|
180
|
+
}
|
181
|
+
|
182
|
+
totaltime = ((TocEntry[last].cdte_addr.lba + CD_MSF_OFFSET) / CD_FRAMES) -
|
183
|
+
((TocEntry[0].cdte_addr.lba + CD_MSF_OFFSET) / CD_FRAMES);
|
184
|
+
|
185
|
+
|
186
|
+
for (i = 0; i < last; i++) { /* write offsets */
|
187
|
+
sprintf(buff, "%d ", TocEntry[i].cdte_addr.lba + CD_MSF_OFFSET);
|
188
|
+
strcat(offsets, buff);
|
189
|
+
}
|
190
|
+
|
191
|
+
sprintf(buff,"%d", (TocEntry[last].cdte_addr.lba + CD_MSF_OFFSET) / CD_FRAMES);
|
192
|
+
strcat(offsets, buff);
|
193
|
+
|
194
|
+
sprintf(str, "%08lx %d %s", (cksum % 0xff) << 24 | totaltime << 8 | last, last, offsets);
|
195
|
+
|
196
|
+
free(TocEntry);
|
197
|
+
|
198
|
+
return rb_str_new2(str);
|
199
|
+
}
|
200
|
+
|
201
|
+
void Init_freedb_cdrom() {
|
202
|
+
|
203
|
+
cFreedb = rb_define_class("Freedb", rb_cObject);
|
204
|
+
rb_define_private_method(cFreedb, "get_cdrom", fdb_get_cdrom, 1);
|
205
|
+
//rb_require("freedb_misc.rb");
|
206
|
+
|
207
|
+
}
|
data/lib/freedb.rb
ADDED
@@ -0,0 +1,638 @@
|
|
1
|
+
# $Id: freedb.rb,v 1.22 2003/02/13 15:52:04 moumar Exp $
|
2
|
+
# = Description
|
3
|
+
#
|
4
|
+
# ruby-freedb is a Ruby library who provide access to cddb/freedb servers as
|
5
|
+
# well as local database, can dump the "discid" from a CD and submit new
|
6
|
+
# entries to the freedb database.
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# = Download
|
10
|
+
#
|
11
|
+
# get tar.gz and debian packages at
|
12
|
+
# http://davedd.free.fr/ruby-freedb/download/
|
13
|
+
#
|
14
|
+
#
|
15
|
+
# = Installation
|
16
|
+
#
|
17
|
+
# <b>CAUTION</b>: Some files have changed since 0.4, please clean up your old ruby-freedb
|
18
|
+
# (0.3.1 and older) installation before installing this one by deleting our
|
19
|
+
# freedb_misc.rb and freedb.so.
|
20
|
+
#
|
21
|
+
# $ ruby extconf.rb
|
22
|
+
# $ make
|
23
|
+
# $ make install
|
24
|
+
#
|
25
|
+
#
|
26
|
+
# = Examples
|
27
|
+
# see examples/ directory for more advanced examples
|
28
|
+
#
|
29
|
+
# === get all possible matches for CD in "/dev/cdrom"
|
30
|
+
#
|
31
|
+
# freedb = Freedb.new("/dev/cdrom")
|
32
|
+
# freedb.fetch
|
33
|
+
# freedb.results.each { |r| puts r }
|
34
|
+
#
|
35
|
+
# === getting full description
|
36
|
+
# # get "rock" match for this cd
|
37
|
+
# freedb.get_result("rock")
|
38
|
+
#
|
39
|
+
# === make something with your freedb object
|
40
|
+
# puts freedb.title # disc's title
|
41
|
+
# puts freedb.artist # disc's artist
|
42
|
+
# puts freedb.length # disc's length in seconds
|
43
|
+
# puts freedb.tracks.size # number of tracks on the CD
|
44
|
+
# puts freedb.tracks[3]["title"] # title of the track 4
|
45
|
+
# # (indexing begin at 0)
|
46
|
+
# puts freedb.tracks[5]["length"] # length of track 6 in seconds
|
47
|
+
#
|
48
|
+
#
|
49
|
+
# = Testing
|
50
|
+
#
|
51
|
+
# In order to run all tests, you have to burn the "freedb CD Test" at
|
52
|
+
# http://ftp.freedb.org/pub/freedb/misc/freedb_testcd.zip
|
53
|
+
# and you must be connected to internet.
|
54
|
+
#
|
55
|
+
# Test::Unit library is used for tests. see http://testunit.talbott.ws/
|
56
|
+
#
|
57
|
+
# $ cd test/
|
58
|
+
# $ ruby test_all.rb
|
59
|
+
#
|
60
|
+
# = ToDo
|
61
|
+
# * CD-ROM access under Win32
|
62
|
+
#
|
63
|
+
# = Changelog
|
64
|
+
#
|
65
|
+
# [0.5 07/02/2003]
|
66
|
+
#
|
67
|
+
# * submission (http or mail) added
|
68
|
+
# * fetching from disk in Unix or Windows format added
|
69
|
+
# * "raw_response" attribute added (raw response from the server) [Fernando Arbeiza <arbeizaf@ono.com>]
|
70
|
+
# * "tracks" removed (however it can be redefined with 'tracks.collect { |h| h["title"] }'
|
71
|
+
# * "tracks_ext" renamed to "tracks"
|
72
|
+
# * "genre" renamed to "category"
|
73
|
+
# * "exact_genre" renamed to "genre"
|
74
|
+
# * "get_result(index)": index can be a String that represents the freedb category
|
75
|
+
# * FetchCGI: does not rely on cgi.rb anymore
|
76
|
+
# * documentation written with "rdoc"
|
77
|
+
#
|
78
|
+
#
|
79
|
+
# [0.4.2 10/01/2003]
|
80
|
+
#
|
81
|
+
# * Fixed a bug in track length computation [Fernando Arbeiza <arbeizaf@ono.com>]
|
82
|
+
#
|
83
|
+
#
|
84
|
+
# [0.4.1 13/10/2002]
|
85
|
+
#
|
86
|
+
# * Improved cddb parser [Akinori MUSHA <knu@iDaemons.org>]
|
87
|
+
# * Many bugs fixed in freedb_cdrom.c [Akinori MUSHA <knu@iDaemons.org>]
|
88
|
+
#
|
89
|
+
#
|
90
|
+
# [0.4 28/09/2002]
|
91
|
+
#
|
92
|
+
# * length attribute added
|
93
|
+
# * tracks_ext attribute added
|
94
|
+
# * fixed a bug in discid computation [Akinori MUSHA <knu@iDaemons.org>]
|
95
|
+
# * protocol level handling
|
96
|
+
# * test suite
|
97
|
+
# * code refactoring
|
98
|
+
# * file renaming (change nothing for end users)
|
99
|
+
#
|
100
|
+
#
|
101
|
+
# [0.3.1 30/08/2002]
|
102
|
+
#
|
103
|
+
# * genre read-only attribute added,
|
104
|
+
# * fixes syntax error due to a change in the Ruby interpreter. [Akinori MUSHA <knu@iDaemons.org>]
|
105
|
+
# * debianization
|
106
|
+
#
|
107
|
+
#
|
108
|
+
# [0.3 07/04/2002]
|
109
|
+
#
|
110
|
+
# * fetch() replaced by fetch_net() however i created an alias to fetch()
|
111
|
+
# * fetch_cgi() added
|
112
|
+
# * discid read-only attribute added
|
113
|
+
# * free() bug on FreeBSD fixed in get_cdrom() [Stephane D'Alu <sdalu@loria.fr>]
|
114
|
+
# * get_cdrom() buffer overrun fixed [OGAWA Takaya <t-ogawa@triaez.kaisei.org>]
|
115
|
+
#
|
116
|
+
#
|
117
|
+
# [0.2 19/01/2002]
|
118
|
+
#
|
119
|
+
# * Big cleaning of code.
|
120
|
+
# * Minimum code ( just the CDROM access ) written in C. Other is in pure Ruby.
|
121
|
+
# * Module now called 'freedb' instead of 'Freedb'.
|
122
|
+
# * Deleted specific exceptions. There is only one now (FreedbError).
|
123
|
+
#
|
124
|
+
#
|
125
|
+
# [0.1 18/12/2001]
|
126
|
+
#
|
127
|
+
# * Initial version
|
128
|
+
#
|
129
|
+
# License:: GPL
|
130
|
+
# Author:: Guillaume Pierronnet (mailto:moumar@netcourrier.com)
|
131
|
+
# Website:: http://davedd.free.fr/ruby-freedb/
|
132
|
+
|
133
|
+
# Raised on any kind of error related to ruby-freedb (cd-rom, network, protocol)
|
134
|
+
class FreedbError < StandardError ; end
|
135
|
+
|
136
|
+
class Freedb
|
137
|
+
|
138
|
+
VERSION = "0.6"
|
139
|
+
PROTO_LEVEL = 5
|
140
|
+
CD_FRAME = 75
|
141
|
+
VALID_CATEGORIES = [ "blues", "classical", "country", "data", "folk", "jazz", "misc", "newage", "reggae", "rock", "soundtrack" ]
|
142
|
+
|
143
|
+
# cddbid of the CD
|
144
|
+
attr_reader(:discid)
|
145
|
+
|
146
|
+
# the complete string used to query the database
|
147
|
+
attr_reader(:query)
|
148
|
+
|
149
|
+
# total length of the CD
|
150
|
+
attr_reader(:length)
|
151
|
+
|
152
|
+
# an array with all possible results for this CD
|
153
|
+
attr_reader(:results)
|
154
|
+
|
155
|
+
# string containing raw entry from freedb database
|
156
|
+
attr_reader(:raw_response)
|
157
|
+
|
158
|
+
# artist of the CD, must not be empty
|
159
|
+
attr_accessor(:artist)
|
160
|
+
|
161
|
+
# title of the CD, must not be empty
|
162
|
+
attr_accessor(:title)
|
163
|
+
|
164
|
+
# freedb category, must be one of +Freedb::VALID_CATEGORIES+
|
165
|
+
attr_accessor(:category)
|
166
|
+
|
167
|
+
# arbitraty string for the genre
|
168
|
+
attr_accessor(:genre)
|
169
|
+
|
170
|
+
# year of the cd (0 if not known)
|
171
|
+
attr_accessor(:year)
|
172
|
+
|
173
|
+
# an array of hashs containing following keys:
|
174
|
+
# "title" (must not be empty), "length", "ext" (for extended infos)
|
175
|
+
attr_accessor(:tracks)
|
176
|
+
|
177
|
+
# extended infos of the CD
|
178
|
+
attr_accessor(:ext_infos)
|
179
|
+
|
180
|
+
# If +is_query+ is false, the discid of the CD in +param+ is dumped.
|
181
|
+
# Else +param+ is considered as a valid freedb query string and is used directly.
|
182
|
+
def initialize(param = "/dev/cdrom", is_query = false)
|
183
|
+
@query =
|
184
|
+
if is_query
|
185
|
+
param
|
186
|
+
else
|
187
|
+
require "freedb_cdrom/freedb_cdrom"
|
188
|
+
get_cdrom(param)
|
189
|
+
end
|
190
|
+
q = @query.split(" ")
|
191
|
+
@discid = q[0]
|
192
|
+
nb_tracks = q[1].to_i
|
193
|
+
@length = q[-1].to_i
|
194
|
+
@offsets = q[2...-1] << @length*CD_FRAME
|
195
|
+
@offsets.collect! { |x| x.to_i }
|
196
|
+
|
197
|
+
@tracks = Array.new
|
198
|
+
|
199
|
+
nb_tracks.times { |i|
|
200
|
+
t = Hash.new
|
201
|
+
t["length"] = ((@offsets[i+1]-@offsets[i]).to_f/CD_FRAME).round
|
202
|
+
@tracks << t
|
203
|
+
}
|
204
|
+
@revision = 0
|
205
|
+
@raw_response = ""
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
# Query database using network
|
210
|
+
# Fill the +results+ array with multiple results.
|
211
|
+
# return nil if no match found
|
212
|
+
def fetch_net(server = "freedb.org", port = 8880)
|
213
|
+
@handler = FetchNet.new(server, port)
|
214
|
+
_fetch
|
215
|
+
end
|
216
|
+
|
217
|
+
alias :fetch :fetch_net
|
218
|
+
|
219
|
+
# Query database using CGI (HTTP) method.
|
220
|
+
# Fill the +results+ array with multiple results.
|
221
|
+
# return nil if no match found
|
222
|
+
def fetch_cgi(server = "www.freedb.org", port = 80, proxy = nil, proxy_port = nil, path = "/~cddb/cddb.cgi")
|
223
|
+
@handler = FetchCGI.new(server, port, proxy, proxy_port, path)
|
224
|
+
_fetch
|
225
|
+
end
|
226
|
+
|
227
|
+
# Query database using local directory. Set +win_format+ to true
|
228
|
+
# if the database has windows format (see freedb howto in "misc/" for details)
|
229
|
+
# return nil if no match found
|
230
|
+
def fetch_disk(directory, win_format = false)
|
231
|
+
@handler = FetchDisk.new(directory, win_format)
|
232
|
+
_fetch
|
233
|
+
end
|
234
|
+
|
235
|
+
# submit the current Freedb object using http
|
236
|
+
# +from+ is an email adress used to return submissions errors
|
237
|
+
# +submit_mode+ can be set to "test" to check submission validity (for developpers)
|
238
|
+
# return nil
|
239
|
+
def submit_http(from = "user@localhost", server = "freedb.org", port = 80, path = "/~cddb/submit.cgi", submit_mode = "submit")
|
240
|
+
require "net/http"
|
241
|
+
headers = {
|
242
|
+
"Category" => @category,
|
243
|
+
"Discid" => @discid,
|
244
|
+
"User-Email" => from,
|
245
|
+
"Submit-Mode" => submit_mode,
|
246
|
+
"Charset" => "ISO-8859-1",
|
247
|
+
"X-Cddbd-Note" => "Sent by ruby-freedb #{VERSION}"
|
248
|
+
}
|
249
|
+
Net::HTTP.start(server, port) { |http|
|
250
|
+
reply, body = http.post(path, submit_body(), headers)
|
251
|
+
if reply.code != 200
|
252
|
+
raise(FreedbError, "Bad response from server: '#{body.chop}'")
|
253
|
+
end
|
254
|
+
}
|
255
|
+
nil
|
256
|
+
end
|
257
|
+
alias :submit :submit_http
|
258
|
+
|
259
|
+
# submit the current Freedb object using smtp
|
260
|
+
# return +nil+
|
261
|
+
def submit_mail(smtp_server, from = "localuser@localhost", port = 25, to = "freedb-submit@freedb.org")
|
262
|
+
# +to+ can be set to "test-submit@freedb.org" to check validity (for
|
263
|
+
# developpers)
|
264
|
+
require "net/smtp"
|
265
|
+
header = {
|
266
|
+
"From" => from,
|
267
|
+
"To" => to,
|
268
|
+
"Subject" => "cddb #{@category} #{@discid}",
|
269
|
+
"MIME-Version" => "1.0",
|
270
|
+
"Content-Type" => "text/plain",
|
271
|
+
"Content-Transfer-Encoding" => "quoted-printable",
|
272
|
+
"X-Cddbd-Note" => "Sent by ruby-freedb #{VERSION}"
|
273
|
+
}
|
274
|
+
msg = ""
|
275
|
+
header.each { |k, v|
|
276
|
+
msg << "#{k}: #{v}\r\n"
|
277
|
+
}
|
278
|
+
msg << "\r\n"
|
279
|
+
msg << submit_body
|
280
|
+
Net::SMTP.start(smtp_server, port) { |smtp| smtp.send_mail(msg, from, to) }
|
281
|
+
nil
|
282
|
+
end
|
283
|
+
|
284
|
+
# Retrieve full result from the database.
|
285
|
+
# If +index+ is a Fixnum, get the +index+'th result in the +result+ array
|
286
|
+
# If +index+ is a String, +index+ is the freedb category
|
287
|
+
def get_result(index)
|
288
|
+
|
289
|
+
if index.is_a?(String)
|
290
|
+
idx = nil
|
291
|
+
@results.each_with_index { |r, i|
|
292
|
+
if r =~ /^#{index}/
|
293
|
+
idx = i
|
294
|
+
end
|
295
|
+
}
|
296
|
+
else
|
297
|
+
idx = index
|
298
|
+
end
|
299
|
+
|
300
|
+
md = /^\S+ [0-9a-fA-F]{8}/.match(@results[idx])
|
301
|
+
@handler.send_cmd("read", md[0])
|
302
|
+
|
303
|
+
# swallow the whole response into a hash
|
304
|
+
response = Hash.new
|
305
|
+
|
306
|
+
each_line(@handler) { |line|
|
307
|
+
@raw_response << line + "\n"
|
308
|
+
case line
|
309
|
+
when /^(\d+) (\S+)/, /^([A-Za-z0-9_]+)=(.*)/
|
310
|
+
key = $1.upcase
|
311
|
+
|
312
|
+
val = $2.gsub(/\\(.)/) {
|
313
|
+
case $1
|
314
|
+
when "t"
|
315
|
+
"\t"
|
316
|
+
when "n"
|
317
|
+
"\n"
|
318
|
+
else
|
319
|
+
$1
|
320
|
+
end
|
321
|
+
}
|
322
|
+
|
323
|
+
(response[key] ||= '') << val
|
324
|
+
when /^# Revision: (\d+)/
|
325
|
+
@revision = $1.to_i
|
326
|
+
end
|
327
|
+
}
|
328
|
+
@category = response['210']
|
329
|
+
@genre = response['DGENRE']
|
330
|
+
@year = response['DYEAR'].to_i
|
331
|
+
@ext_infos = response['EXTD']
|
332
|
+
|
333
|
+
# Use a regexp instead of a bare string to avoid ruby >= 1.7 warning
|
334
|
+
@artist, @title = response['DTITLE'].split(/ \/ /, 2)
|
335
|
+
# A self-titled album may not have a title part
|
336
|
+
@title ||= @artist
|
337
|
+
|
338
|
+
response.each { |key, val|
|
339
|
+
case key
|
340
|
+
when /^4\d\d$/
|
341
|
+
raise(FreedbError, val)
|
342
|
+
when /^TTITLE(\d+)$/
|
343
|
+
i = $1.to_i
|
344
|
+
@tracks[i]["title"] = val
|
345
|
+
when /^EXTT(\d+)$/
|
346
|
+
i = $1.to_i
|
347
|
+
@tracks[i]["ext"] = val
|
348
|
+
end
|
349
|
+
}
|
350
|
+
self
|
351
|
+
end
|
352
|
+
|
353
|
+
# close all pending connections
|
354
|
+
def close
|
355
|
+
@handler.close if @handler
|
356
|
+
@handler = nil
|
357
|
+
end
|
358
|
+
|
359
|
+
private
|
360
|
+
|
361
|
+
def _fetch
|
362
|
+
@handler.gets #banner
|
363
|
+
#@handler.send_cmd("hello", "#{ENV['USER']} #{`hostname`.chop} ruby-freedb #{VERSION}")
|
364
|
+
@handler.send_cmd("hello", "user localhost ruby-freedb #{VERSION}")
|
365
|
+
if @handler.gets.chop =~ /^4\d\d (.+)/ #welcome
|
366
|
+
raise(FreedbError, $1)
|
367
|
+
end
|
368
|
+
set_proto_level(PROTO_LEVEL)
|
369
|
+
@handler.send_cmd("query", @query)
|
370
|
+
resp = @handler.gets.chop
|
371
|
+
@results = []
|
372
|
+
case resp
|
373
|
+
when /^200 (.+)/ #single result
|
374
|
+
@results << $1
|
375
|
+
when /^211/ #multiple results
|
376
|
+
each_line(@handler) { |l|
|
377
|
+
@results << l
|
378
|
+
}
|
379
|
+
when /^202/ #no match found
|
380
|
+
return nil
|
381
|
+
end
|
382
|
+
self
|
383
|
+
end
|
384
|
+
|
385
|
+
def set_proto_level(l)
|
386
|
+
if l < 1
|
387
|
+
raise(FreedbError, "Server doesn't support level 1!")
|
388
|
+
end
|
389
|
+
@handler.send_cmd("proto", l.to_s)
|
390
|
+
if @handler.gets =~ /^501/
|
391
|
+
set_proto_level(l-1)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def each_line(handler)
|
396
|
+
until (l = handler.gets) =~ /^\./
|
397
|
+
yield l.chop
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def submit_body
|
402
|
+
if @tracks.detect { |h| h["title"].empty? }
|
403
|
+
raise(FreedbError, "Some tracks title are empty")
|
404
|
+
elsif not VALID_CATEGORIES.include?(@category)
|
405
|
+
raise(FreedbError, "Category is not valid")
|
406
|
+
elsif @artist.empty?
|
407
|
+
raise(FreedbError, "Artist field must not be empty")
|
408
|
+
elsif @title.empty?
|
409
|
+
raise(FreedbError, "Title field must not be empty")
|
410
|
+
end
|
411
|
+
body = <<EOF
|
412
|
+
# xmcd CD database file
|
413
|
+
#
|
414
|
+
# Track frame offsets:
|
415
|
+
EOF
|
416
|
+
@offsets[0..-2].each { |o|
|
417
|
+
body << "# #{o}\n"
|
418
|
+
}
|
419
|
+
body << <<EOF
|
420
|
+
#
|
421
|
+
# Disc length: #{@length} seconds
|
422
|
+
#
|
423
|
+
# Revision: #{@revision}
|
424
|
+
# Submitted via: ruby-freedb #{VERSION}
|
425
|
+
#
|
426
|
+
DISCID=#{discid}
|
427
|
+
DTITLE=#{artist.gsub(/ \/ /, "/")} / #{title.gsub(/ \/ /, "/")}
|
428
|
+
DYEAR=#{@year.to_i == 0 ? "" : "%04d" % @year}
|
429
|
+
DGENRE=#{(@genre || "").split(" ").collect do |w| w.capitalize end.join(" ")}
|
430
|
+
EOF
|
431
|
+
@tracks.each_with_index { |t, i|
|
432
|
+
body << "TTITLE#{i}=#{escape(t["title"])}\n"
|
433
|
+
}
|
434
|
+
body << "EXTD=#{escape(@ext_infos)}\n"
|
435
|
+
@tracks.each_with_index { |t, i|
|
436
|
+
body << "EXTT#{i}=#{escape(t["ext"])}\n"
|
437
|
+
}
|
438
|
+
body << "PLAYORDER=\n"
|
439
|
+
body
|
440
|
+
end
|
441
|
+
|
442
|
+
#FIXME optimize that, this is UGLY!
|
443
|
+
def escape(str)
|
444
|
+
str.gsub(/\t/, '\t').gsub(/\n/, '\n').gsub(/\\/, "\\\\\\")
|
445
|
+
end
|
446
|
+
|
447
|
+
class FetchCGI #:nodoc:
|
448
|
+
|
449
|
+
def initialize(server, port, proxy, proxy_port, path)
|
450
|
+
require "net/http"
|
451
|
+
@session = Net::HTTP.new(server, port, proxy, proxy_port)
|
452
|
+
@path = path
|
453
|
+
@proto_level = 1
|
454
|
+
@res = []
|
455
|
+
end
|
456
|
+
|
457
|
+
def send_cmd(cmd, args)
|
458
|
+
if cmd == "hello"
|
459
|
+
@hello_str = "hello=" + cgi_escape(args)
|
460
|
+
@res << "201" #necessary for the next call to gets
|
461
|
+
elsif cmd == "proto"
|
462
|
+
@proto_level = args
|
463
|
+
@res << "200" #necessary for the next call to gets
|
464
|
+
else
|
465
|
+
request = "?cmd=cddb+#{cmd}+#{cgi_escape(args)}&#{@hello_str}&proto=#{@proto_level}"
|
466
|
+
resp, data = @session.get(@path + request)
|
467
|
+
raise(FreedbError, "Bad HTTP response: #{resp.code} #{resp.message} while querying server") unless resp.code == "200"
|
468
|
+
@res.concat( data.split("\n") )
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
def gets
|
473
|
+
#(@res.nil? ? "" : @res.shift)
|
474
|
+
@res.shift
|
475
|
+
end
|
476
|
+
|
477
|
+
def close; end
|
478
|
+
|
479
|
+
private
|
480
|
+
|
481
|
+
#stolen from cgi.rb
|
482
|
+
def cgi_escape(str)
|
483
|
+
str.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
|
484
|
+
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
485
|
+
end.tr(' ', '+')
|
486
|
+
end
|
487
|
+
|
488
|
+
end
|
489
|
+
|
490
|
+
class FetchNet #:nodoc:
|
491
|
+
|
492
|
+
def initialize(server, port)
|
493
|
+
require "socket"
|
494
|
+
@socket = TCPSocket.new(server, port)
|
495
|
+
end
|
496
|
+
|
497
|
+
def send_cmd(cmd, args)
|
498
|
+
@socket.puts("cddb #{cmd} #{args}")
|
499
|
+
end
|
500
|
+
|
501
|
+
def gets
|
502
|
+
@socket.gets
|
503
|
+
end
|
504
|
+
|
505
|
+
def close
|
506
|
+
@socket.close
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
class FetchDisk #:nodoc:
|
511
|
+
def initialize(basedir, win_format)
|
512
|
+
raise(FreedbError, "#{basedir} is not a directory") if not File.directory?(basedir)
|
513
|
+
@basedir = File.expand_path(basedir)
|
514
|
+
@win = win_format
|
515
|
+
@res = ["201"]
|
516
|
+
|
517
|
+
# used to store results to avoid rescanning files when getting result
|
518
|
+
# hash key if the name of the category
|
519
|
+
@temp_results = {}
|
520
|
+
|
521
|
+
# storing full directory and name of differents categories ( each
|
522
|
+
# represented by a directory)
|
523
|
+
@categs = Dir.entries(@basedir).collect do |d|
|
524
|
+
dir = File.join(@basedir, d)
|
525
|
+
if File.directory?(dir) and d !~ /^\.\.?$/
|
526
|
+
@temp_results[d] = []
|
527
|
+
[dir, d]
|
528
|
+
end
|
529
|
+
end
|
530
|
+
@categs.compact!
|
531
|
+
end
|
532
|
+
|
533
|
+
def send_cmd(cmd, args)
|
534
|
+
case cmd
|
535
|
+
when "hello"
|
536
|
+
a = args.split(" ")
|
537
|
+
@res << "200 Hello and welcome #{a[0]}@#{a[1]} running #{a[2]} #{a[3]}."
|
538
|
+
when "query"
|
539
|
+
discid = args.split(" ")[0]
|
540
|
+
match = []
|
541
|
+
####################
|
542
|
+
#WINDOWS DB FORMAT
|
543
|
+
####################
|
544
|
+
if @win
|
545
|
+
good_line = "#FILENAME=#{discid}\n"
|
546
|
+
find_files_win(discid).each do |f, categ|
|
547
|
+
content = []
|
548
|
+
catch(:finish) do
|
549
|
+
started = false
|
550
|
+
File.foreach(f) do |line|
|
551
|
+
if line == good_line
|
552
|
+
started = true
|
553
|
+
elsif started
|
554
|
+
if line =~ /^#FILENAME=/
|
555
|
+
match << categ + " " + discid + " " + disc_name(content)
|
556
|
+
@temp_results[categ] = content
|
557
|
+
throw(:finish)
|
558
|
+
end
|
559
|
+
content << line
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
563
|
+
end
|
564
|
+
####################
|
565
|
+
#CLASSIC DB FORMAT
|
566
|
+
####################
|
567
|
+
else
|
568
|
+
@categs.each do |dir, categ|
|
569
|
+
filename = File.join(dir, discid)
|
570
|
+
#if file exists, we've got a match
|
571
|
+
if File.file?(filename)
|
572
|
+
@temp_results[categ] = File.readlines(filename)
|
573
|
+
match << categ + " " + discid + " " + disc_name(@temp_results[categ])
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
577
|
+
if match.size == 1
|
578
|
+
@res << "200 #{match[0]}"
|
579
|
+
elsif match.size > 1
|
580
|
+
@res << "211 Multiple match found"
|
581
|
+
match.each { |m|
|
582
|
+
@res << m
|
583
|
+
}
|
584
|
+
@res << "."
|
585
|
+
else
|
586
|
+
@res << "202 No match found"
|
587
|
+
end
|
588
|
+
when "read"
|
589
|
+
categ, discid = args.split(" ")
|
590
|
+
@res << "210 #{categ} #{discid}"
|
591
|
+
if @win
|
592
|
+
lines = @temp_results[categ]
|
593
|
+
else
|
594
|
+
filename = File.join(@basedir, categ, discid)
|
595
|
+
lines = File.readlines(filename)
|
596
|
+
end
|
597
|
+
@res.concat(lines.collect { |l| l.chop })
|
598
|
+
@res << "."
|
599
|
+
when "proto"
|
600
|
+
@res << "200 CDDB protocol level: current #{PROTO_LEVEL}, supported #{PROTO_LEVEL}"
|
601
|
+
else
|
602
|
+
@res << "501"
|
603
|
+
#$stderr.puts "#{self.class} unsupported command #{cmd}"
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
def gets
|
608
|
+
@res.shift + "\n"
|
609
|
+
end
|
610
|
+
|
611
|
+
def close; end
|
612
|
+
|
613
|
+
private
|
614
|
+
|
615
|
+
def disc_name(content)
|
616
|
+
disc_name = nil
|
617
|
+
content.each { |line|
|
618
|
+
if md = /DTITLE=(.+)/.match(line)
|
619
|
+
disc_name = $1
|
620
|
+
end
|
621
|
+
}
|
622
|
+
disc_name
|
623
|
+
end
|
624
|
+
|
625
|
+
def find_files_win(discid)
|
626
|
+
ret = []
|
627
|
+
head = discid[0, 2]
|
628
|
+
@categs.each do |dir, categ|
|
629
|
+
Dir.foreach(dir) do |filename|
|
630
|
+
if filename =~ /^([0-9a-fA-f]{2})to([0-9a-fA-F]{2})$/ and head >= $1 and head <= $2
|
631
|
+
ret << [File.join(dir, filename), categ]
|
632
|
+
end
|
633
|
+
end
|
634
|
+
end
|
635
|
+
ret
|
636
|
+
end
|
637
|
+
end
|
638
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-freedb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.6'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Guillaume Pierronnet
|
8
|
+
- Patric Mueller
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-07-10 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A Ruby library which provides dumping CD info, fetching from and submitting
|
15
|
+
data to cddb/freedb servers.
|
16
|
+
email: bhaak@gmx.net
|
17
|
+
executables: []
|
18
|
+
extensions:
|
19
|
+
- ext/freedb_cdrom/extconf.rb
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- ext/freedb_cdrom/extconf.rb
|
23
|
+
- ext/freedb_cdrom/freedb_cdrom.c
|
24
|
+
- lib/freedb.rb
|
25
|
+
homepage: http://ruby-freedb.rubyforge.org/
|
26
|
+
licenses:
|
27
|
+
- Artistic
|
28
|
+
- GPL-2
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.2.2
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: A Ruby library for accessing cddb/freedb servers.
|
50
|
+
test_files: []
|