Tool to identify commercial MP3s

Introduction

Media files have become an important part of business content. Seeing that in some environments we cannot explicitly block MP3s, how do we identify what files are business recordings, and which are (possibly illegal) music?

IsMP3Song.exe is my attempt to address this issue. 

The idea is very simple, pass the filename to the tool and it returns a score indicating the confidence of it deeming the file as a commercial MP3.

The tool achieves this by doing a web-service call to iTunes and then scoring the iTunes results with the filename of the MP3.

Demo Execution

As you can see, the first couple of executions return a score because these are identified as commercial songs and the last one is scored as not commercial.

These scores are also returned by the application as an exit code, allowing return values to be used for other logic, such as email notifications or file classifications.

USING WITH POWERSHELL

Going a step further, the PowerShell snippet below can be used as a starting point to loop through folders and score all the MP3 files found.

$inputDir = "E:\SomeDataFolder";
$filterExt = "*.mp3";

$files = Get-ChildItem -Path $inputDir -Recurse -Filter $filterExt;
foreach ($file in $files)
{
$process = start-process .\IsMP3Song.exe -windowstyle Hidden -ArgumentList """$($file.Name)""" -PassThru -Wait
"$($file.Name) $($process.ExitCode)"
}

The C# code

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace IsMP3Song
{
public class Song
{
public string ArtistName;
public string TrackName;
public int Match;
}
class Program
{
static void Main(string[] args)
{
List<Song> songs = new List<Song>();

try
{
if (args.Length == 1)
{
string searchTerm = FormatSearchTerm(args[0]);
string jsonReturn = GetRequest($"https://itunes.apple.com/search?term={searchTerm}");
dynamic jsonCollection = JObject.Parse(jsonReturn);
foreach (var result in jsonCollection.results)
{
dynamic jsonObject = JObject.Parse(result.ToString());
double match = ((CalculateSimilarity(args[0], $"{jsonObject.artistName} - {jsonObject.trackName}")) * 100);

Song song = new Song();
song.ArtistName = jsonObject.artistName;
song.TrackName = jsonObject.trackName;
song.Match = (int)match;
songs.Add(song);
}

if (songs.Count > 0)
{
Song bestMatch = songs.OrderByDescending(s => s.Match).First();
Console.WriteLine($"{bestMatch.ArtistName} - {bestMatch.TrackName} [{bestMatch.Match}]");
Environment.Exit(bestMatch.Match);
}
else
{
Console.WriteLine($"Seems that is not a commercial MP3...");
Environment.Exit(0);
}

}
}
catch
{

}
}
// Returns JSON string
static string GetRequest(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
try
{
WebResponse response = request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, System.Text.Encoding.UTF8);
return reader.ReadToEnd();
}
}
catch (WebException ex)
{
WebResponse errorResponse = ex.Response;
using (Stream responseStream = errorResponse.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, System.Text.Encoding.GetEncoding("utf-8"));
String errorText = reader.ReadToEnd();
// log errorText
}
throw;
}
}
public static double CalculateSimilarity(string source, string target)
{
if ((source == null) || (target == null)) return 0.0;
if ((source.Length == 0) || (target.Length == 0)) return 0.0;
if (source == target) return 1.0;

int stepsToSame = ComputeLevenshteinDistance(source, target);
return (1.0 - ((double)stepsToSame / (double)Math.Max(source.Length, target.Length)));
}
public static int ComputeLevenshteinDistance(string source, string target)
{
if ((source == null) || (target == null)) return 0;
if ((source.Length == 0) || (target.Length == 0)) return 0;
if (source == target) return source.Length;

int sourceWordCount = source.Length;
int targetWordCount = target.Length;

// Step 1
if (sourceWordCount == 0)
return targetWordCount;

if (targetWordCount == 0)
return sourceWordCount;

int[,] distance = new int[sourceWordCount + 1, targetWordCount + 1];

// Step 2
for (int i = 0; i <= sourceWordCount; distance[i, 0] = i++) ;
for (int j = 0; j <= targetWordCount; distance[0, j] = j++) ;

for (int i = 1; i <= sourceWordCount; i++)
{
for (int j = 1; j <= targetWordCount; j++)
{
// Step 3
int cost = (target[j - 1] == source[i - 1]) ? 0 : 1;

// Step 4
distance[i, j] = Math.Min(Math.Min(distance[i - 1, j] + 1, distance[i, j - 1] + 1), distance[i - 1, j - 1] + cost);
}
}

return distance[sourceWordCount, targetWordCount];
}
public static string FormatSearchTerm(string fileName)
{
//Get only filename from full path
fileName = fileName.Split('\\')[fileName.Split('\\').Length - 1];
string searchTerm = fileName;

//Remove MP3 extension if present
searchTerm = Regex.Replace(searchTerm, ".mp3", "", RegexOptions.IgnoreCase).Trim();

//Remove all non-alpha characters
Regex regEx = new Regex("[^a-zA-Z ]");
searchTerm = regEx.Replace(searchTerm, "");

//Remove duplicate spaces
RegexOptions options = RegexOptions.None;
Regex regex = new Regex("[ ]{2,}", options);
searchTerm = regex.Replace(searchTerm, " ");

return searchTerm.Trim().Replace(" ", "+");
}
}
}

Download

Visual Studio project download

https://bitbucket.org/svermaak/ismp3song/downloads/

Binary download

http://blog.ittelligence.com/wp-content/uploads/2018/03/IsMP3Song.zip

 

Active Directory – Securely Set Local Account Passwords

How it works

A token is generated for a supplied account with the desired password. 

Example of a token: k8vVeIYZeI+6rkvlvw8eLOEnHK2yTcBfHQP4UEZrCgigcagy7+qt969LISkmHH/7CS5KfVWLEZh8cZMzCkVYGw==

This token (an AES-256 encrypted version of the username and the password) is passed to the SecurelySetPassword tool which is executed at start-up via an Active Directory Group Policy.

The token is decrypted and used to set the password for the specified account to the desired password.

Step 1: Download

Download SecurelySetPassword tool

Step 2: Create and Test Token

1) Run SecurelySetPassword.exe USERNAME PASSWORD (Note how the generated token is different on each run, this is because the value is salted for added security)

2) Copy token. It will be used in the implementation steps

3) To test the token, run SecurelySetPassword.exe TOKEN (Note for a successful test the user needs to exists)

Step 3: Copy SecurelySetPassword.exe to a network share

Copy SecurelySetPassword.exe to a network share accessible by all users (such as NETLOGON share)

Step 4: Implement Active Directory Group Policy

1) Start Microsoft Group Policy Management Console (GPMC.msc

2) Create and link a new Group Policy with the desired scope

3) Browse to Computer Configuration > Preferences > Windows Settings > Files and add a new file object

4) Set the Source files(s) path to the location of SecurelySetPassword.exe (\\ittelligence.com\NETLOGON\Software\SecurelySetPassword\SecurelySetPassword.exe in my case)

5) Set the Destination file to %CommonAppdataDir%\SecurelySetPassword\SecurelySetPassword.exe

6) Browse to Computer Configuration > Preferences > Control Panel Settings > Scheduled Tasks and add a new scheduled task object 

7) On the Triggers tab create new trigger and set to At startup

8) On the Actions tab create a Start a program action to %CommonAppdataDir%\SecurelySetPassword\SecurelySetPassword.exe with token as argument

I hope you found this tutorial useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.

Active Directory – Securely Set Local Account Passwords

Prerequisites: The following assumptions have been made in this tutorial. Readers should have a basic working knowledge of Microsoft Active Directory, SQL Server and Visual Studio software.

Step 1:  Create ACTIVE DIRECTORY SERVICE ACCOUNT

Create an Active directory service account with password reset as well as user account unlock permissions.

Step 2:  Download Visual Studio Project

1) Download the provided source zip file by clicking this link  (See below)

2) Extract and open the project in Visual Studio

Step 3:  Create database

Note: The basic steps for creating the database are listed below. Explaining MS SQL functionality is beyond the scope of this article, but I am happy to answer any questions in the comments section below.

1) From the Open Project in Visual Studio, open ModelSSPR.edmx

2) Right-click on white-space on the diagram page

3) Then select Generate Database from Model as shown below

4) Save the SQL script and use it on Microsoft SQL Server to build the database schema

5) Create an MS SQL user and grant it DB owner rights

step 4:  Modify config file

1) From the open project in Visual Studio

2) Replace the ADConnectionString connection string with the Active Directory LDAP string for the domain used in the Create Active Directory Service Account (Step 1)

3) Replace the SSPREntities connection string with the connection string of the database used in the Create Database (Step 3)

4) Configure ADMembershipProvider to the account created in the Create Active Directory Service Account (Step 1)

5) Replace the appSettings values with the correct information for the domain and account used in the Create Active Directory Service Account (Step 1)

Step 5:  Publish Site

Please Note: Explaining Visual Studio publishing is beyond the scope of this article, but I am happy to answer any questions in the comments section below.

1) From the open project in Visual Studio

2) Publish site with the Visual Studio Publishing wizard

step 6:  Testing Site

Registering password hints

1) Browse to site published in Publish Site (Step 5)

2) Click on Log in

3) Specify the Username and Password for the account to register for self-service password reset.

Note: Username must be in UPN format

4) Create password hints by adding questions and answers

Note: At least four hints need to be specified to utilize the self-service password reset function.

Self-Service Password Reset Request

1) Browse to the site published in the Publish Site (Step 5)

2) Click on Reset Password

3) Enter the Username for the account to reset the password for as shown below

Note: Username must be in UPN format

4) Enter answers to the security questions and provide new password

Note: Three random questions will be selected out of the hints configured

5) Click Reset Password

6) If the password was successfully reset, the following screen will display

I hope you found this tutorial useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.