Skip to content

Commit

Permalink
Python docs updates and bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Marekkon5 committed Sep 8, 2023
1 parent af16785 commit b8568bb
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 33 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion client/src/components/DevTools.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<q-btn class='q-mx-sm' outline color='primary' @click='devServer()'>Go to dev server</q-btn>
<q-btn class='q-mx-sm' outline color='primary' @click='devtools()'>Open webview devtools</q-btn>
<q-btn class='q-mx-sm' outline color='primary' @click='$1t.send("openSettingsFolder")'>Open data dir</q-btn>
<q-btn class='q-mx-sm' outline color='primary' @click='pythonDocs()'>Generate and open Python Docs</q-btn>
<div class='q-my-sm'></div>

<!-- Log -->
Expand Down Expand Up @@ -63,11 +64,12 @@
</template>

<script lang='ts' setup>
import { useDialogPluginComponent } from 'quasar';
import { useDialogPluginComponent, useQuasar } from 'quasar';
import { get1t } from '../scripts/onetagger';
import { computed, onMounted, ref } from 'vue';
const { dialogRef, onDialogHide } = useDialogPluginComponent();
const $q = useQuasar();
const $1t = get1t();
const logRef = ref(null);
const fullLog = ref(false);
Expand Down Expand Up @@ -108,6 +110,18 @@ function logBottom() {
logRef.value!.scrollTo(log.value.length);
}
// Generate python docs and show warning
function pythonDocs() {
$1t.send('pythonDocs');
$q.dialog({
title: 'Python Docs',
message: 'Python docs are generating, which might take a while and will open once ready. You can also access them later from 1T folder',
ok: {
color: 'primary'
}
});
}
// Get color for level
function levelColor(level: string) {
return {
Expand Down
2 changes: 1 addition & 1 deletion crates/onetagger-autotag/src/platforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ impl AutotaggerPlatforms {

// Generate info
let info = AutotaggerPlatformInfo {
id: entry.file_name().to_string_lossy().to_string(),
id: format!("{}.py", entry.file_name().to_string_lossy()),
built_in: false,
platform: platform.info.info.to_owned(),
icon,
Expand Down
1 change: 1 addition & 0 deletions crates/onetagger-python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ chrono = "0.4"
anyhow = "1.0"
tempdir = "0.3"
rmp-serde = "1.1"
pythonize = "0.18"
serde_json = "1.0"

log = { version = "0.4", features = ["serde"] }
Expand Down
56 changes: 54 additions & 2 deletions crates/onetagger-python/src/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,76 @@
import requests
from bs4 import BeautifulSoup

STYLE = """
body {
background-color: #181818;
color: #fffff8;
}
a {
color: #00d2bf;
}
pre {
background-color: #202020 !important;
color: #fffff8 !important;
}
code {
background-color: #202020 !important;
color: #fffff8 !important;
}
.hljs-keyword {
color: #c791e8;
}
.hljs-title {
color: #00d2bf;
}
.hljs-string {
color: #c2e58a;
}
.hljs-literal {
color: #ef632a;
}
.ident {
color: #00d2bf;
}
"""

def generate_docs(module, output):
html = pdoc.html(module)
soup = BeautifulSoup(html, features='lxml')

# Make CSS offline
for e in soup.select('link'):
if e.has_attr('href'):
r = requests.get(e.attrs['href'])
new = soup.new_tag('style')
new.string = r.text
e.replace_with(new)

# Make JS offline
for e in soup.select('script'):
if e.has_attr('src'):
r = requests.get(e.attrs['src'])
new = soup.new_tag('script')
new.string = r.text
e.replace_with(new)


# Cleanup
for e in soup.select('div.desc'):
if 'Return an attribute of instance, which is of type owner.' in e.text.strip():
e.replace_with('')

# Inject style
style = soup.new_tag('style')
style.string = STYLE
soup.body.append(style)

with open(output, 'w') as f:
f.write(soup.prettify())
50 changes: 24 additions & 26 deletions crates/onetagger-python/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn setup() {
/// ## How to implement your own module:
///
/// ```
/// from onetagger import AudioFileInfo, TaggerConfig, TrackMatch, Track, new_track, match_track
/// from onetagger import AudioFileInfo, TaggerConfig, TrackMatch, Track, new_track, match_tracks
///
/// # This function should search on your platform and return list of relevant / matched tracks
/// def match_track(info: AudioFileInfo, config: TaggerConfig) -> list[TrackMatch]:
Expand All @@ -60,7 +60,7 @@ pub fn setup() {
/// artists = ["Artist"]
/// )
/// # Match your tracks
/// matches = match_track(info, [track], config, True)
/// matches = match_tracks(info, [track], config, True)
/// return matches
///
/// # This function will be called later on matched track, so here you can fetch additional metadata
Expand Down Expand Up @@ -143,14 +143,14 @@ fn onetagger(_py: Python<'_>, module: &PyModule) -> PyResult<()> {
/// NOTE: Output is unsorted, sorted later in AT
#[pyfn(module)]
#[pyo3(signature = (info, tracks, config, match_artist = true))]
fn match_track(info: &AudioFileInfo, tracks: Vec<Track>, config: &TaggerConfig, match_artist: bool) -> Vec<TrackMatch> {
fn match_tracks(info: &AudioFileInfo, tracks: Vec<Track>, config: &TaggerConfig, match_artist: bool) -> Vec<TrackMatch> {
MatchingUtils::match_track(info, &tracks, config, match_artist)
}

/// Do exact matches on each step of track cleaning
#[pyfn(module)]
#[pyo3(signature = (info, tracks, config, match_artist = true))]
fn match_track_exact_fallback(info: &AudioFileInfo, tracks: Vec<Track>, config: &TaggerConfig, match_artist: bool) -> Vec<Track> {
fn match_tracks_exact_fallback(info: &AudioFileInfo, tracks: Vec<Track>, config: &TaggerConfig, match_artist: bool) -> Vec<Track> {
MatchingUtils::match_track_exact_fallback(info, &tracks, config, match_artist)
}

Expand Down Expand Up @@ -215,32 +215,30 @@ fn onetagger(_py: Python<'_>, module: &PyModule) -> PyResult<()> {
///
#[pyfunction]
#[pyo3(signature = (**kwargs))]
fn new_track(kwargs: Option<&PyDict>) -> Track {
fn new_track(kwargs: Option<&PyDict>) -> Result<Track, Error> {
let kwargs = match kwargs {
Some(k) => k,
None => return Track::default()
None => return Ok(Track::default())
};

// Copy fields to track
let mut track = Track::default();
macro_rules! gen_track_fields {
($($field: tt),*) => {
$(
kwargs
.get_item(stringify!(field))
.map(|i| i.extract().map(|i| track.$field = i).ok())
.flatten()
.unwrap_or_default();
)*
};
}
gen_track_fields!(
platform, title, version, artists, album_artists, album, key, bpm, genres,
styles, art, url, label, catalog_number, other, track_id, release_id, duration,
remixers, track_number, track_total, disc_number, isrc, mood, explicit, lyrics,
release_year, release_date, publish_year, publish_date, thumbnail
);
track
// Set some fields to default value
macro_rules! set_default {
($field: tt, $value: expr) => {
if !kwargs.hasattr($field).unwrap_or(false) {
kwargs.set_item($field, $value)?;
}
}
}
set_default!("album_artists", Vec::<()>::new());
set_default!("genres", Vec::<()>::new());
set_default!("styles", Vec::<()>::new());
set_default!("other", Vec::<()>::new());
set_default!("remixers", Vec::<()>::new());
set_default!("duration", 0.0f64);
set_default!("release_id", String::new());
set_default!("url", String::new());

Ok(pythonize::depythonize(kwargs)?)
}

/// Create new TrackMatch object from track
Expand Down
40 changes: 38 additions & 2 deletions crates/onetagger-tag/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
#[cfg(feature = "tag")]
#[macro_use] extern crate anyhow;

use serde::{Serialize, Deserialize};
use serde::de::Visitor;
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::time::Duration;
Expand Down Expand Up @@ -581,7 +582,7 @@ pub struct LyricsLinePart {
}

/// Duration which can be used in Python as well
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct OTDuration(pub Duration);

Expand Down Expand Up @@ -623,4 +624,39 @@ impl<'a> FromPyObject<'a> for OTDuration {
fn extract(ob: &'a PyAny) -> PyResult<Self> {
Ok(OTDuration(Duration::from_secs_f64(ob.extract()?)))
}
}

impl Serialize for OTDuration {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
{
serializer.serialize_f64(self.as_secs_f64())
}
}

impl<'de> Deserialize<'de> for OTDuration {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>
{
deserializer.deserialize_f64(OTDurationVisitor)
}
}

struct OTDurationVisitor;

impl<'de> Visitor<'de> for OTDurationVisitor {
type Value = OTDuration;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Duration as f64 seconds")
}

fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(OTDuration(Duration::from_secs_f64(v)))
}
}
7 changes: 6 additions & 1 deletion crates/onetagger-tagger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,12 @@ impl Track {
if v.trim().is_empty() {
self.title.to_string()
} else {
format!("{} ({})", self.title, v.trim())
let v = v.trim();
if v.starts_with("(") || v.starts_with("[") || v.starts_with("{") {
format!("{} {}", self.title, v)
} else {
format!("{} ({})", self.title, v)
}
}
} else {
self.title.to_string()
Expand Down
6 changes: 6 additions & 0 deletions crates/onetagger/src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum Action {
OpenFile { path: PathBuf },
DeleteFiles { paths: Vec<String> },
GetLog,
PythonDocs,

StartTagging { config: TaggerConfigs, playlist: Option<UIPlaylist> },
StopTagging,
Expand Down Expand Up @@ -594,6 +595,11 @@ fn handle_message(text: &str, websocket: &mut WebSocket<TcpStream>, context: &mu
})).ok();
},
}
},

// Generate and open Python documentation
Action::PythonDocs => {
webbrowser::open(&format!("file://{}", onetagger_python::generate_docs()?.to_string_lossy()))?;
}

}
Expand Down

0 comments on commit b8568bb

Please sign in to comment.