aboutsummaryrefslogtreecommitdiff
path: root/import
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-04-23 13:42:55 +0200
committermetamuffin <metamuffin@disroot.org>2025-04-23 13:42:55 +0200
commit5d2145406c4f2e11b0cde06f5f12c9d16ab51ded (patch)
treeb5ee3363b5181aaa8543185b71e862af07c771ea /import
parentac31201ba788dbaae1606fc0ee8a1021681d2776 (diff)
downloadjellything-5d2145406c4f2e11b0cde06f5f12c9d16ab51ded.tar
jellything-5d2145406c4f2e11b0cde06f5f12c9d16ab51ded.tar.bz2
jellything-5d2145406c4f2e11b0cde06f5f12c9d16ab51ded.tar.zst
acoustid works
Diffstat (limited to 'import')
-rw-r--r--import/src/acoustid.rs26
-rw-r--r--import/src/lib.rs98
2 files changed, 75 insertions, 49 deletions
diff --git a/import/src/acoustid.rs b/import/src/acoustid.rs
index 8e8a603..49c5e38 100644
--- a/import/src/acoustid.rs
+++ b/import/src/acoustid.rs
@@ -25,12 +25,18 @@ pub(crate) struct AcoustID {
rate_limit: Arc<Semaphore>,
}
-#[derive(Debug, Hash, Clone, Encode, Decode, Deserialize)]
+#[derive(Debug, Hash, Clone, Encode, Decode)]
pub(crate) struct Fingerprint {
duration: u32,
fingerprint: String,
}
+#[derive(Debug, Deserialize)]
+pub(crate) struct FpCalcOutput {
+ duration: f32,
+ fingerprint: String,
+}
+
#[derive(Deserialize, Encode, Decode)]
pub(crate) struct AcoustIDLookupResultRecording {
id: String,
@@ -65,8 +71,8 @@ impl AcoustID {
}
}
- pub async fn get_atid_mbid(&self, fp: Fingerprint) -> Result<Option<(String, String)>> {
- let res = self.lookup(fp).await?;
+ pub async fn get_atid_mbid(&self, fp: &Fingerprint) -> Result<Option<(String, String)>> {
+ let res = self.lookup(fp.to_owned()).await?;
for r in &res.results {
if let Some(k) = r.recordings.get(0) {
return Ok(Some((r.id.clone(), k.id.clone())));
@@ -81,19 +87,19 @@ impl AcoustID {
let permit_drop_ts = Instant::now() + Duration::SECOND;
let duration = fp.duration;
- let fingerprint = &fp.fingerprint;
+ let fingerprint = fp.fingerprint.replace("=", "%3D");
let client = &self.key;
- let meta = "recordingids";
- let body = format!("format=json&client={client}&duration={duration}&fingerprint={fingerprint}&meta={meta}");
+ let body = format!("format=json&meta=recordingids&client={client}&duration={duration}&fingerprint={fingerprint}");
let resp = self
.client
.post(format!("https://api.acoustid.org/v2/lookup"))
+ // .post(format!("http://127.0.0.1:1234/v2/lookup"))
+ .header("Content-Type", "application/x-www-form-urlencoded")
.body(body)
.send()
.await?.error_for_status()?.json::<AcoustIDLookupResponse>().await?;
-
tokio::task::spawn(async move {
sleep_until(permit_drop_ts).await;
drop(_permit);
@@ -116,7 +122,11 @@ pub(crate) async fn acoustid_fingerprint(path: &Path) -> Result<Arc<Fingerprint>
let mut buf = Vec::new();
child.stdout.unwrap().read_to_end(&mut buf).await?;
- let out: Fingerprint = serde_json::from_slice(&buf)?;
+ let out: FpCalcOutput = serde_json::from_slice(&buf)?;
+ let out = Fingerprint {
+ duration: out.duration as u32,
+ fingerprint: out.fingerprint,
+ };
Ok(out)
})
diff --git a/import/src/lib.rs b/import/src/lib.rs
index 452d9b9..bd01fc9 100644
--- a/import/src/lib.rs
+++ b/import/src/lib.rs
@@ -4,7 +4,7 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
#![feature(duration_constants)]
-use acoustid::AcoustID;
+use acoustid::{acoustid_fingerprint, AcoustID};
use anyhow::{anyhow, bail, Context, Result};
use infojson::YVideo;
use jellybase::{
@@ -89,12 +89,21 @@ fn import(db: &Database, incremental: bool) -> Result<()> {
incremental,
NodeID::MIN,
"",
- Visibility::Visible,
+ InheritedFlags {
+ visibility: Visibility::Visible,
+ use_acoustid: false,
+ },
)?;
Ok(())
}
+#[derive(Debug, Clone, Copy)]
+struct InheritedFlags {
+ visibility: Visibility,
+ use_acoustid: bool,
+}
+
fn import_traverse(
path: &Path,
db: &Database,
@@ -103,7 +112,7 @@ fn import_traverse(
incremental: bool,
parent: NodeID,
parent_slug_fragment: &str,
- mut visibility: Visibility,
+ mut iflags: InheritedFlags,
) -> Result<()> {
if path.is_dir() {
let slug_fragment = if path == CONF.media_path {
@@ -122,8 +131,9 @@ fn import_traverse(
if let Ok(content) = read_to_string(path.join("flags")) {
for flag in content.lines() {
match flag.trim() {
- "hidden" => visibility = visibility.min(Visibility::Hidden),
- "reduced" => visibility = visibility.min(Visibility::Reduced),
+ "hidden" => iflags.visibility = iflags.visibility.min(Visibility::Hidden),
+ "reduced" => iflags.visibility = iflags.visibility.min(Visibility::Reduced),
+ "use_acoustid" => iflags.use_acoustid = true,
_ => (),
}
}
@@ -134,7 +144,7 @@ fn import_traverse(
n.parents.insert(parent);
}
n.slug = slug;
- n.visibility = visibility;
+ n.visibility = iflags.visibility;
Ok(())
})?;
@@ -148,7 +158,7 @@ fn import_traverse(
incremental,
id,
&slug_fragment,
- visibility,
+ iflags,
) {
IMPORT_ERRORS
.blocking_write()
@@ -170,7 +180,7 @@ fn import_traverse(
}
}
- import_file(db, apis, rthandle, path, parent, visibility)?;
+ import_file(db, apis, rthandle, path, parent, iflags)?;
db.set_import_file_mtime(path, mtime)?;
}
Ok(())
@@ -182,7 +192,7 @@ fn import_file(
rthandle: &Handle,
path: &Path,
parent: NodeID,
- visibility: Visibility,
+ iflags: InheritedFlags,
) -> Result<()> {
let filename = path.file_name().unwrap().to_string_lossy();
match filename.as_ref() {
@@ -265,9 +275,7 @@ fn import_file(
Ok(())
})?;
}
- _ => {
- import_media_file(db, apis, rthandle, path, parent, visibility).context("media file")?
- }
+ _ => import_media_file(db, apis, rthandle, path, parent, iflags).context("media file")?,
}
Ok(())
@@ -279,7 +287,7 @@ fn import_media_file(
rthandle: &Handle,
path: &Path,
parent: NodeID,
- visibility: Visibility,
+ iflags: InheritedFlags,
) -> Result<()> {
info!("media file {path:?}");
let Some(m) = (*checked_matroska_metadata(path)?).to_owned() else {
@@ -348,11 +356,44 @@ fn import_media_file(
let node = NodeID::from_slug(&slug);
let meta = path.metadata()?;
+ let mut eids = BTreeMap::new();
+
+ for (key, value) in &tags {
+ match key.as_str() {
+ "MUSICBRAINZ_TRACKID" => eids.insert("musicbrainz.track".to_string(), value.to_owned()),
+ "MUSICBRAINZ_ARTISTID" => {
+ eids.insert("musicbrainz.artist".to_string(), value.to_owned())
+ }
+ "MUSICBRAINZ_ALBUMID" => eids.insert("musicbrainz.album".to_string(), value.to_owned()),
+ "MUSICBRAINZ_ALBUMARTISTID" => {
+ eids.insert("musicbrainz.albumartist".to_string(), value.to_owned())
+ }
+ "MUSICBRAINZ_RELEASEGROUPID" => {
+ eids.insert("musicbrainz.releasegroup".to_string(), value.to_owned())
+ }
+ "ISRC" => eids.insert("isrc".to_string(), value.to_owned()),
+ "BARCODE" => eids.insert("barcode".to_string(), value.to_owned()),
+ _ => None,
+ };
+ }
+
+ if iflags.use_acoustid {
+ let fp = rthandle.block_on(acoustid_fingerprint(path))?;
+ if let Some((atid, mbid)) = rthandle.block_on(
+ apis.acoustid
+ .as_ref()
+ .ok_or(anyhow!("need acoustid"))?
+ .get_atid_mbid(&fp),
+ )? {
+ eids.insert("acoustid.track".to_string(), atid);
+ eids.insert("musicbrainz.track".to_string(), mbid);
+ };
+ }
db.update_node_init(node, |node| {
node.slug = slug;
node.title = info.title.or(node.title.clone());
- node.visibility = visibility;
+ node.visibility = iflags.visibility;
node.poster = m.cover.or(node.poster.clone());
node.description = tags
.remove("DESCRIPTION")
@@ -361,6 +402,8 @@ fn import_media_file(
node.tagline = tags.remove("COMMENT").or(node.tagline.clone());
node.parents.insert(parent);
+ node.external_ids.extend(eids);
+
if let Some(ct) = tags.get("CONTENT_TYPE") {
node.kind = match ct.to_lowercase().trim() {
"movie" | "documentary" | "film" => NodeKind::Movie,
@@ -369,33 +412,6 @@ fn import_media_file(
}
}
- for (key, value) in &tags {
- match key.as_str() {
- "MUSICBRAINZ_TRACKID" => node
- .external_ids
- .insert("musicbrainz.track".to_string(), value.to_owned()),
- "MUSICBRAINZ_ARTISTID" => node
- .external_ids
- .insert("musicbrainz.artist".to_string(), value.to_owned()),
- "MUSICBRAINZ_ALBUMID" => node
- .external_ids
- .insert("musicbrainz.album".to_string(), value.to_owned()),
- "MUSICBRAINZ_ALBUMARTISTID" => node
- .external_ids
- .insert("musicbrainz.albumartist".to_string(), value.to_owned()),
- "MUSICBRAINZ_RELEASEGROUPID" => node
- .external_ids
- .insert("musicbrainz.releasegroup".to_string(), value.to_owned()),
- "ISRC" => node
- .external_ids
- .insert("isrc".to_string(), value.to_owned()),
- "BARCODE" => node
- .external_ids
- .insert("barcode".to_string(), value.to_owned()),
- _ => None,
- };
- }
-
let tracks = tracks
.entries
.into_iter()