our
$VERSION
= v0.14;
my
%names
= (
service
=> {
'wikidata'
=>
'198bc92a-be09-42d2-bf96-20a177294b79'
,
'fellig'
=>
'43e7f8fe-2b90-4a5d-88e2-b1d46856d942'
,
'youtube'
=>
'de49b663-ff54-428b-ac56-d1950fb3cec7'
,
'youtube-nocookie'
=>
'c7acc624-de92-4480-8a21-31186e8bef54'
,
'dropbox'
=>
'f8022569-fdc0-4922-8a95-3de51be087aa'
,
'0wx'
=>
'b279726c-a349-4d87-b87c-929319a20b3e'
,
'e621'
=>
'9bde88c4-1784-4756-b009-6111b4a69f96'
,
'dnb'
=>
'1c5eb5fb-3f2a-4a5a-9b28-9fba163873a0'
,
'britishmuseum'
=>
'ac0cad64-4bf2-4924-a855-bc4147f6cdb3'
,
'musicbrainz'
=>
'fcb39c86-34f6-481c-9bb7-63c4a7c2256b'
,
'wikimedia-commons'
=>
'a283b6cb-c8c5-4b5d-8a58-e0327e087e50'
,
'wikipedia'
=>
'1262f7fe-2d98-42aa-9ed5-5cc5182fc4f4'
,
'noembed.com'
=>
'66c2ac78-936b-4241-b041-567080db3f6a'
,
'osm'
=>
'fdb14a39-f175-4aba-bcec-53c4683b72bd'
,
'overpass'
=>
'5350885e-92f5-4aee-b72e-dd9d95c6700a'
,
'xkcd'
=>
'6d90e7e2-c193-4e96-8d0a-c9a3d42beecf'
,
'Data::URIID'
=>
'65a5000f-c37f-4fa1-9ad0-c9682fcd8756'
,
'Data::Identifier'
=>
'1d1e84e9-dac5-43d6-9ff9-a72fb7de38d5'
,
'viaf'
=>
'b542f123-b304-4f60-a2a9-15a0cc62e25d'
,
'europeana'
=>
'2ddf371f-20b5-4fdb-99d5-934b212ed596'
,
'open-library'
=>
'173f7237-9ca0-490d-8a98-6a04c386769a'
,
'ngv'
=>
'01aa1e39-6d90-41c6-a010-f3850844f2e1'
,
'geonames'
=>
'2860d918-ac49-42a1-818d-68abd84972b3'
,
'find-a-grave'
=>
'30deaf5b-470b-46da-8af1-6e5174d0eaf4'
,
'nla'
=>
'0715561c-0189-4c1f-99bf-21cc6746f5ee'
,
'agsa'
=>
'a61dda0f-b914-496a-b473-2a333b9f0f9f'
,
'amc'
=>
'fec16f49-a9fe-4d89-bad2-7dbb44860e83'
,
'a-p-and-p'
=>
'91a4981f-c1c7-4136-9e2f-39f2cd2eda7f'
,
'tww'
=>
'aafdcd22-828b-413e-be0c-ed9a92d941db'
,
'factgrid'
=>
'9a6b8382-c004-458a-bf2a-68f03d863282'
,
'grove-art-online'
=>
'be8b12e5-b32d-4b89-9301-84827a79589e'
,
'wikitree'
=>
'70b9de08-2b73-4c0d-91d2-e89561cf94d2'
,
'doi'
=>
'60387716-fa98-4c92-ae2b-7f4496d6f9be'
,
'iconclass'
=>
'75cbefbb-e622-4b72-9829-348f3986d709'
,
'iana'
=>
'f11657cc-95da-4eae-95fc-62d16fecf473'
,
'uriid'
=>
'772aa1ed-9a3a-4806-94a1-42cbc0e9f962'
,
'oidref'
=>
'b5a63482-f92c-4ed5-8ec3-49caa0bafa66'
,
'furaffinity'
=>
'978a4622-2a87-4c67-b9bf-c1b1e5d69b05'
,
'imgur'
=>
'e6c5f855-221a-4c48-9f31-cf0a852140da'
,
'notalwaysright'
=>
'50882a9d-b405-4d5d-8068-0d38fb0b2f90'
,
'fefe'
=>
'80195e74-7afb-435b-8c76-3da8b343f235'
,
'schemaorg'
=>
'b1db320a-ab00-4650-ab54-f162b52bca08'
,
'purlorg'
=>
'53f8ea47-1de3-4562-9459-5f093782ae10'
,
'ruthede'
=>
'6c7dba44-dd07-4928-874e-f076e98cc96b'
,
},
type
=> {
'uuid'
=>
'8be115d2-dc2f-4a98-91e1-a6e3075cbc31'
,
'oid'
=>
'd08dc905-bbf6-4183-b219-67723c3c8374'
,
'uri'
=>
'a8d1637d-af19-49e9-9ef8-6bc1fbcf6439'
,
'tagname'
=>
'bfae7574-3dae-425d-89b1-9c087c140c23'
,
'wikidata-identifier'
=>
'ce7aae1e-a210-4214-926a-0ebca56d77e3'
,
'musicbrainz-identifier'
=>
'95bd826b-bd3e-4b40-b16a-aa20c9f673e4'
,
'british-museum-term'
=>
'310776dc-1433-4623-9ffa-42d038d400a4'
,
'gnd-identifier'
=>
'893a7d5c-124c-4ad6-9a56-0ea8be50b536'
,
'fellig-box-number'
=>
'c036d4d9-d983-4322-917c-acbf6133df64'
,
'fellig-identifier'
=>
'90ecb0c5-f99a-4702-8575-430247de8f48'
,
'youtube-video-identifier'
=>
'0d88a8f0-0fce-41ae-beef-88d74d83eb32'
,
'e621tagtype'
=>
'da72fa90-5990-46b4-b4ca-05eaf68170a5'
,
'wikimedia-commons-identifier'
=>
'a6b1a981-48a0-445e-adc7-11df14e91769'
,
'e621-post-identifier'
=>
'4a7fc2e2-854b-42ec-b24f-c7fece371865'
,
'e621-pool-identifier'
=>
'a0a4fae2-be6f-4a51-8326-6110ba845a16'
,
'osm-node'
=>
'6c09afad-0109-4a05-a430-f3bdade19c24'
,
'osm-way'
=>
'01da1735-25b3-4560-9c8c-186e42dd8904'
,
'osm-relation'
=>
'bdd9b297-e0a8-427e-8487-83f600226f5b'
,
'xkcd-num'
=>
'943315e7-9efd-41df-b3f5-4a42b93df46d'
,
'factgrid-identifier'
=>
'd576b9d1-47d4-43ae-b7ec-bbea1fe009ba'
,
'viaf-identifier'
=>
'685c7871-2965-4f0a-ac63-d6bacd1e575e'
,
'open-library-identifier'
=>
'435f6b8c-cae4-4dcf-816a-1225fc35108f'
,
'unesco-thesaurus-identifier'
=>
'3ff707af-1f72-4e1f-a81b-7871fb6079e1'
,
'isni'
=>
'a6de24d2-95a2-4577-870c-31ad10339f22'
,
'aev-identifier'
=>
'e9c13254-831f-474c-8881-31012ca45a72'
,
'europeana-entity-identifier'
=>
'a1cffa6b-6b78-4b11-9a6c-3673ec25c489'
,
'ngv-artist-identifier'
=>
'8fb7807b-c15a-4ae1-8f15-4b3d8e4f5cef'
,
'ngv-artwork-identifier'
=>
'4d25c32b-a169-40f5-be88-3d609b7d05ff'
,
'geonames-identifier'
=>
'02e34fcc-cf5e-445a-ba54-bf6df8ae036a'
,
'find-a-grave-identifier'
=>
'39ea7c88-3fc2-4a01-89f9-547f451764f7'
,
'libraries-australia-identifier'
=>
'22a80a6d-0c69-41f5-b5be-6c889f8e601b'
,
'agsa-creator-identifier'
=>
'fb3bac19-7d4e-4995-9ef0-08dbcea7f340'
,
'amc-artist-identifier'
=>
'0b907ca8-a84f-4780-b708-910a858228a8'
,
'a-p-and-p-artist-identifier'
=>
'5bafcbd4-5fcf-4823-848f-7eab8175a80c'
,
'nla-trove-people-identifier'
=>
'0edc2854-37bf-4562-a05b-ac4113ead938'
,
'tww-artist-identifier'
=>
'b49d88ba-1b61-4f13-b5c9-73a09ffb2b3f'
,
'grove-art-online-identifier'
=>
'80c548f6-4d23-43c1-ab50-b4546319c752'
,
'wikitree-person-identifier'
=>
'a6f7d17a-ced2-4cf7-8ce7-fcb4a98f7aa0'
,
'doi'
=>
'931f155e-5a24-499b-9fbb-ed4efefe27fe'
,
'iconclass-identifier'
=>
'241348a8-c5d0-4473-9ec1-de7c2ba00fbb'
,
'media-subtype-identifier'
=>
'c1166bf7-c4ab-40ad-9a92-a55103bec509'
,
'gtin'
=>
'82d529be-0f00-4b4f-a43f-4a22de5f5312'
,
'small-identifier'
=>
'f87a38cb-fd13-4e15-866c-e49901adbec5'
,
'language-tag-identifier'
=>
'd0a4c6e2-ce2f-4d4c-b079-60065ac681f1'
,
'chat-0-word-identifier'
=>
'2c7e15ed-aa2f-4e2f-9a1d-64df0c85875a'
,
'furaffinity-post-identifier'
=>
'b8dd10ec-d46b-4316-b3f3-2bc28cff9d35'
,
'imgur-post-identifier'
=>
'f2425e42-0083-4205-aa0b-2005f1fa62a3'
,
'notalwaysright-post-identifier'
=>
'700a0082-0201-46f1-b0a1-37e2caf76cc2'
,
'fefe-blog-post-identifier'
=>
'9ad1edae-f08e-4605-b9f9-a1d3894c290a'
,
'ruthede-comic-post-identifier'
=>
'2db1003d-dbf4-47bf-bfe4-7874c5bf0263'
,
},
action
=> {
'documentation'
=>
'b75354b2-a43b-44d9-99d5-9c0ec4fa5287'
,
'manage'
=>
'01fc3e42-7b5c-403e-94fb-a4fa7990c0ed'
,
'render'
=>
'b608ad23-e61a-4ab3-a1ca-f3f4e269b03b'
,
'embed'
=>
'0fecb446-89a9-4b0c-a7db-e83b5acec419'
,
'info'
=>
'478bc202-51ac-4c5e-9f9a-38e233a42dfb'
,
'edit'
=>
'e775b770-90eb-4b2f-9b78-26021688722d'
,
'fetch'
=>
'4ab02627-c452-4f4e-a9c0-4bde8f1e6b0e'
,
'file-fetch'
=>
'a3b66e23-15f2-4bc6-b22e-8f072ba839e7'
,
'stream-fetch'
=>
'4060a966-9fae-4d43-9006-2288b58afabb'
,
'metadata'
=>
'6f1c921b-e0bb-4449-911f-a00719e91a1e'
,
},
);
my
%service_lists
= (
ALL
=> [
keys
%{
$names
{service}}],
LOCAL
=> [
qw(Data::URIID Data::Identifier)
],
REMOTE
=> [
qw(wikidata factgrid wikimedia-commons fellig noembed.com osm overpass xkcd doi iconclass e621 furaffinity imgur)
],
wmf
=> [
qw(wikidata wikimedia-commons wikipedia)
],
osm
=> [
qw(osm overpass)
],
friendly
=> [
qw(fellig uriid)
],
uafriendly
=> [
qw(@friendly)
],
);
my
%ises
;
foreach
my
$class
(
keys
%names
) {
$ises
{
$class
} = {
map
{
$names
{
$class
}{
$_
} =>
$_
}
keys
%{
$names
{
$class
}}
};
}
{
my
$found
;
do
{
$found
=
undef
;
foreach
my
$content
(
values
%service_lists
) {
my
@newlist
;
foreach
my
$entry
(@{
$content
}) {
if
(
$entry
=~ /^\@(.+)$/) {
push
(
@newlist
, @{
$service_lists
{$1}});
$found
= 1;
}
else
{
push
(
@newlist
,
$entry
);
}
}
@{
$content
} =
@newlist
;
}
}
while
(
$found
);
}
sub
new {
my
(
$pkg
,
%opts
) =
@_
;
my
$self
=
bless
\
%opts
,
$pkg
;
if
(
defined
(
my
$services_online
=
delete
$self
->{services_online})) {
foreach
my
$service_name
(
$self
->match_services(
$services_online
)) {
$self
->service(
$service_name
)->online(1);
}
}
return
$self
;
}
sub
lookup {
my
(
$self
,
$type
,
$uri
) =
@_
;
my
%secondary
;
if
(
scalar
(
@_
) == 2) {
(
$type
,
$uri
) = (
'auto'
=>
$type
);
}
croak
'Passed undef as URI'
unless
defined
$uri
;
if
(blessed(
$uri
) && !
$uri
->isa(
'URI'
)) {
if
(
defined
(
my
$displaycolour
=
eval
{
$uri
->displaycolour})) {
if
(
eval
{
$displaycolour
->isa(
'Data::URIID::Colour'
)}) {
$secondary
{attributes} //= {};
$secondary
{attributes}{displaycolour} //= {
'*'
=>
$displaycolour
};
}
}
if
(
defined
(
my
$displayname
=
eval
{
$uri
->displayname(
default
=>
undef
,
no_defaults
=> 1)})) {
$secondary
{attributes} //= {};
$secondary
{attributes}{displayname} //= {
'*'
=>
$displayname
};
}
if
(
$uri
->isa(
'Data::URIID::Result'
)) {
$uri
=
$uri
->url;
}
elsif
(
$uri
->isa(
'Data::Identifier'
)) {
$type
=
$uri
->type->uuid;
$uri
=
$uri
->id;
}
elsif
(
index
(blessed(
$uri
), __PACKAGE__) == 0 &&
$uri
->can(
'ise'
)) {
(
$type
,
$uri
) = (
ise
=>
$uri
->ise);
}
elsif
(
$uri
->isa(
'Mojo::URL'
)) {
$uri
= URI->new(
"$uri"
);
}
elsif
(
$uri
->isa(
'File::FStore::File'
)) {
my
$data
=
$uri
->get;
$type
=
'ise'
;
$uri
=
$uri
->get(
properties
=>
'contentise'
);
$secondary
{attributes} //= {};
$secondary
{attributes}{final_file_size} //= {
'*'
=>
$data
->{properties}{size}}
if
defined
$data
->{properties}{size};
$secondary
{attributes}{roles} = [[Data::Identifier->new(
sid
=> 17)]];
$secondary
{digest} =
$data
->{digests};
}
else
{
croak
'Invalid type of object passed'
;
}
}
unless
(blessed
$uri
) {
if
(
ref
$type
) {
my
URI
$u
;
if
(
$type
->isa(
'URI'
) ||
$type
->isa(
'Mojo::URL'
)) {
$type
=
$self
->lookup(
$type
);
}
$type
=
$type
->ise;
$u
->path_segments(
''
,
$type
,
$uri
);
$uri
=
$u
;
}
else
{
if
(
$type
eq
'qrcode'
) {
$uri
=~ s
$type
=
'auto'
;
}
if
(
$type
eq
'auto'
||
$type
eq
'ise'
) {
if
(
$uri
=~ /^[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$/) {
$uri
=
'urn:uuid:'
.
$uri
;
}
elsif
(
$uri
=~ /^[1-3](?:\.(?:0|[1-9][0-9]*))+$/) {
$uri
=
'urn:oid:'
.
$uri
;
}
}
elsif
(
$type
=~ /^[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$/) {
$u
->path_segments(
''
,
$type
,
$uri
);
$uri
=
$u
;
}
$uri
= URI->new(
$uri
);
}
}
croak
'Passed a non-URI object'
unless
$uri
->isa(
'URI'
);
croak
'URI is not absolute'
unless
defined
(
$uri
->scheme) &&
length
(
$uri
->scheme);
return
Data::URIID::Result->new(
uri
=>
$uri
,
extractor
=>
$self
,
secondary
=> \
%secondary
);
}
sub
online {
my
(
$self
,
$new_value
) =
@_
;
if
(
scalar
(
@_
) == 2) {
$self
->{online} = !!
$new_value
;
}
return
$self
->{online};
}
sub
default_online {
my
(
$self
,
$new_value
) =
@_
;
if
(
scalar
(
@_
) == 2) {
$self
->{default_online} = !!
$new_value
;
}
return
$self
->{default_online};
}
sub
language_tags {
my
(
$self
,
@new_value
) =
@_
;
if
(
scalar
(
@new_value
)) {
$self
->{language_tags} = \
@new_value
;
}
$self
->{language_tags} //= [I18N::LangTags::implicate_supers(I18N::LangTags::Detect::detect())];
return
@{
$self
->{language_tags}};
}
sub
_get_language_tags {
my
(
$self
,
%opts
) =
@_
;
if
(
defined
(
my
$language_tags
=
$opts
{language_tags})) {
return
@{
$language_tags
}
if
ref
(
$language_tags
) eq
'ARRAY'
;
return
I18N::LangTags::implicate_supers(I18N::LangTags::extract_language_tags(
$language_tags
));
}
return
$self
->language_tags;
}
sub
_ua {
my
(
$self
) =
@_
;
return
$self
->{ua} //=
do
{
my
$ua
= LWP::UserAgent->new(
agent
=>
$self
->{agent});
my
$x
= 1001;
$ua
->default_header(
'Accept-Language'
=>
join
(
', '
,
map
{
sprintf
(
'%s; q=%.3f'
,
$_
, --
$x
/1000)}
$self
->language_tags));
$ua
;
};
}
sub
known {
my
(
$self
,
$class
,
%opts
) =
@_
;
my
@list
;
if
(
$class
eq
':all'
) {
foreach
my
$c
(
keys
%names
) {
push
(
@list
,
values
%{
$names
{
$c
}});
}
}
else
{
@list
=
values
%{
$names
{
$class
//
''
} // croak
'Invalid class'
};
}
if
(
defined
(
my
$as
=
$opts
{as})) {
if
(
$as
eq
'ise'
) {
}
elsif
(
$as
eq
'uuid'
) {
foreach
my
$e
(
@list
) {
croak
'Cannot return requested data: as=uuid not supported on all elements of list. Try as=ise'
unless
$e
=~ Data::URIID::Result->RE_UUID;
}
}
elsif
(
$as
eq
'Data::Identifier'
) {
@list
=
map
{Data::Identifier->new(
ise
=>
$_
)}
@list
;
}
}
return
@list
;
}
sub
name_to_ise {
my
(
$self
,
$class
,
$name
) =
@_
;
return
$name
if
$self
->is_ise(
$name
);
if
(blessed(
$name
)) {
if
(
index
(blessed(
$name
), __PACKAGE__) == 0 &&
$name
->can(
'ise'
)) {
return
$name
->ise;
}
elsif
(
$name
->isa(
'Data::Identifier'
)) {
return
$name
->ise;
}
}
return
$names
{
$class
//
''
}{
$name
//
''
} // croak
sprintf
(
'Invalid class or name: %s: %s'
,
$class
//
'<undef>'
,
$name
//
'<undef>'
);
}
sub
ise_to_name {
my
(
$self
,
$class
,
$ise
) =
@_
;
return
$ises
{
$class
//
''
}{
$ise
//
''
} // croak
'Invalid class or ISE'
;
}
sub
is_ise {
my
(
$self
,
$str
) =
@_
;
return
$str
=~ /^[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$/ ||
$str
=~ /^[1-3](?:\.(?:0|[1-9][0-9]*))+$/ ||
$str
=~ /^[a-zA-Z][a-zA-Z0-9\+\.\-]+:[^:]/;
}
sub
service {
my
(
$self
,
$service
) =
@_
;
my
$cache
=
$self
->{service_cache} //= {};
$service
=
$service
->ise
if
ref
$service
;
$service
=
$self
->name_to_ise(
service
=>
$service
);
croak
'Not a known service: '
.
$service
unless
defined
$ises
{service}{
$service
};
return
$cache
->{
$service
} //= Data::URIID::Service->new(
extractor
=>
$self
,
ise
=>
$service
,
online
=>
$self
->default_online,
);
}
sub
match_services {
my
(
$self
,
@list
) =
@_
;
my
%selected
;
@list
=
map
{
split
/\s*[,:]\s*|\s+/}
map
{
ref
(
$_
) eq
'ARRAY'
? @{
$_
} :
$_
}
@list
;
foreach
my
$entry
(
@list
) {
my
(
$neg
,
$prefix
,
$name
) =
$entry
=~ /^(\!?)(\@?)(.+)$/;
my
@sublist
;
if
(
$prefix
eq
'@'
) {
@sublist
= @{
$service_lists
{
$name
} // croak
'Invalid service list: '
.
$name
};
}
else
{
$name
=
$name
->ise
if
ref
$name
;
$name
=
$self
->name_to_ise(
service
=>
$name
);
$name
=
$self
->ise_to_name(
service
=>
$name
);
@sublist
= (
$name
);
}
if
(
$neg
eq
''
) {
$selected
{
$_
} = 1
foreach
@sublist
;
}
else
{
$selected
{
$_
} = 0
foreach
@sublist
;
}
}
return
grep
{
$selected
{
$_
}}
keys
%selected
;
}
1;