xeno-crufto/xeno-crufto

579 lines
16 KiB
Python
Executable File

#!/usr/bin/env python3
"""
xeno-crufto
Copyright 2023, Jeff Moe <moe@spacecruft.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import argparse
import json
import requests
import urllib.parse
parser = argparse.ArgumentParser(
prog='xeno-crufto',
description='Scripts for working with the xeno-canto sound archive.',
)
parser.add_argument(
"--api",
help="API URL (default https://xeno-canto.org/api/2/recordings)",
type=str,
required=False,
default="https://xeno-canto.org/api/2/recordings",
)
parser.add_argument(
"--dry-run",
help="Print URI but don't run query (default False)",
action=argparse.BooleanOptionalAction,
type=bool,
required=False,
default=False,
)
parser.add_argument(
"--query",
help="Arbitrary query string (default None)",
type=str,
required=False,
default="",
)
"""
also: an array with the identified background species in the recording
animal-seen: was the recorded animal seen?
auto: automatic (non-supervised) recording?
bird-seen: despite the field name (which was kept to ensure backwards compatibility), this field indicates whether the recorded animal was seen
cnt: the country where the recording was made
date: the date that the recording was made
dvc: recording device used
en: the English name of the species
file-name: the original file name of the audio file
file: the URL to the audio file
gen: the generic name of the species
group: the group to which the species belongs (birds, grasshoppers, bats)
id: the catalogue number of the recording on xeno-canto
lat: the latitude of the recording in decimal coordinates
length: the length of the recording in minutes
lic: the URL describing the license of this recording
lng: the longitude of the recording in decimal coordinates
loc: the name of the locality
method: the recording method (field recording, in the hand, etc.)
mic: microphone used
osci: an object with the urls to the three versions of oscillograms
playback-used: was playback used to lure the animal?
q: the current quality rating for the recording
rec: the name of the recordist
regnr: registration number of specimen (when collected)
rmk: additional remarks by the recordist
sex: the sex of the animal
smp: sample rate
sono: an object with the urls to the four versions of sonograms
sp: the specific name (epithet) of the species
ssp: the subspecies name (subspecific epithet)
stage: the life stage of the animal (adult, juvenile, etc.)
temperature: temperature during recording (applicable to specific groups only)
time: the time of day that the recording was made
type: the sound type of the recording (combining both predefined terms such as 'call' or 'song' and additional free text options)
uploaded: the date that the recording was uploaded to xeno-canto
url: the URL specifying the details of this recording
"""
# Need to create array
# parser.add_argument(
# "--also",
# help="an array with the identified background species in the recording",
# type=str,
# required=False,
# default="",
# )
parser.add_argument(
"--animal-seen",
help="was the recorded animal seen?",
type=str,
required=False,
choices=["yes", "no", "unknown"],
default="",
)
parser.add_argument(
"--auto",
help="(non-supervised) recording?",
type=str,
required=False,
choices=["yes", "no", "unknown"],
default="",
)
# parser.add_argument(
# "--bird-seen",
# help="despite the field name (which was kept to ensure backwards compatibility), this field indicates whether the recorded animal was seen",
# type=str,
# required=False,
# default="",
# )
parser.add_argument(
"--cnt",
help="the country where the recording was made (example: brazil)",
type=str,
required=False,
default="",
)
parser.add_argument(
"--date",
help="the date that the recording was made (example: 2022-09-18)",
type=str,
required=False,
default="",
)
parser.add_argument(
"--dvc",
help='recording device used (example: "Panasonic RR-US300")',
type=str,
required=False,
default="",
)
parser.add_argument(
"--en",
help='the English name of the species (example: "Great Tinamou")',
type=str,
required=False,
default="",
)
parser.add_argument(
"--file",
help="the URL to the audio file (example: https://xeno-canto.org/770944/download)",
type=str,
required=False,
default="",
)
parser.add_argument(
"--file-name",
help='the original file name of the audio file (example: "XC483178-Tinamus tao_Rio Azul_1032.mp3")',
type=str,
required=False,
default="",
)
parser.add_argument(
"--gen",
help='the generic name of the species (example: "Rhea")',
type=str,
required=False,
default="",
)
parser.add_argument(
"--group",
help="the group to which the species belongs",
type=str,
required=False,
choices=["bats", "birds", "grasshoppers"],
default="",
)
parser.add_argument(
"--id",
help="the catalogue number of the recording on xeno-canto (example: 830675)",
type=str,
required=False,
default="",
)
parser.add_argument(
"--lat",
help="the latitude of the recording in decimal coordinates (example: -26.7144)",
type=str,
required=False,
default="",
)
# parser.add_argument(
# "--length",
# help="the length of the recording in minutes (example: 1:49)",
# type=str,
# required=False,
# default="",
# )
# parser.add_argument(
# "--lic",
# help="the URL describing the license of this recording (example //creativecommons.org/licenses/by-sa/4.0/)",
# type=str,
# required=False,
# default="",
# )
parser.add_argument(
"--lng",
help="the longitude of the recording in decimal coordinates (example: -67.7537)",
type=str,
required=False,
default="",
)
parser.add_argument(
"--loc",
help='the name of the locality (example "Araponga, Minas Gerais")',
type=str,
required=False,
default="",
)
parser.add_argument(
"--method",
help='the recording method (example: "field recording")',
type=str,
required=False,
default="",
)
parser.add_argument(
"--mic",
help='microphone used (example: "Telinga + AT4022")',
type=str,
required=False,
default="",
)
# parser.add_argument(
# "--osci",
# help="an object with the urls to the three versions of oscillograms",
# type=str,
# required=False,
# default="",
# )
parser.add_argument(
"--playback-used",
help="was playback used to lure the animal?",
type=str,
required=False,
choices=["yes", "no", "unknown"],
default="",
)
parser.add_argument(
"--q",
help='the current quality rating for the recording (Letters A to E. Prepend > or < for better or worse than. Example: ">C" for A and B.)',
type=str,
required=False,
default="",
)
parser.add_argument(
"--rec",
help='the name of the recordist (example: "Jeff Moe")',
type=str,
required=False,
default="",
)
parser.add_argument(
"--regnr",
help="registration number of specimen (when collected)",
type=str,
required=False,
default="",
)
parser.add_argument(
"--rmk",
help="additional remarks by the recordist",
type=str,
required=False,
default="",
)
parser.add_argument(
"--sex",
help="the sex of the animal",
type=str,
required=False,
choices=["female", "male", "unknown"],
default="",
)
parser.add_argument(
"--smp",
help="sample rate (example: 48000)",
type=str,
required=False,
default="",
)
# parser.add_argument(
# "--sono",
# help="an object with the urls to the four versions of sonograms",
# type=str,
# required=False,
# default="",
# )
parser.add_argument(
"--sp",
help="the specific name (epithet) of the species (example: obsoletus)",
type=str,
required=False,
default="",
)
parser.add_argument(
"--ssp",
help="the subspecies name (subspecific epithet) (example: americana)",
type=str,
required=False,
default="",
)
parser.add_argument(
"--stage",
help="the life stage of the animal",
type=str,
required=False,
choices=["adult", "juvenile", "nesting", "nymph", "subadult", "unknown"],
default="",
)
parser.add_argument(
"--temperature",
help="temperature during recording (applicable to specific groups only)",
type=str,
required=False,
default="",
)
# parser.add_argument(
# "--time",
# help="the time of day that the recording was made (example: 17:35)",
# type=str,
# required=False,
# default="",
# )
parser.add_argument(
"--type",
help="the sound type of the recording",
type=str,
required=False,
choices=[
"aberrant",
'"alarm call"',
'"begging call"',
"call",
'"calling song"',
'"courtship song"',
'"dawn song"',
'"distress call"',
'"distrubance song"',
"drumming",
"duet",
"echolocation",
'"female song"',
'"flight call"',
'"flight song"',
"imitation",
'"nocturnal flight call"',
'"rivalry song"',
'"searching song"',
'"social call"',
"song",
"subsong",
"unknown",
],
default="",
)
parser.add_argument(
"--uploaded",
help="the date that the recording was uploaded to xeno-canto (example: 2022-03-02)",
type=str,
required=False,
default="",
)
# parser.add_argument(
# "--url",
# help="the URL specifying the details of this recording (example: //xeno-canto.org/755078)",
# type=str,
# required=False,
# default="",
# )
args = parser.parse_args()
API_URL = args.api
DRY_RUN = args.dry_run
QUERY = args.query
# API fields
# ALSO = args.also
ANIMAL_SEEN = args.animal_seen
AUTO = args.auto
# BIRD_SEEN = args.bird_seen
CNT = args.cnt
DATE = args.date
DVC = args.dvc
EN = args.en
FILE = args.file
FILE_NAME = args.file_name
GEN = args.gen
GROUP = args.group
ID = args.id
LAT = args.lat
# LENGTH = args.length
# LIC = args.lic
LNG = args.lng
LOC = args.loc
METHOD = args.method
MIC = args.mic
# OSCI = args.osci
PLAYBACK_USED = args.playback_used
Q = args.q
REC = args.rec
REGNR = args.regnr
RMK = args.rmk
SEX = args.sex
SMP = args.smp
# SONO = args.sono
SP = args.sp
SSP = args.ssp
STAGE = args.stage
TEMPERATURE = args.temperature
# TIME = args.time
TYPE = args.type
UPLOADED = args.uploaded
# URL = args.url
def print_json(jsn):
print(json.dumps(json.loads(jsn), sort_keys=True, indent=4))
QUERYURL = ""
if QUERY:
QUERYURL = QUERYURL + "" + urllib.parse.quote('"' + QUERY + '"' + " ")
# xeno-canto API
#'''
# "also": [
# "Cercomacra cinerascens",
# "Ceratopipra rubrocapilla",
# "Cymbilaimus lineatus"
# ],
#'''
# if ALSO:
# QUERYURL = QUERYURL + "also:" + ALSO + ","
# Note, the API returns for field name "seen", but not "animal-seen"
if ANIMAL_SEEN:
QUERYURL = QUERYURL + "seen:" + urllib.parse.quote('"' + ANIMAL_SEEN + '"' + " ")
if AUTO:
QUERYURL = QUERYURL + "auto:" + urllib.parse.quote('"' + AUTO + '"' + " ")
#'''
# "bird-seen": "yes",
# The field animal-seen replaces the field bird-seen.
#'''
# if BIRD_SEEN:
# QUERYURL = QUERYURL + "bird-seen:" + BIRD_SEEN + ","
if CNT:
QUERYURL = QUERYURL + "cnt:" + urllib.parse.quote('"' + CNT + '"' + " ")
if DATE:
QUERYURL = QUERYURL + "date:" + urllib.parse.quote('"' + DATE + '"' + " ")
if DVC:
QUERYURL = QUERYURL + "dvc:" + urllib.parse.quote('"' + DVC + '"' + " ")
if EN:
QUERYURL = QUERYURL + "en:" + urllib.parse.quote('"' + EN + '"' + " ")
if FILE:
QUERYURL = QUERYURL + "file:" + urllib.parse.quote('"' + FILE + '"' + " ")
if FILE_NAME:
QUERYURL = QUERYURL + "file-name:" + urllib.parse.quote('"' + FILE_NAME + '"' + " ")
if GEN:
QUERYURL = QUERYURL + "gen:" + urllib.parse.quote('"' + GEN + '"' + " ")
if GROUP:
QUERYURL = QUERYURL + "group:" + urllib.parse.quote('"' + GROUP + '"' + " ")
if ID:
QUERYURL = QUERYURL + "id:" + urllib.parse.quote('"' + ID + '"' + " ")
if LAT:
QUERYURL = QUERYURL + "lat:" + urllib.parse.quote('"' + LAT + '"' + " ")
#'''
# "length": "1:49",
#'''
# if LENGTH:
# QUERYURL = QUERYURL + "length:" + urllib.parse.quote('"' + LENGTH + '"' + " ")
#'''
# "lic": "//creativecommons.org/licenses/by-sa/4.0/",
#'''
# https://xeno-canto.org/explore?query=lic:%22by-sa%22%20
# if LIC:
# QUERYURL = QUERYURL + "lic:" + urllib.parse.quote('"' + LIC + '"' + " ")
if LNG:
QUERYURL = QUERYURL + "lng:" + urllib.parse.quote('"' + LNG + '"' + " ")
if LOC:
QUERYURL = QUERYURL + "loc:" + urllib.parse.quote('"' + LOC + '"' + " ")
if METHOD:
QUERYURL = QUERYURL + "method:" + urllib.parse.quote('"' + METHOD + '"' + " ")
if MIC:
QUERYURL = QUERYURL + "mic:" + urllib.parse.quote('"' + MIC + '"' + " ")
#'''
# "osci": {
# "large": "//xeno-canto.org/sounds/uploaded/DGVLLRYDXS/wave/XC214239-large.png",
# "med": "//xeno-canto.org/sounds/uploaded/DGVLLRYDXS/wave/XC214239-med.png",
# "small": "//xeno-canto.org/sounds/uploaded/DGVLLRYDXS/wave/XC214239-small.png"
# },
#'''
# if OSCI:
# QUERYURL = QUERYURL + "osci:" + urllib.parse.quote('"' + OSCI + '"' + " ")
if PLAYBACK_USED:
QUERYURL = (
QUERYURL
+ "playback-used:"
+ urllib.parse.quote('"' + PLAYBACK_USED + '"' + " ")
)
if Q:
QUERYURL = QUERYURL + "q:" + urllib.parse.quote('"' + Q + '"' + " ")
if REC:
QUERYURL = QUERYURL + "rec:" + urllib.parse.quote('"' + REC + '"' + " ")
if REGNR:
QUERYURL = QUERYURL + "regnr:" + urllib.parse.quote('"' + REGNR + '"' + " ")
if RMK:
QUERYURL = QUERYURL + "rmk:" + urllib.parse.quote('"' + RMK + '"' + " ")
if SEX:
QUERYURL = QUERYURL + "sex:" + urllib.parse.quote('"' + SEX + '"' + " ")
if SMP:
QUERYURL = QUERYURL + "smp:" + urllib.parse.quote('"' + SMP + '"' + " ")
#'''
# "sono": {
# "full": "//xeno-canto.org/sounds/uploaded/TGBFXDVERJ/ffts/XC171181-full.png",
# "large": "//xeno-canto.org/sounds/uploaded/TGBFXDVERJ/ffts/XC171181-large.png",
# "med": "//xeno-canto.org/sounds/uploaded/TGBFXDVERJ/ffts/XC171181-med.png",
# "small": "//xeno-canto.org/sounds/uploaded/TGBFXDVERJ/ffts/XC171181-small.png"
# },
#'''
# if SONO:
# QUERYURL = QUERYURL + "sono:" + urllib.parse.quote('"' + SONO + '"' + " ")
if SP:
QUERYURL = QUERYURL + "sp:" + urllib.parse.quote('"' + SP + '"' + " ")
if SSP:
QUERYURL = QUERYURL + "ssp:" + urllib.parse.quote('"' + SSP + '"' + " ")
if STAGE:
QUERYURL = QUERYURL + "stage:" + urllib.parse.quote('"' + STAGE + '"' + " ")
if TEMPERATURE:
QUERYURL = (
QUERYURL + "temperature:" + urllib.parse.quote('"' + TEMPERATURE + '"' + " ")
)
#'''
# "time": "17:35",
#'''
# if TIME:
# QUERYURL = QUERYURL + "time:" + urllib.parse.quote('"' + TIME + '"' + " ")
if TYPE:
QUERYURL = QUERYURL + "type:" + urllib.parse.quote('"' + TYPE + '"' + " ")
if UPLOADED:
QUERYURL = QUERYURL + "uploaded:" + urllib.parse.quote('"' + UPLOADED + '"' + " ")
#'''
# "url": "//xeno-canto.org/755078"
#'''
# if URL:
# QUERYURL = QUERYURL + "url:" + URL + ","
# QUERYURL = QUERYURL + "url:" + urllib.parse.quote('"' + URL + '"' + " ")
QUERYURL = QUERYURL.rstrip(f"20")
QUERYURL = QUERYURL.rstrip(f"%%")
if QUERYURL == "":
print("See xeno-crufto -h for help")
exit()
QUERYURL = "?query=" + QUERYURL
QUERYURL = API_URL + QUERYURL
if DRY_RUN == True:
print(QUERYURL)
else:
resp = requests.post(f"{QUERYURL}")
print_json(resp.text)