<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <algorithm>
#include <stdio.h>
#include <ctype.h>

#include "conf.hh"
#include "globals.hh"
#include "consumer.hh"
#include "env.hh"

static int find_feat(std::string::iterator bstring,
                     std::string::iterator estring) {
    // bounds check for ft./Ft.
    if (bstring + 2 >= estring) {
        return 0;
    }
    if (bstring[0] == 'f' || bstring[0] == 'F') {
        if (bstring[1] == 't' && bstring[2] == '.') {
            return 3;
        }

        // bounds check for feat./Feat.
        if (bstring + 4 >= estring) {
            return 0;
        }
        if (bstring[1] == 'e' && bstring[2] == 'a' &&
            bstring[3] == 't' && bstring[4] == '.') {
            return 5;
        }
    }
    return 0;
}

static std::string
fix_feat_delim(const std::string::iterator bstring,
               const std::string::iterator estring, const char odelim,
               const char cdelim) {
    auto istring = find(bstring, estring, odelim);
    if (istring == estring || /* !!! */ ++istring == estring) {
        return {bstring, estring};
    }

    // istring is now after the [
    if (auto feat_offset = find_feat(istring, estring)) {
        auto ebracket = find(istring, estring, cdelim);
        if (ebracket == estring) {
            return {};
        }
        std::string output{bstring, istring - 1};
        output += '(';
        output.append("feat.");
        output.append(istring + feat_offset, ebracket);
        output += ')';
        if (ebracket + 1 != estring) {
            output.append(ebracket + 1, estring);
        }
        return output;
    }
    return {bstring, estring};
}

static std::string
fix_feat_nodelim(std::string::iterator bstring,
                 std::string::iterator estring) {
    auto istring = bstring;
    while (istring != estring) {
        istring = find_if(istring, estring, [](char ch) {
            return ch == 'f' || ch == 'F';
        });
        if (istring - 1 != bstring && istring[-1] == '(') {
            ++istring;
            continue;
        }
        if (istring == estring) {
            break;
        }
        if (auto feat_offset = find_feat(istring, estring)) {
            std::string output{bstring, istring};
            output += "(feat.";

            // if has (for example) a `(SXX Remix)`
            auto efeat = find(istring + feat_offset, estring, '(');
            while (isspace(*--efeat)) {
            }
            ++efeat;

            output.append(istring + feat_offset, efeat);
            output += ')';
            if (efeat != estring) {
                output.append(efeat, estring);
            }
            return output;
        }
        ++istring;
    }
    return {bstring, estring};
}

/// Change featuring info to the way I want:
/// Words:
/// `ft.`, `Ft.`, `Feat.` --> feat.
/// Delimiter:
/// None, `[` then `]` --> `(` then `)`
static std::string fix_feat(std::string::iterator istring,
                            std::string::iterator estring) {
    auto fbrackets = fix_feat_delim(istring, estring, '[', ']');
    auto fparens =
        fix_feat_delim(fbrackets.begin(), fbrackets.end(), '(', ')');
    auto fnone = fix_feat_nodelim(fparens.begin(), fparens.end());
    return fnone;
}

/// Remove `\[.*?\] ` and an optional `- ` from the beginning.
static void remove_genre(std::string::iterator& istring,
                         const std::string::iterator& estring) {
    if (*istring == '[') {
        do {
            istring = find(istring, estring, ']');
            if (istring == estring) {
                std::fprintf(stderr, "Unmatched `[`.  Skipping "
                                     "removing genre.\n");
                break;
            }
            if (istring[1] == ' ') {
                istring += 2;
                if (istring[0] == '-' && istring[1] == ' ') {
                    istring += 2;
                }
            }
        } while (0);
    }
}

/// Remove `\s*\[.*?\]` from end if it exists.
static void remove_trailing_tags(const std::string& no_slashes,
                                 std::string::iterator& istring,
                                 std::string::iterator& estring) {
    do {
        auto rbstring = no_slashes.rbegin();
        auto ristring = rbstring;
        auto restring = rbstring + (estring - istring);

        if (ristring != restring && *ristring == ']') {
            ristring = find(ristring, restring, '[');
            if (ristring == restring) {
                std::fprintf(stderr,
                             "Unmatched `]`.  Skipping remove "
                             "trailing tags.\n");
                break;
            }

            // check for feat, skip if is
            auto iter = ristring;
            --iter;
            if (*iter == 'f' || *iter == 'F') {
                --iter;
                if (*iter == 't') {
                    --iter;
                    if (*iter == '.') {
                        break;
                    }
                } else if (*iter == 'e') {
                    --iter;
                    if (*iter == 'a') {
                        --iter;
                        if (*iter == 't') {
                            --iter;
                            if (*iter == '.') {
                                break;
                            }
                        }
                    }
                }
            }

            ++ristring;
            if (ristring == restring) {
                std::fprintf(stderr,
                             "Trailing tags are entire filename.  "
                             "Skipping remove trailing tags.\n");
                break;
            }

            // skip whitespace
            ristring =
                find_if(ristring, restring,
                        [](char ch) -> bool { return !isspace(ch); });
            if (ristring == restring) {
                std::fprintf(stderr,
                             "Trailing tags are entire filename.  "
                             "Skipping remove trailing tags.\n");
                break;
            }

            // commit ristring to estring
            estring = estring - (ristring - rbstring);
        }
    } while (0);
}

/// Split `fname` into `artist\s+-\s*name`
static void parse_file_name(const std::string& fname,
                            std::string& artist, std::string& name) {
    const auto bfname = fname.begin();
    const auto efname = fname.end();

    auto dash_loc = bfname;
restart:
    dash_loc = find(dash_loc, efname, '-');

    /* artist */ {
        auto eartist = dash_loc;
        bool went_through_space = false;
        // backtrack through whitespace
        while (isspace(*--eartist)) {
            went_through_space = true;
        }
        ++eartist;
        // handle the case where name has a -
        if (!went_through_space) {
            ++dash_loc;
            goto restart;
        }
        artist.assign(bfname, eartist);
    }

    /* name */ {
        auto bname = dash_loc;
        // go forward through whitespace
        while (isspace(*++bname)) {
        }
        name.assign(bname, efname);
    }
}

void Consumer::consume(const boost::filesystem::path& p) {
    // dropped .mp3 here
    auto no_slashes = p.filename().replace_extension().string();

    auto bstring = no_slashes.begin();
    auto estring = no_slashes.end();
    auto istring = bstring;

    remove_genre(istring, estring);
    remove_trailing_tags(no_slashes, istring, estring);
    auto new_fname = fix_feat(istring, estring);

    auto new_path = get_songs_dir() / (new_fname + ".mp3");

    _sorted_insert_song_(new_path.filename().string());
    if (p != new_path) {
        if (NONO) {
            std::printf("mv \"%s\" \"%s\"\n", p.c_str(),
                        new_path.c_str());
        } else {
            rename(p, new_path);
        }
    }

    std::string artist, name;
    parse_file_name(new_fname, artist, name);

    if (NONO) {
        _apply_tags_(p, artist, name);
    } else {
        _apply_tags_(new_path, artist, name);
    }
}
<commit_msg>Discard text after feat. unless it is parenthesis.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <algorithm>
#include <stdio.h>
#include <ctype.h>

#include "conf.hh"
#include "globals.hh"
#include "consumer.hh"
#include "env.hh"

static int find_feat(std::string::iterator bstring,
                     std::string::iterator estring) {
    // bounds check for ft./Ft.
    if (bstring + 2 >= estring) {
        return 0;
    }
    if (bstring[0] == 'f' || bstring[0] == 'F') {
        if (bstring[1] == 't' && bstring[2] == '.') {
            return 3;
        }

        // bounds check for feat./Feat.
        if (bstring + 4 >= estring) {
            return 0;
        }
        if (bstring[1] == 'e' && bstring[2] == 'a' &&
            bstring[3] == 't' && bstring[4] == '.') {
            return 5;
        }
    }
    return 0;
}

static void add_parenthesized_postfix(std::string& output,
                                      std::string::iterator begin,
                                      std::string::iterator end) {
    auto iter = begin;
    while (iter != end && isspace(*iter)) {
        ++iter;
    }
    if (iter != end && *iter == '(') {
        output.append(begin, end);
    }
}

static std::string
fix_feat_delim(const std::string::iterator bstring,
               const std::string::iterator estring, const char odelim,
               const char cdelim) {
    auto istring = find(bstring, estring, odelim);
    if (istring == estring || /* !!! */ ++istring == estring) {
        return {bstring, estring};
    }

    // istring is now after the [
    if (auto feat_offset = find_feat(istring, estring)) {
        auto ebracket = find(istring, estring, cdelim);
        if (ebracket == estring) {
            return {};
        }
        std::string output{bstring, istring - 1};
        output += '(';
        output.append("feat.");
        output.append(istring + feat_offset, ebracket);
        output += ')';

        add_parenthesized_postfix(output, ebracket + 1, estring);

        return output;
    }
    return {bstring, estring};
}

static std::string
fix_feat_nodelim(std::string::iterator bstring,
                 std::string::iterator estring) {
    auto istring = bstring;
    while (istring != estring) {
        istring = find_if(istring, estring, [](char ch) {
            return ch == 'f' || ch == 'F';
        });
        if (istring - 1 != bstring && istring[-1] == '(') {
            ++istring;
            continue;
        }
        if (istring == estring) {
            break;
        }
        if (auto feat_offset = find_feat(istring, estring)) {
            std::string output{bstring, istring};
            output += "(feat.";

            // if has (for example) a `(SXX Remix)`
            auto efeat = find(istring + feat_offset, estring, '(');
            while (isspace(*--efeat)) {
            }
            ++efeat;

            output.append(istring + feat_offset, efeat);
            output += ')';

            add_parenthesized_postfix(output, efeat, estring);

            return output;
        }
        ++istring;
    }
    return {bstring, estring};
}

/// Change featuring info to the way I want:
/// Words:
/// `ft.`, `Ft.`, `Feat.` --> feat.
/// Delimiter:
/// None, `[` then `]` --> `(` then `)`
static std::string fix_feat(std::string::iterator istring,
                            std::string::iterator estring) {
    auto fbrackets = fix_feat_delim(istring, estring, '[', ']');
    auto fparens =
        fix_feat_delim(fbrackets.begin(), fbrackets.end(), '(', ')');
    auto fnone = fix_feat_nodelim(fparens.begin(), fparens.end());
    return fnone;
}

/// Remove `\[.*?\] ` and an optional `- ` from the beginning.
static void remove_genre(std::string::iterator& istring,
                         const std::string::iterator& estring) {
    if (*istring == '[') {
        do {
            istring = find(istring, estring, ']');
            if (istring == estring) {
                std::fprintf(stderr, "Unmatched `[`.  Skipping "
                                     "removing genre.\n");
                break;
            }
            if (istring[1] == ' ') {
                istring += 2;
                if (istring[0] == '-' && istring[1] == ' ') {
                    istring += 2;
                }
            }
        } while (0);
    }
}

/// Remove `\s*\[.*?\]` from end if it exists.
static void remove_trailing_tags(const std::string& no_slashes,
                                 std::string::iterator& istring,
                                 std::string::iterator& estring) {
    do {
        auto rbstring = no_slashes.rbegin();
        auto ristring = rbstring;
        auto restring = rbstring + (estring - istring);

        if (ristring != restring && *ristring == ']') {
            ristring = find(ristring, restring, '[');
            if (ristring == restring) {
                std::fprintf(stderr,
                             "Unmatched `]`.  Skipping remove "
                             "trailing tags.\n");
                break;
            }

            // check for feat, skip if is
            auto iter = ristring;
            --iter;
            if (*iter == 'f' || *iter == 'F') {
                --iter;
                if (*iter == 't') {
                    --iter;
                    if (*iter == '.') {
                        break;
                    }
                } else if (*iter == 'e') {
                    --iter;
                    if (*iter == 'a') {
                        --iter;
                        if (*iter == 't') {
                            --iter;
                            if (*iter == '.') {
                                break;
                            }
                        }
                    }
                }
            }

            ++ristring;
            if (ristring == restring) {
                std::fprintf(stderr,
                             "Trailing tags are entire filename.  "
                             "Skipping remove trailing tags.\n");
                break;
            }

            // skip whitespace
            ristring =
                find_if(ristring, restring,
                        [](char ch) -> bool { return !isspace(ch); });
            if (ristring == restring) {
                std::fprintf(stderr,
                             "Trailing tags are entire filename.  "
                             "Skipping remove trailing tags.\n");
                break;
            }

            // commit ristring to estring
            estring = estring - (ristring - rbstring);
        }
    } while (0);
}

/// Split `fname` into `artist\s+-\s*name`
static void parse_file_name(const std::string& fname,
                            std::string& artist, std::string& name) {
    const auto bfname = fname.begin();
    const auto efname = fname.end();

    auto dash_loc = bfname;
restart:
    dash_loc = find(dash_loc, efname, '-');

    /* artist */ {
        auto eartist = dash_loc;
        bool went_through_space = false;
        // backtrack through whitespace
        while (isspace(*--eartist)) {
            went_through_space = true;
        }
        ++eartist;
        // handle the case where name has a -
        if (!went_through_space) {
            ++dash_loc;
            goto restart;
        }
        artist.assign(bfname, eartist);
    }

    /* name */ {
        auto bname = dash_loc;
        // go forward through whitespace
        while (isspace(*++bname)) {
        }
        name.assign(bname, efname);
    }
}

void Consumer::consume(const boost::filesystem::path& p) {
    // dropped .mp3 here
    auto no_slashes = p.filename().replace_extension().string();

    auto bstring = no_slashes.begin();
    auto estring = no_slashes.end();
    auto istring = bstring;

    remove_genre(istring, estring);
    remove_trailing_tags(no_slashes, istring, estring);
    auto new_fname = fix_feat(istring, estring);

    auto new_path = get_songs_dir() / (new_fname + ".mp3");

    _sorted_insert_song_(new_path.filename().string());
    if (p != new_path) {
        if (NONO) {
            std::printf("mv \"%s\" \"%s\"\n", p.c_str(),
                        new_path.c_str());
        } else {
            rename(p, new_path);
        }
    }

    std::string artist, name;
    parse_file_name(new_fname, artist, name);

    if (NONO) {
        _apply_tags_(p, artist, name);
    } else {
        _apply_tags_(new_path, artist, name);
    }
}

{'fraction_non_alphanumeric': 0.1416965915111556, 'fraction_numerical': 0.003826455524813092, 'mean_word_length': 1.4552681023269258, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4359703', 'n_tokens_mistral': 5159, 'n_tokens_neox': 4987, 'n_words': 1184}