RadioFreeMonkey 1.2

Download and get help for different MediaMonkey Addons.

Moderators: Peke, Gurus

Risser
Posts: 184
Joined: Thu Mar 03, 2005 11:28 am

RadioFreeMonkey 1.2

Post by Risser » Fri Sep 16, 2005 10:15 am

A minor update to Monkey Radio:
- The 'boost' from being in the library for 30+days no longer can push the weighting over the original rating. This means that stuff that was rated 2 stars won't slowly creep up into 5 star range as time goes by and they aren't getting played.
- No longer use the Left function
- Actually implemented the ReduceIfPlayed flag. If this is false, all items will simply be weighted based on their ratings, no messing around with play-counts and dates.

I can see that, if you have listened to an album 50 times over the last year since you first added it to your library, the weighting for those songs will be zero or less (negative). This means they will never come up. This isn't usually a problem for me, because I have a very large library, and the work computer I usually use it on has a lot of churn, with songs being added and removed almost every day.

If this is causing a problem for you, you will have to either (a) turn off ReduceIfPlayed (set it to false) or (b) remove and re-add your tunes to reset the playcount to 0. Simply moving them to another directory out of MM's view, then back should do it.

I've looked into two-for and three-for artist blocks, but that involves extra spinning through the list of tunes, which is okay, but also a lot of checking to make sure that it's not picking the same song it just picked, and that you actually have a second song by that artist.

If you didn't mind whether the second or third song were weighted, it might be easier... I'll think about it.

Meanwhile, enjoy!
Peter



===

' RadioFreeMonkey
' Version 1.2
' A script to create a "radio station" for you based on your song ratings.

' This script creates a root node in the tree, beneath the library node, called "RadioFreeMonkey".
' Under this node, you have a Radio List node, a Done node and a Weightings node.
' - The Radio List node lists 20 songs, in random order. These are weighted, so songs
' with a higher weighting have a higher chance of getting selected.
' - If you have selected a sorting column, even though the songs are selected randomly, the
' radio list will be sorted based on that column. To return the list to "random" sorting, click
' on any playlist (Now Playing works nicely). When you visit the Radio List node, the songs will
' be unsorted, in true random order.
' - The Done list shows you which songs aren't going to be played. This includes songs that have
' passed their maximum playcount, or anything within MinDaysRepeat.
' - The Weighting node shows all tunes, sorted by their calculated weights. This can help you
' determine what the optimal weighting choices are for you. Plus, it's just nice to see what's
' more likely to be played.

' Songs are weighted as follows:
' Rating * 2 (5 stars = 10, 3.5 stars = 7, 0 stars = 0)
' - Number of Plays (if Reduce if Played is TRUE)
' + Days since added to library / DayFactor (always rounded down)
' However, the weighting can't be more than the original rating (* 2).

' TO DO: At some point, I'd like to make the main 'list' node a PlayList instead of a regular
' list of tracks, but I'm not sure how to do this.
'
' This script can be freely used and modified.
' This is an early release and there may be bugs. If it's causing you problems, simply delete
' it or move it out of the scripts\auto folder.
'
' The script does not modify the database, registry or INI file.

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
' Global Variables and Declarations
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Option Explicit

' %%% The caption for the root node.
Const RootNodeCaption = "RadioFreeMonkey"

' %%% Add 1 to weighting for each X days since added to library
Const DayFactor = 30

' %%% Anything rated this or below will not receive the 'date boost'
Const DateBoostCutoff = 1.5

' %%% The minimum number of days that must pass before a song is repeated. Zero means, go ahead
' and repeat it right away.
Const MinDaysRepeat = 1

' %%% Reduce the weighting by the number of times played. This means, as songs are played more often,
' they are less likely to be played again
Const ReduceIfPlayed = True

' %%% Number of Songs in list
Const NumberOfSongs = 20




'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
' The Meat. Don't change anything under here.
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

' the text of the formulas to be used throughout
Dim weightedRatingFormula, dateBoostFormula, ratingFormula, weightedPlayCountFormula
dateBoostFormula = "IIF(Songs.rating <= "&(DateBoostCutoff*20)&", 0, FIX(DateDiff('d',Songs.DateAdded,Now) / "&DayFactor&"))"
ratingFormula = "(IIF(Songs.rating < 0, 0, Songs.rating) / 10)"

If ReduceIfPlayed Then
weightedPlayCountFormula = "(Songs.PlayCounter - " & dateBoostFormula & ")"
Else
weightedPlayCountFormula = "0"
End If

weightedRatingFormula = ratingFormula & " - IIF(" & weightedPlayCountFormula & "<0,0," & weightedPlayCountFormula & ")"


Sub FillStandardProperties(parentNode, childNode)
With childNode
.CustomNodeId = parentNode.CustomNodeId
.CustomDataId = parentNode.CustomDataId + 1
.UseScript = Script.ScriptPath
End With
End Sub


Sub FillGoodLeaf(Node)
Randomize

Dim SplitCustomData, Tracks
Dim SQLCondition, SQLStatement
Dim SELECT_Clause, FROM_Clause, WHERE_Clause, ORDER_Clause
Dim Iter, res, i, total, sum, index, weight
Dim hold, weights
Set hold = CreateObject("Scripting.Dictionary")
Set weights = CreateObject("Scripting.Dictionary")

SELECT_Clause = " SELECT Songs.Id, "&weightedRatingFormula&" "
FROM_Clause = " FROM Songs "
' WHERE_Clause = " WHERE Songs.PlayCounter < "&weightedRatingFormula&" "
WHERE_Clause = " WHERE " & weightedPlayCountFormula &" < " & ratingFormula & " AND DateDiff('d',Songs.LastTimePlayed, Now) > " & MinDaysRepeat
SQLStatement = SELECT_Clause & FROM_Clause & WHERE_Clause
Set Iter = SDB.Database.OpenSQL(SQLStatement)

total=0
sum = 0
While Not Iter.EOF
hold.add total,Iter.StringByIndex(0)
weights.add total,Iter.StringByIndex(1)
total = total + 1
sum = sum + Iter.StringByIndex(1)
Iter.Next
Wend

Dim R, max : max = NumberOfSongs
If (total < max) Then
max = total
End If

Dim inStr : inStr = ""
Set Tracks = SDB.MainTracksWindow
While i < max
' R = Round((rnd() * (total+1))-0.5, 0)
R = rnd() * sum
For index = 0 to total
If R < cdbl(weights.item(index)) Then
Exit For
Else
R = R - cdbl(weights.item(index))
End If
Next 'index
If i = 0 Then
inStr = index
Else
inStr = inStr & ", " & hold.item(index)
End If
i = i + 1
Wend
'res = SDB.MessageBox(inStr, mtError, Array(mbOk))

Tracks.AddTracksFromQuery("AND Songs.ID IN (" & inStr & ") ORDER BY Rnd((1000*Songs.ID)*Now())")
Tracks.FinishAdding
' function WeightedRandom(const Weights : array of Double) : Integer ;
' var R : Double ;
' begin
' R := Random * Sum(Weights) ;
' for Result := 0 to High(Weights) do
' if (R < Weights[Result]) then BREAK
' else R := R - Weights[Result] ;
' end ;

End Sub

Sub FillWeightNode(Node)
Dim SplitCustomData ' Auxiliary Array used to collect the pieces of Node.CustomData
Dim SQLLinking ' Piece of SQL Code (WHERE statement) defining the relations between tables
Dim SQLTables ' Part of FROM clause indicating which tables to query
Dim SQLCondition ' String containing the part of the WHERE statement coming from the ancestor nodes
Dim fullMask, curLevelMask
Dim Tree, newNode, nextIsLeaf
Dim FldTxt ' Text identifying the field by which the created nodes will be filtered
Dim Field, IdField, OrderField ' Fields to be queried
' Variables that store qualifier values and related expressions
Dim TopQualifier, TopClause, SortCondition, minTracks, maxTracks, trimValue, doFormatting
Dim ContentIndex ' An index to the field to be displayed
Dim SELECT_Clause, FROM_Clause, WHERE_Clause, GROUP_BY_Clause, ORDER_BY_Clause, HAVING_Clause
Dim SQLStatement ' SQL query to the database
Dim CaptionPrefix ' Used to modify the caption when a sorting has been specified
Dim EscapedId ' Used to escape a text value in the database to be used as a test
Dim idArgument ' The first argument submitted to the Format function
Dim Iter ' SDBD Iterator obtained by running the SQL query to get the nodes


Set Tree = SDB.MainTree
Node.HasChildren = false ' To delete all old children

SQLStatement = "SELECT DISTINCT " & weightedRatingFormula & " from Songs "

Set Iter = SDB.Database.OpenSQL(SQLStatement)

While Not Iter.EOF

Set newNode = Tree.CreateNode

NewNode.Caption = Iter.StringByIndex(0)
NewNode.iconIndex = 32
newNode.CustomData = Iter.StringByIndex(0)

FillStandardProperties node,newNode
newNode.onFillTracksFunct = "FillWeightLeaf"
newNode.hasChildren = False
Tree.AddNode Node, NewNode, 3

Iter.Next
Wend

End Sub


Sub FillWeightLeaf(Node)
Dim Weight
Dim Tracks
Dim SELECT_Clause, FROM_Clause, WHERE_Clause

Weight = Node.CustomData

SELECT_Clause = " SELECT Songs.Id "
FROM_Clause = " FROM Songs "
WHERE_Clause = " WHERE " & weightedRatingFormula & " = " & Weight & " AND DateDiff('d',Songs.LastTimePlayed, Now) > " & MinDaysRepeat

Set Tracks = SDB.MainTracksWindow

Tracks.AddTracksFromQuery("AND Songs.ID IN (" & SELECT_Clause & FROM_Clause & WHERE_Clause & ")")
Tracks.FinishAdding

End Sub


Sub FillDoneLeaf(Node)
Dim Tracks
Dim SELECT_Clause, FROM_Clause, WHERE_Clause


SELECT_Clause = " SELECT Songs.Id "
FROM_Clause = " FROM Songs "
WHERE_Clause = " WHERE "& weightedPlayCountFormula &" >= " & ratingFormula & " OR DateDiff('d',Songs.LastTimePlayed, Now) <= " & MinDaysRepeat

Set Tracks = SDB.MainTracksWindow

Tracks.AddTracksFromQuery("AND Songs.ID IN (" & SELECT_Clause & FROM_Clause & WHERE_Clause & ")")
Tracks.FinishAdding

End Sub



'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
' Startup Function
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Sub onStartUp
Dim Tree, RadioFreeMonkeyRoot
Dim RadioMonkeyGood, RadioMonkeyBad, RadioMonkeyWeight

Set Tree = Sdb.MainTree
Set RadioFreeMonkeyRoot = Tree.createNode

RadioFreeMonkeyRoot.Caption = RootNodeCaption
RadioFreeMonkeyRoot.IconIndex = 14
RadioFreeMonkeyRoot.UseScript = Script.ScriptPath
RadioFreeMonkeyRoot.hasChildren = True
Tree.AddNode Tree.Node_Library, RadioFreeMonkeyRoot, 1
SDB.Objects("RadioFreeMonkeyRoot") = RadioFreeMonkeyRoot

Set RadioMonkeyGood = Tree.createNode
RadioMonkeyGood.Caption = "Radio List"
RadioMonkeyGood.IconIndex = 14
RadioMonkeyGood.UseScript = Script.ScriptPath
RadioMonkeyGood.hasChildren = False
RadioMonkeyGood.onFillTracksFunct = "FillGoodLeaf"
Tree.AddNode RadioFreeMonkeyRoot, RadioMonkeyGood, 2
SDB.Objects("RadioMonkeyGood") = RadioMonkeyGood

Set RadioMonkeyWeight = Tree.createNode
RadioMonkeyWeight.Caption = "Weightings"
RadioMonkeyWeight.IconIndex = 32
RadioMonkeyWeight.UseScript = Script.ScriptPath
RadioMonkeyWeight.hasChildren = True
RadioMonkeyWeight.onFillTracksFunct = "FillWeightNode"
Tree.AddNode RadioFreeMonkeyRoot, RadioMonkeyWeight, 3
SDB.Objects("RadioMonkeyWeight") = RadioMonkeyWeight

Set RadioMonkeyBad = Tree.createNode
RadioMonkeyBad.Caption = "Done"
RadioMonkeyBad.IconIndex = 15
RadioMonkeyBad.UseScript = Script.ScriptPath
RadioMonkeyBad.hasChildren = False
RadioMonkeyBad.onFillTracksFunct = "FillDoneLeaf"
Tree.AddNode RadioFreeMonkeyRoot, RadioMonkeyBad, 3
SDB.Objects("RadioMonkeyBad") = RadioMonkeyBad
End Sub

rovingcowboy
Posts: 14153
Joined: Sat Oct 25, 2003 7:57 am
Location: (Texas)
Contact:

Post by rovingcowboy » Tue Sep 27, 2005 4:15 pm

wow this is a lot to take in?

how do you put this in monkey?

right now i am using the windows scheduler script from peke and the auto play count to keep the jingles out of the top 50 list.

but i have notice that the rateing for songs was preventing some from being played at all.

so i made them all 5 stars for the whole librairy

would this script change them back to ratings on how many times they were played?

it does seem like a good idea but i also have a huge librairy but i don't remove songs i keep adding :D i have not heard some in weeks.
but i then also only use the shuffle button on the monkeys player.
it is better then winamp's shuffle. and using the widows scheduler script it only works when using the monkey player not winamp.

so is this script for radio monkey going to do me any good or will it just be causing me to do the same thing i already am doing?

:-?
roving cowboy / keith hall. My skins http://www.mediamonkey.com/forum/viewto ... =9&t=16724 for some help check on Monkey's helpful messages at http://www.mediamonkey.com/forum/viewto ... 4008#44008 MY SYSTEMS.1.Jukebox WinXp pro sp 3 version 3.5 gigabyte mb. 281 GHz amd athlon x2 240 built by me.) 2.WinXP pro sp3, vers 2.5.5 and vers 3.5 backup storage, shuttle 32a mb,734 MHz amd athlon put together by me.) 3.Dell demension, winxp pro sp3, mm3.5 spare jukebox.) 4.WinXp pro sp3, vers 3.5, dad's computer bought from computer store. )5. Samsung Galaxy 5 Android 5) 6. Proscan tablet Android 4.3 ) 7. amd a8-5600 apu 3.60ghz mm version 4 windows 7 pro bought from computer store.

psyXonova
Posts: 785
Joined: Fri May 20, 2005 3:57 am
Location: Nicosia, Cyprus
Contact:

Post by psyXonova » Wed Sep 28, 2005 4:09 am

Why don't you give it a try and see if it does your job. After all this script doesnt modify database or files in any way so you are safe.
Don't forget you are the only one that knows what exactly wants to do, and so you are the only one that can answer your question...

rovingcowboy
Posts: 14153
Joined: Sat Oct 25, 2003 7:57 am
Location: (Texas)
Contact:

Post by rovingcowboy » Wed Sep 28, 2005 3:01 pm

psyxonova wrote:Why don't you give it a try and see if it does your job. After all this script doesnt modify database or files in any way so you are safe.
Don't forget you are the only one that knows what exactly wants to do, and so you are the only one that can answer your question...

:lol: :o

so in other words you don't know either :P
roving cowboy / keith hall. My skins http://www.mediamonkey.com/forum/viewto ... =9&t=16724 for some help check on Monkey's helpful messages at http://www.mediamonkey.com/forum/viewto ... 4008#44008 MY SYSTEMS.1.Jukebox WinXp pro sp 3 version 3.5 gigabyte mb. 281 GHz amd athlon x2 240 built by me.) 2.WinXP pro sp3, vers 2.5.5 and vers 3.5 backup storage, shuttle 32a mb,734 MHz amd athlon put together by me.) 3.Dell demension, winxp pro sp3, mm3.5 spare jukebox.) 4.WinXp pro sp3, vers 3.5, dad's computer bought from computer store. )5. Samsung Galaxy 5 Android 5) 6. Proscan tablet Android 4.3 ) 7. amd a8-5600 apu 3.60ghz mm version 4 windows 7 pro bought from computer store.

psyXonova
Posts: 785
Joined: Fri May 20, 2005 3:57 am
Location: Nicosia, Cyprus
Contact:

Post by psyXonova » Thu Sep 29, 2005 2:15 am

I am a user of RadioFreeMOnkey for quite a long time. I know what it does but i'm not sure what you want, that's why i say give it a try
:lol: :P

charlieMOGUL
Posts: 168
Joined: Fri Mar 11, 2005 5:26 am

Post by charlieMOGUL » Thu Sep 29, 2005 6:04 pm

Risser,

Thanks for the update on the RadioFreeMonkey script. I use it all the time and enjoy your programming.
A feature request for version 1.3: could you implement a 'factor' that adds random songs to the playlist as well ?
eg: unrateFactor = 5, then 5% of the songs of the RadioFreeMonkey playlist are truly random (regardless to their rating or number of times played).
The reason I ask for this is that usually not all tracks are rated. You rate songs when you hear them. But when you don't hear the tracks, you won't rate them. Hence...

Once again thanks for your script: let the monkey play FREE!

charlieMOGUL

popper
Posts: 34
Joined: Mon Feb 21, 2005 5:10 pm
Location: Germany

RadioFreeMonkey v1.5

Post by popper » Mon Apr 03, 2006 4:19 pm

I've been using this script for quite some time now. When Auto-DJ became available, I switched over to that, only to find that that it could not do the same for me as RadioFreeMonkey did! So thank you for that great script, Risser.

Still, there were some things I was missing here. Over the time, I've made some modifications and added some features to it. These are:

- added possibility to boost songs that have been added to the DB recently (like a radio station would play new songs more often)
- added possibility to define the genres that should be played (so that classic and pop won't be mixed)
- redesign of the mechanism that selects the actual songs to be played (because I did not really understand what was going on in the existing algorithm :lol:)
- now it is possible to influence more easily what songs you will hear (e.g. 30% of songs with a weighting of 8-10 and 70% with a weighting of 5-7).

You will probably have to do some tweaking of the parameters, the ones below are giving me sensible results.

My programming skills are not very good and I don't really know VBScript, but by looking closely at the code and a lot of debugging I found out what was going on and was able to make the modifications I wanted. I learned a lot from this already, but would be glad if one of you Real Programmers(tm) out there could do some kind of code review for me to point out what could be done better.

For installation, just copy the code below into a text file, name it RadioFreeMonkey.vbs and put it in your [...]\MediaMonkey\Scripts\Auto directory. Make your modifications to the parameters at the top of the script. They all have comments so it should be easy to work out what their intention is.
Then save the script and restart MediaMonkey.

I hope you like it.
Thanks again to Risser for the original version.

popper.

Code: Select all


' RadioFreeMonkey
' Version 1.5
' A script to create a "radio station" for you based on your song ratings.

' #####################
' Version 1.2 by Risser

' This script creates a root node in the tree, beneath the library node, called "RadioFreeMonkey".
' Under this node, you have a Radio List node, a Done node and a Weightings node.
' - The Radio List node lists 20 songs, in random order. These are weighted, so songs
' with a higher weighting have a higher chance of getting selected.
' - If you have selected a sorting column, even though the songs are selected randomly, the
' radio list will be sorted based on that column. To return the list to "random" sorting, click
' on any playlist (Now Playing works nicely). When you visit the Radio List node, the songs will
' be unsorted, in true random order.
' - The Done list shows you which songs aren't going to be played. This includes songs that have
' passed their maximum playcount, or anything within MinDaysRepeat.
' - The Weighting node shows all tunes, sorted by their calculated weights. This can help you
' determine what the optimal weighting choices are for you. Plus, it's just nice to see what's
' more likely to be played.

' Songs are weighted as follows:
' Rating * 2 (5 stars = 10, 3.5 stars = 7, 0 stars = 0)
' - Number of Plays (if Reduce if Played is TRUE)
' + Days since added to library / DayFactor (always rounded down)
' However, the weighting can't be more than the original rating (* 2).

' TO DO: At some point, I'd like to make the main 'list' node a PlayList instead of a regular
' list of tracks, but I'm not sure how to do this.
'
' This script can be freely used and modified.
' This is an early release and there may be bugs. If it's causing you problems, simply delete
' it or move it out of the scripts\auto folder.
'
' The script does not modify the database, registry or INI file.

' #####################
' Version 1.5 by popper

' - added possibility to boost songs that have been added to the DB recently
' - added possibility to define the genres that should be played
' - redesign of the mechanism that selects the actual songs to be played (because I did not 
'   really understand what was going on in the existing algorithm ;-), with the goal of making
'   it easier to influence what will be played (e.g. 30% of songs with a weighting of 8-10 and 
'   70% with a weighting of 5-7))
'
' Disclaimer:
' Use at your own risk. Back up your data before using.
' Changing some of the values might lead to endless loops that can only be solved by
' killing the MediaMonkey process, so take care when modifying anything!

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
' Global Variables and Declarations
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Option Explicit

' %%% The caption for the root node.
Const RootNodeCaption = "RadioFreeMonkey 1.5"

' %%% Add 1 to weighting for each X days since added to library
Const DayFactor = 150

' %%% Anything rated this or below will not receive the 'date boost'
Const DateBoostCutoff = 1.5

' %%% The minimum number of days that must pass before a song is repeated. Zero means, go ahead
' and repeat it right away.
Const MinDaysRepeat = 5

' %%% Reduce the weighting by the number of times played. This means, as songs are played more often,
' they are less likely to be played again
Const ReduceIfPlayed = True

' %%% Boost Songs that have been added during the last n days (MinDaysRepeat does still apply for these)
Const BoostNewSongsDays = 75

' %%% The boost modifier for new songs (Zero means, don't boost): 
' Add x to the weighting of these songs
Const BoostNewSongsModifier = 3

' %%% Anything rated this or below will not receive the 'new song boost'
Const BoostNewSongsCutoff = 2.0

' %%% Genres that should be played. Leave empty (Const PlayGenres = "") to ignore. 
' "-1, 13, 17, 20, 24" means "empty genre field, Pop, Rock, Alternative, Soundtrack"
' Look up the other genre Ids in the database by using MS Access
Const PlayGenres = "-1, 13, 17, 20, 24"

' %%% Number of Songs in list
Const NumberOfSongs = 20


' %%% Influence which songs will be played. You have three sets that you can use
' Example:
' Const Set1Weight = 8.5    ' Basic weight for this set is 8.5
' Const Set1Boundary = 1.5  ' with a variation of 1.5, which means that songs between 7 and 10 will be chosen
' Const Set1Percentage = 60 ' this is how big the part of this set should be in the overall contents

Const Set1Weight = 8.5
Const Set1Boundary = 1.5
Const Set1Percentage = 60 	' make sure all 3 percentages sum up to 100!

Const Set2Weight = 6.0
Const Set2Boundary = 1
Const Set2Percentage = 40 	' make sure all 3 percentages sum up to 100!

Const Set3Weight = 2.0		' Right now, it is not possible to choose songs with a weight < 1, so unfortunately you cannot add unrated songs
Const Set3Boundary = 1
'Const Set3Percentage = 20  ' This is not needed because it just takes what is missing to 100%


'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
' The Meat. Don't change anything under here.
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

' the text of the formulas to be used throughout
Dim weightedRatingFormula, dateBoostFormula, ratingFormula, weightedPlayCountFormula, boostNewSongsFormula
If BoostNewSongsModifier <> 0 Then
   dateBoostFormula = "IIF(Songs.rating <= " & (DateBoostCutoff*20) & " OR FIX(DateDiff('d',Songs.DateAdded,Now) <= " & BoostNewSongsDays & "), 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))"
Else
   dateBoostFormula = "IIF(Songs.rating <= " & (DateBoostCutoff*20) & ", 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))"
End If

ratingFormula = "(IIF(Songs.rating < 0, 0, Songs.rating) / 10)"

If ReduceIfPlayed Then
	weightedPlayCountFormula = "(Songs.PlayCounter - " & dateBoostFormula & ")" 
Else
	weightedPlayCountFormula = "0"
End If

If BoostNewSongsModifier <> 0 Then
	boostNewSongsFormula = "IIF(Songs.rating <= " & (BoostNewSongsCutoff*20) & ", 0, (IIF(FIX(DateDiff('d',Songs.DateAdded,Now) > " & BoostNewSongsDays & "), 0, " & BoostNewSongsModifier & ")))"
End If

weightedRatingFormula = ratingFormula & " - IIF(" & weightedPlayCountFormula & " < 0, 0, " & weightedPlayCountFormula & ") + " & boostNewSongsFormula


Sub FillStandardProperties(parentNode, childNode)
	With childNode
		.CustomNodeId = parentNode.CustomNodeId
		.CustomDataId = parentNode.CustomDataId + 1
		.UseScript = Script.ScriptPath
	End With
End Sub


Sub FillGoodLeaf(Node)
	Randomize

	Dim Tracks
	Dim SQLStatement
	Dim SELECT_Clause, FROM_Clause, WHERE_Clause, ORDER_Clause
	Dim Iter, res, i, total, sum, index
	Dim weight, minweight, maxweight
	Dim hold, weights, average, start, baseweight, boundary
	Dim R, max : max = NumberOfSongs
	Dim inStr : inStr = ""

	Set hold = CreateObject("Scripting.Dictionary") ' define an associative array or "hash array" to hold the songs
	Set weights = CreateObject("Scripting.Dictionary") ' define an associative array or "hash array" to hold the weightings

	
	SELECT_Clause = " SELECT Songs.Id, " & weightedRatingFormula & " "
	FROM_Clause = " FROM Songs "
	WHERE_Clause = " WHERE " & weightedPlayCountFormula &" < " & ratingFormula & " AND DateDiff('d',Songs.LastTimePlayed, Now) > " & MinDaysRepeat 
	If PlayGenres <> "" then
		WHERE_Clause = WHERE_Clause & " AND Songs.Genre IN (" & PlayGenres & ")"
	End If
	ORDER_Clause = " ORDER BY Rnd((1000*Songs.ID)*Now())" ' This is important because otherwise they will all come as they are stored in the DB
	
	SQLStatement = SELECT_Clause & FROM_Clause & WHERE_Clause & ORDER_Clause
	'res = InputBox("SQL Statement: ", "debugging", SQLStatement) ' For debugging
	
	Set Iter = SDB.Database.OpenSQL(SQLStatement) ' holds all songs that are collected by the SQL formula

	total=0 ' initialise total amount of songs that can be used for the playlist (=all songs with weights from 1 to 10 minus "done" songs)
	sum = 0 ' initialise sum of all weightings
	maxweight = 0 ' initialise maximum weight in this iteration
	minweight = 9 ' initialise minimum weight in this iteration
	
	While (Not Iter.EOF)
		hold.add total,Iter.StringByIndex(0)
		weight = Iter.StringByIndex(1)
		weights.add total,weight
		total = total + 1
		sum = sum + weight
		If minweight > cdbl(weight) then
			minweight = cdbl(weight)
		End if
		if maxweight < cdbl(weight) then
			maxweight = cdbl(weight)
		End if

  		Iter.Next
	Wend

	' some overflow checks
	if total <= 0 or sum <= 0 then 
		SDB.MessageBox "Something is wrong with your ratings. Do you have any rated songs at all?", mtInformation, Array(mbOk)
		Exit Sub
	end if
	If Set1Weight < minweight or Set2Weight < minweight or Set3Weight < minweight or Set1Weight > maxweight or Set2Weight > maxweight or Set3Weight > maxweight then
		SDB.MessageBox "Something is wrong with your SetXWeight constants. They are either too high or too low.", mtInformation, Array(mbOk)
		Exit Sub
	end if
	
	If (total < max) Then
  		max = total
	End If
      
	Set Tracks = SDB.MainTracksWindow
	start = 0
	
	While i < max ' do this until the max number of songs for the playlist is reached
	
		If i < max * Set1Percentage / 100 then ' put in Set1Percentage percent of higher-rated songs
			baseweight = Set1Weight 
			boundary = Set1Boundary
		elseif i < max * (Set1Percentage + Set2Percentage) / 100 then ' put in Set2Percentage percent of lower-rated songs
			baseweight = Set2Weight
			boundary = Set2Boundary
		elseif i <= max then ' and also put in some really lowly-rated songs
			baseweight = Set3Weight 
			boundary = Set3Boundary
		end if
				
		For index = start to total ' go through all of the songs that could potentially become part of the radio node, starting where we left off last time
			weight = cdbl(weights.item(index)) ' weight of the song that is being looked at right now
				
			if weight >= (baseweight - boundary) and weight <= (baseweight + boundary) then
				' This song is inside the weighting borders that are defined above
				' so let's see if it is also lucky
				R = rnd(1) ' this song's lucky number
				if R > 0.8 then
					' so let's leave this for-next loop and add it to the list!
					Exit For
				end if
			end if
		Next	 
		
		start = index + 1 ' We don't want to go through the same songs again, so next time we start from here
		if start > total then
			if inStr = "" then
				SDB.MessageBox "There does not seem to be enough stuff to work on. Try to use criteria that are easier to meet.", mtInformation, Array(mbOk)
				Exit Sub
			else
				start = 0 ' start over again
			end if
		else
			' We've got a winner! The song with the hash index "index" will now be added to the radio node
			' This is done by collecting all songs in inStr, separating them by commas, and afterwards collecting their song.ids via SQL  		
  			If i = 0 Then
    			inStr = index
  			Else
    			inStr = inStr & ", " & hold.item(index)
  			End If
  			i = i + 1
		end if	

	Wend

	If inStr <> "" then
		Tracks.AddTracksFromQuery("AND Songs.ID IN (" & inStr & ") ORDER BY Rnd((1000*Songs.ID)*Now())")
		Tracks.FinishAdding
	End If
		
End Sub





Sub FillWeightNode(Node)

	Dim Tree, newNode
	Dim SQLStatement ' SQL query to the database
	Dim Iter ' SDBD Iterator obtained by running the SQL query to get the nodes

	Set Tree = SDB.MainTree
	Node.HasChildren = false ' To delete all old children

	SQLStatement = "SELECT DISTINCT " & weightedRatingFormula & " from Songs "
	'res = InputBox("SQL Statement: ", SQLStatement, SQLStatement) ' For debugging
	Set Iter = SDB.Database.OpenSQL(SQLStatement)

	While Not Iter.EOF

		Set newNode = Tree.CreateNode

		NewNode.Caption = Iter.StringByIndex(0)
		NewNode.iconIndex = 32
		newNode.CustomData = Iter.StringByIndex(0)

		FillStandardProperties node,newNode
		newNode.onFillTracksFunct = "FillWeightLeaf"
		newNode.hasChildren = False
		Tree.AddNode Node, NewNode, 3

		Iter.Next
	Wend

End Sub


Sub FillWeightLeaf(Node)
	Dim Weight
	Dim Tracks
	Dim SELECT_Clause, FROM_Clause, WHERE_Clause

	Weight = Node.CustomData

	SELECT_Clause = " SELECT Songs.Id "
	FROM_Clause = " FROM Songs "
	WHERE_Clause = " WHERE " & weightedRatingFormula & " = " & Weight & " AND DateDiff('d',Songs.LastTimePlayed, Now) > " & MinDaysRepeat 
	If PlayGenres <> "" then
		WHERE_Clause = WHERE_Clause & " AND Songs.Genre IN (" & PlayGenres & ")"
	End If
	'res = InputBox("SQL Statement: ", "Debugging", SELECT_Clause & FROM_Clause & WHERE_Clause) ' For debugging
	
	Set Tracks = SDB.MainTracksWindow

	Tracks.AddTracksFromQuery("AND Songs.ID IN (" & SELECT_Clause & FROM_Clause & WHERE_Clause & ")")
	Tracks.FinishAdding

End Sub


Sub FillDoneLeaf(Node)
	Dim Tracks
	Dim SELECT_Clause, FROM_Clause, WHERE_Clause


	SELECT_Clause = " SELECT Songs.Id "
	FROM_Clause = " FROM Songs "
	WHERE_Clause = " WHERE "& weightedPlayCountFormula &" >= " & ratingFormula & " OR DateDiff('d',Songs.LastTimePlayed, Now) <= " & MinDaysRepeat 
	If PlayGenres <> "" then
		WHERE_Clause = WHERE_Clause & " OR Songs.Genre NOT IN (" & PlayGenres & ")"
	End If

	Set Tracks = SDB.MainTracksWindow

	Tracks.AddTracksFromQuery("AND Songs.ID IN (" & SELECT_Clause & FROM_Clause & WHERE_Clause & ")")
	Tracks.FinishAdding

End Sub



'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
' Startup Function
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Sub onStartUp
	Dim Tree, RadioFreeMonkeyRoot
	Dim RadioMonkeyGood, RadioMonkeyBad, RadioMonkeyWeight

	Set Tree = Sdb.MainTree
	Set RadioFreeMonkeyRoot = Tree.createNode

	RadioFreeMonkeyRoot.Caption = RootNodeCaption
	RadioFreeMonkeyRoot.IconIndex = 14
	RadioFreeMonkeyRoot.UseScript = Script.ScriptPath
	RadioFreeMonkeyRoot.hasChildren = True
	Tree.AddNode Tree.Node_Library, RadioFreeMonkeyRoot, 1
	SDB.Objects("RadioFreeMonkeyRoot") = RadioFreeMonkeyRoot

	Set RadioMonkeyGood = Tree.createNode
	RadioMonkeyGood.Caption = "Radio List"
	RadioMonkeyGood.IconIndex = 14
	RadioMonkeyGood.UseScript = Script.ScriptPath
	RadioMonkeyGood.hasChildren = False
	RadioMonkeyGood.onFillTracksFunct = "FillGoodLeaf"
	Tree.AddNode RadioFreeMonkeyRoot, RadioMonkeyGood, 2
	SDB.Objects("RadioMonkeyGood") = RadioMonkeyGood

	Set RadioMonkeyWeight = Tree.createNode
	RadioMonkeyWeight.Caption = "Weightings"
	RadioMonkeyWeight.IconIndex = 32
	RadioMonkeyWeight.UseScript = Script.ScriptPath
	RadioMonkeyWeight.hasChildren = True
	RadioMonkeyWeight.onFillTracksFunct = "FillWeightNode"
	Tree.AddNode RadioFreeMonkeyRoot, RadioMonkeyWeight, 3
	SDB.Objects("RadioMonkeyWeight") = RadioMonkeyWeight

	Set RadioMonkeyBad = Tree.createNode
	RadioMonkeyBad.Caption = "Done"
	RadioMonkeyBad.IconIndex = 15
	RadioMonkeyBad.UseScript = Script.ScriptPath
	RadioMonkeyBad.hasChildren = False
	RadioMonkeyBad.onFillTracksFunct = "FillDoneLeaf"
	Tree.AddNode RadioFreeMonkeyRoot, RadioMonkeyBad, 3
	SDB.Objects("RadioMonkeyBad") = RadioMonkeyBad
End Sub

DuQ3r

Post by DuQ3r » Thu May 04, 2006 4:01 pm

When I press Radio Playlist It says "Something is wrong! Do you have any rated songs?"

But I do have reted songs? What does it mean then?
Am I doing something wrong?

popper
Posts: 34
Joined: Mon Feb 21, 2005 5:10 pm
Location: Germany

Post by popper » Sun May 07, 2006 3:06 am

Hmm. Several possibilities come to mind:

- Perhaps you have to play around with the settings at the beginning of the script ("Global Variables and Declarations"), especially the part where the Set1Weight etc. are set. This has to match the ratings that you have given your songs.
- How many songs do you have in your library?
- What happens when you open the node "Weightings" oder "done"?
- Perhaps you use different genres than I did? Then you should in a first step disable the genres by setting the following: Const PlayGenres = ""

Good luck & Let me know how it goes!
popper

fastspinecho

Post by fastspinecho » Sun Aug 20, 2006 1:22 pm

Thanks for the excellent script, risser & popper!

I'm using popper's version, but I think I've come across a small bug: BoostNewSongsCutoff is defined but never actually used. I'm no programmer, but I'll bet that

Code: Select all

If BoostNewSongsModifier <> 0 Then 
   dateBoostFormula = "IIF(Songs.rating <= " & (DateBoostCutoff*20) & " OR FIX(DateDiff('d',Songs.DateAdded,Now) <= " & BoostNewSongsDays & "), 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))" 
Else 
   dateBoostFormula = "IIF(Songs.rating <= " & (DateBoostCutoff*20) & ", 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))" 
End If
should actually read

Code: Select all

If BoostNewSongsModifier <> 0 Then 
   dateBoostFormula = "IIF(Songs.rating <= " & (BoostNewSongsCutoff *20) & " OR FIX(DateDiff('d',Songs.DateAdded,Now) <= " & BoostNewSongsDays & "), 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))" 
Else 
   dateBoostFormula = "IIF(Songs.rating <= " & (DateBoostCutoff*20) & ", 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))" 
End If

fastspinecho

Post by fastspinecho » Sun Aug 20, 2006 1:26 pm

Oh, snap. NVM.

Tylast
Posts: 130
Joined: Sun Jan 29, 2006 12:54 pm
Location: US
Contact:

Post by Tylast » Sat Oct 07, 2006 10:43 am

Feature Request:

How about creates nodes in the browser tree based on the genres & other options popper introduced that we pick? I imagine only needing to make 3 of these nodes.

Also, how about an easier way to pick the genres to be used?
Image

ElGringo
Posts: 6
Joined: Sat Oct 21, 2006 2:04 pm
Location: Val-David, Qc, Canada

Some Fixes

Post by ElGringo » Thu Oct 26, 2006 9:11 pm

Hi !

This script is exactly what i was looking for... Thank you very much.

I picked the last script from popper, and saw a couple of errors that i corrected.

1. There was always a song that had nothing to do in my playlists, investigated and saw the error in FillGoodLeaf :

Code: Select all

' We've got a winner! The song with the hash index "index" will now be added to the radio node 
         ' This is done by collecting all songs in inStr, separating them by commas, and afterwards collecting their song.ids via SQL         
           If i = 0 Then 
             inStr = index
           Else 
             inStr = inStr & ", " & hold.item(index) 
           End If 
           i = i + 1 
If i=0, then It should be hold.item(index), instead of index, so the first song that was added was not good, we were taking the index, instead of the real song.id

2. There is an error in the calculation of the dateboost formula, the dateboostcutoff*20 should be dateboostcutoff*10. There were a couple of places wrong

Code: Select all

If BoostNewSongsModifier <> 0 Then 
   dateBoostFormula = "IIF(Songs.rating <= " & (DateBoostCutoff*20) & " OR FIX(DateDiff('d',Songs.DateAdded,Now) <= " & BoostNewSongsDays & "), 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))" 
Else 
   dateBoostFormula = "IIF(Songs.rating <= " & (DateBoostCutoff*20) & ", 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))" 
End If 
3. By adding, the BoostNewSongsModifier, the actual weight calculated, could go higher than 10, but the algorithm was never picking songs with a weight higher than 10. So, i fixed a couple of places in the script, so that calculated weight higher than 10, are now fixed at 10.

4. There was a bug, when the BoostNewSongsModifier is set to 0... fixed it.


I paste here the complete fixed script :

Code: Select all

' RadioFreeMonkey 
' Version 1.6
' A script to create a "radio station" for you based on your song ratings. 

' ##################### 
' Version 1.2 by Risser 

' This script creates a root node in the tree, beneath the library node, called "RadioFreeMonkey". 
' Under this node, you have a Radio List node, a Done node and a Weightings node. 
' - The Radio List node lists 20 songs, in random order. These are weighted, so songs 
' with a higher weighting have a higher chance of getting selected. 
' - If you have selected a sorting column, even though the songs are selected randomly, the 
' radio list will be sorted based on that column. To return the list to "random" sorting, click 
' on any playlist (Now Playing works nicely). When you visit the Radio List node, the songs will 
' be unsorted, in true random order. 
' - The Done list shows you which songs aren't going to be played. This includes songs that have 
' passed their maximum playcount, or anything within MinDaysRepeat. 
' - The Weighting node shows all tunes, sorted by their calculated weights. This can help you 
' determine what the optimal weighting choices are for you. Plus, it's just nice to see what's 
' more likely to be played. 

' Songs are weighted as follows: 
' Rating * 2 (5 stars = 10, 3.5 stars = 7, 0 stars = 0) 
' - Number of Plays (if Reduce if Played is TRUE) 
' + Days since added to library / DayFactor (always rounded down) 
' However, the weighting can't be more than the original rating (* 2). 

' TO DO: At some point, I'd like to make the main 'list' node a PlayList instead of a regular 
' list of tracks, but I'm not sure how to do this. 
' 
' This script can be freely used and modified. 
' This is an early release and there may be bugs. If it's causing you problems, simply delete 
' it or move it out of the scripts\auto folder. 
' 
' The script does not modify the database, registry or INI file. 

' ##################### 
' Version 1.5 by popper 

' - added possibility to boost songs that have been added to the DB recently 
' - added possibility to define the genres that should be played 
' - redesign of the mechanism that selects the actual songs to be played (because I did not 
'   really understand what was going on in the existing algorithm ;-), with the goal of making 
'   it easier to influence what will be played (e.g. 30% of songs with a weighting of 8-10 and 
'   70% with a weighting of 5-7)) 
' ##################### 
' Version 1.6 by ElGringo

' - Fixes to the algorithm
'   - 1. There was always a song that had nothing to do in my playlists, investigated 
'        and saw the error in FillGoodLeaf
'   - 2. There is an error in the calculation of the dateboost formula, the 
'        dateboostcutoff*20 should be dateboostcutoff*10. There were a couple of places wrong
'   - 3. By adding, the BoostNewSongsModifier, the actual weight calculated, could go higher 
'        than 10, but the algorithm was never picking songs with a weight higher than 10. So, 
'        i fixed a couple of places in the script, so that calculated weight higher than 10, 
'        are now fixed at 10.
'   - 4.There was a bug, when the BoostNewSongsModifier is set to 0... fixed it.
'
' 
' Disclaimer: 
' Use at your own risk. Back up your data before using. 
' Changing some of the values might lead to endless loops that can only be solved by 
' killing the MediaMonkey process, so take care when modifying anything! 

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
' Global Variables and Declarations 
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

Option Explicit 

' %%% The caption for the root node. 
Const RootNodeCaption = "Radio Rock2" 

' %%% Add 1 to weighting for each X days since added to library 
Const DayFactor = 150 

' %%% Anything rated this or below will not receive the 'date boost' 
Const DateBoostCutoff = 1.5 

' %%% The minimum number of days that must pass before a song is repeated. Zero means, go ahead 
' and repeat it right away. 
Const MinDaysRepeat = 5 

' %%% Reduce the weighting by the number of times played. This means, as songs are played more often, 
' they are less likely to be played again 
Const ReduceIfPlayed = True 

' %%% Boost Songs that have been added during the last n days (MinDaysRepeat does still apply for these) 
Const BoostNewSongsDays = 5 

' %%% The boost modifier for new songs (Zero means, don't boost): 
' Add x to the weighting of these songs 
Const BoostNewSongsModifier = 3 

' %%% Anything rated this or below will not receive the 'new song boost' 
Const BoostNewSongsCutoff = 2.0 

' %%% Genres that should not be played. Leave empty (Const PlayGenres = "") to ignore. 
' Look up the other genre Ids in the database by using MS Access 
Const PlayGenres = "-1, 13, 17, 20, 24" 

' %%% Number of Songs in list 
Const NumberOfSongs = 20 


' %%% Influence which songs will be played. You have three sets that you can use 
' Example: 
' Const Set1Weight = 8.5    ' Basic weight for this set is 8.5 
' Const Set1Boundary = 1.5  ' with a variation of 1.5, which means that songs between 7 and 10 will be chosen 
' Const Set1Percentage = 60 ' this is how big the part of this set should be in the overall contents 

Const Set1Weight = 8.5 
Const Set1Boundary = 1.5 
Const Set1Percentage = 60    ' make sure all 3 percentages sum up to 100! 

Const Set2Weight = 6.0 
Const Set2Boundary = 1 
Const Set2Percentage = 40    ' make sure all 3 percentages sum up to 100! 

Const Set3Weight = 2.0      ' Right now, it is not possible to choose songs with a weight < 1, so unfortunately you cannot add unrated songs 
Const Set3Boundary = 1 
'Const Set3Percentage = 20  ' This is not needed because it just takes what is missing to 100% 


'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
' The Meat. Don't change anything under here. 
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

' the text of the formulas to be used throughout 
Dim weightedRatingFormula, dateBoostFormula, ratingFormula, weightedPlayCountFormula, boostNewSongsFormula 

If BoostNewSongsModifier <> 0 Then 
   dateBoostFormula = "IIF(Songs.rating <= " & (BoostNewSongsCutoff *10) & " OR FIX(DateDiff('d',Songs.DateAdded,Now) <= " & BoostNewSongsDays & "), 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))" 
Else 
   dateBoostFormula = "IIF(Songs.rating <= " & (DateBoostCutoff*10) & ", 0, FIX(DateDiff('d',Songs.DateAdded,Now) / " & DayFactor & "))" 
End If

ratingFormula = "(IIF(Songs.rating < 0, 0, Songs.rating) / 10)" 

If ReduceIfPlayed Then 
   weightedPlayCountFormula = "(Songs.PlayCounter - " & dateBoostFormula & ")" 
Else 
   weightedPlayCountFormula = "0" 
End If 

If BoostNewSongsModifier <> 0 Then 
   boostNewSongsFormula = "IIF(Songs.rating <= " & (BoostNewSongsCutoff*10) & ", 0, (IIF(FIX(DateDiff('d',Songs.DateAdded,Now) > " & BoostNewSongsDays & "), 0, " & BoostNewSongsModifier & ")))" 
End If 

weightedRatingFormula = ratingFormula & " - IIF(" & weightedPlayCountFormula & " < 0, 0, " & weightedPlayCountFormula & ") " 
if boostNewSongsFormula <> "" then weightedRatingFormula = weightedRatingFormula & "+ " & boostNewSongsFormula


Sub FillStandardProperties(parentNode, childNode) 
   With childNode 
      .CustomNodeId = parentNode.CustomNodeId 
      .CustomDataId = parentNode.CustomDataId + 1 
      .UseScript = Script.ScriptPath 
   End With 
End Sub 


Sub FillGoodLeaf(Node) 
   Randomize 

   Dim Tracks 
   Dim SQLStatement 
   Dim SELECT_Clause, FROM_Clause, WHERE_Clause, ORDER_Clause 
   Dim Iter, res, i, total, sum, index 
   Dim weight, minweight, maxweight 
   Dim hold, weights, average, start, baseweight, boundary 
   Dim R, max : max = NumberOfSongs 
   Dim inStr : inStr = "" 

   Set hold = CreateObject("Scripting.Dictionary") ' define an associative array or "hash array" to hold the songs 
   Set weights = CreateObject("Scripting.Dictionary") ' define an associative array or "hash array" to hold the weightings 

    
   SELECT_Clause = " SELECT Songs.Id, IIF(" & weightedRatingFormula & ">10,10," & weightedRatingFormula & ") " 
   FROM_Clause = " FROM Songs " 
   WHERE_Clause = " WHERE " & weightedPlayCountFormula &" < " & ratingFormula & " AND DateDiff('d',Songs.LastTimePlayed, Now) > " & MinDaysRepeat
   If PlayGenres <> "" then 
      WHERE_Clause = WHERE_Clause & " AND Songs.Genre IN (" & PlayGenres & ")" 
   End If 
   ORDER_Clause = " ORDER BY Rnd((1000*Songs.ID)*Now())" ' This is important because otherwise they will all come as they are stored in the DB 
    
   SQLStatement = SELECT_Clause & FROM_Clause & WHERE_Clause & ORDER_Clause 
   'res = InputBox("SQL Statement: ", "debugging", SQLStatement) ' For debugging 
    
   Set Iter = SDB.Database.OpenSQL(SQLStatement) ' holds all songs that are collected by the SQL formula 

   total=0 ' initialise total amount of songs that can be used for the playlist (=all songs with weights from 1 to 10 minus "done" songs) 
   sum = 0 ' initialise sum of all weightings 
   maxweight = 0 ' initialise maximum weight in this iteration 
   minweight = 9 ' initialise minimum weight in this iteration 
    
   While (Not Iter.EOF) 
      hold.add total,Iter.StringByIndex(0) 
      weight = Iter.StringByIndex(1) 
      weights.add total,weight 
      total = total + 1 
      sum = sum + weight 
      If minweight > cdbl(weight) then 
         minweight = cdbl(weight) 
      End if 
      if maxweight < cdbl(weight) then 
         maxweight = cdbl(weight) 
      End if 

        Iter.Next 
   Wend 

   ' some overflow checks 
   if total <= 0 or sum <= 0 then 
      SDB.MessageBox "Something is wrong with your ratings. Do you have any rated songs at all?", mtInformation, Array(mbOk) 
      Exit Sub 
   end if 

   If Set1Weight < minweight or Set2Weight < minweight or Set3Weight < minweight or Set1Weight > maxweight or Set2Weight > maxweight or Set3Weight > maxweight then 
      SDB.MessageBox "Something is wrong with your SetXWeight constants. They are either too high or too low.", mtInformation, Array(mbOk) 
      Exit Sub 
   end if 
    
   If (total < max) Then 
        max = total 
   End If 
      
   Set Tracks = SDB.MainTracksWindow 
   start = 0 
    
   While i < max ' do this until the max number of songs for the playlist is reached 
      If i < max * Set1Percentage / 100 then ' put in Set1Percentage percent of higher-rated songs 
         baseweight = Set1Weight 
         boundary = Set1Boundary 
      elseif i < max * (Set1Percentage + Set2Percentage) / 100 then ' put in Set2Percentage percent of lower-rated songs 
         baseweight = Set2Weight 
         boundary = Set2Boundary 
      elseif i <= max then ' and also put in some really lowly-rated songs 
         baseweight = Set3Weight 
         boundary = Set3Boundary 
      end if 
             
      For index = start to total ' go through all of the songs that could potentially become part of the radio node, starting where we left off last time 
         weight = cdbl(weights.item(index)) ' weight of the song that is being looked at right now 
             
         if weight >= (baseweight - boundary) and weight <= (baseweight + boundary) then 
            ' This song is inside the weighting borders that are defined above 
            ' so let's see if it is also lucky 
            R = rnd(1) ' this song's lucky number 
            if R > 0.8 then 
               ' so let's leave this for-next loop and add it to the list! 
               Exit For 
            end if 
         end if 
      Next    
       
      start = index + 1 ' We don't want to go through the same songs again, so next time we start from here 
      if start > total then 
         if inStr = "" then 
            SDB.MessageBox "There does not seem to be enough stuff to work on. Try to use criteria that are easier to meet.", mtInformation, Array(mbOk) 
            Exit Sub 
         else 
            start = 0 ' start over again 
         end if 
      else 
         ' We've got a winner! The song with the hash index "index" will now be added to the radio node 
         ' This is done by collecting all songs in inStr, separating them by commas, and afterwards collecting their song.ids via SQL         
           If i = 0 Then 
             inStr = hold.item(index)
           Else 
             inStr = inStr & ", " & hold.item(index) 
           End If 
           i = i + 1 
      end if    

   Wend 

   If inStr <> "" then 
      Tracks.AddTracksFromQuery("AND Songs.ID IN (" & inStr & ") ORDER BY Rnd((1000*Songs.ID)*Now())") 
      Tracks.FinishAdding 
   End If 
       
End Sub 





Sub FillWeightNode(Node) 

   Dim Tree, newNode 
   Dim SQLStatement ' SQL query to the database 
   Dim Iter ' SDBD Iterator obtained by running the SQL query to get the nodes 

   Set Tree = SDB.MainTree 
   Node.HasChildren = false ' To delete all old children 

   SQLStatement = "SELECT DISTINCT IIF(" & weightedRatingFormula & ">10,10," & weightedRatingFormula & ") from Songs " 
   'res = InputBox("SQL Statement: ", SQLStatement, SQLStatement) ' For debugging 
   Set Iter = SDB.Database.OpenSQL(SQLStatement) 

   While Not Iter.EOF 

      Set newNode = Tree.CreateNode 

      NewNode.Caption = Iter.StringByIndex(0) 
      NewNode.iconIndex = 32 
      newNode.CustomData = Iter.StringByIndex(0) 

      FillStandardProperties node,newNode 
      newNode.onFillTracksFunct = "FillWeightLeaf" 
      newNode.hasChildren = False 
      Tree.AddNode Node, NewNode, 3 

      Iter.Next 
   Wend 

End Sub 


Sub FillWeightLeaf(Node) 
   Dim Weight 
   Dim Tracks 
   Dim SELECT_Clause, FROM_Clause, WHERE_Clause 

   Weight = Node.CustomData 

   SELECT_Clause = " SELECT Songs.Id " 
   FROM_Clause = " FROM Songs " 
   WHERE_Clause = " WHERE IIF(" & weightedRatingFormula & ">10,10," & weightedRatingFormula & ") = " & Weight & " AND DateDiff('d',Songs.LastTimePlayed, Now) > " & MinDaysRepeat 
   If PlayGenres <> "" then 
      WHERE_Clause = WHERE_Clause & " AND Songs.Genre IN (" & PlayGenres & ")" 
   End If 
   'res = InputBox("SQL Statement: ", "Debugging", SELECT_Clause & FROM_Clause & WHERE_Clause) ' For debugging 
    
   Set Tracks = SDB.MainTracksWindow 

   Tracks.AddTracksFromQuery("AND Songs.ID IN (" & SELECT_Clause & FROM_Clause & WHERE_Clause & ")") 
   Tracks.FinishAdding 

End Sub 


Sub FillDoneLeaf(Node) 
   Dim Tracks 
   Dim SELECT_Clause, FROM_Clause, WHERE_Clause 


   SELECT_Clause = " SELECT Songs.Id " 
   FROM_Clause = " FROM Songs " 
   WHERE_Clause = " WHERE "& weightedPlayCountFormula &" >= " & ratingFormula & " OR DateDiff('d',Songs.LastTimePlayed, Now) <= " & MinDaysRepeat 
   If PlayGenres <> "" then 
      WHERE_Clause = WHERE_Clause & " OR Songs.Genre NOT IN (" & PlayGenres & ")" 
   End If 

   Set Tracks = SDB.MainTracksWindow 

   Tracks.AddTracksFromQuery("AND Songs.ID IN (" & SELECT_Clause & FROM_Clause & WHERE_Clause & ")") 
   Tracks.FinishAdding 

End Sub 



'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
' Startup Function 
'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

Sub onStartUp 
   Dim Tree, RadioFreeMonkeyRoot 
   Dim RadioMonkeyGood, RadioMonkeyBad, RadioMonkeyWeight 

   Set Tree = Sdb.MainTree 
   Set RadioFreeMonkeyRoot = Tree.createNode 

   RadioFreeMonkeyRoot.Caption = RootNodeCaption 
   RadioFreeMonkeyRoot.IconIndex = 14 
   RadioFreeMonkeyRoot.UseScript = Script.ScriptPath 
   RadioFreeMonkeyRoot.hasChildren = True 
   Tree.AddNode Tree.Node_Library, RadioFreeMonkeyRoot, 1 
   SDB.Objects("RadioFreeMonkeyRoot") = RadioFreeMonkeyRoot 

   Set RadioMonkeyGood = Tree.createNode 
   RadioMonkeyGood.Caption = "Radio List" 
   RadioMonkeyGood.IconIndex = 14 
   RadioMonkeyGood.UseScript = Script.ScriptPath 
   RadioMonkeyGood.hasChildren = False 
   RadioMonkeyGood.onFillTracksFunct = "FillGoodLeaf" 
   Tree.AddNode RadioFreeMonkeyRoot, RadioMonkeyGood, 2 
   SDB.Objects("RadioMonkeyGood") = RadioMonkeyGood 

   Set RadioMonkeyWeight = Tree.createNode 
   RadioMonkeyWeight.Caption = "Weightings" 
   RadioMonkeyWeight.IconIndex = 32 
   RadioMonkeyWeight.UseScript = Script.ScriptPath 
   RadioMonkeyWeight.hasChildren = True 
   RadioMonkeyWeight.onFillTracksFunct = "FillWeightNode" 
   Tree.AddNode RadioFreeMonkeyRoot, RadioMonkeyWeight, 3 
   SDB.Objects("RadioMonkeyWeight") = RadioMonkeyWeight 

   Set RadioMonkeyBad = Tree.createNode 
   RadioMonkeyBad.Caption = "Done" 
   RadioMonkeyBad.IconIndex = 15 
   RadioMonkeyBad.UseScript = Script.ScriptPath 
   RadioMonkeyBad.hasChildren = False 
   RadioMonkeyBad.onFillTracksFunct = "FillDoneLeaf" 
   Tree.AddNode RadioFreeMonkeyRoot, RadioMonkeyBad, 3 
   SDB.Objects("RadioMonkeyBad") = RadioMonkeyBad 
End Sub 
[/code]

popper
Posts: 34
Joined: Mon Feb 21, 2005 5:10 pm
Location: Germany

Re: Some Fixes

Post by popper » Sun Oct 29, 2006 2:55 pm

Hi ElGringo,

thank you for pointing out my mistakes and correcting them. I was waiting all the time for someone to take a closer look at the code, because I knew that it was probably not perfect.

I have got some comments to your improvements.
ElGringo wrote:Hi !

This script is exactly what i was looking for... Thank you very much.

I picked the last script from popper, and saw a couple of errors that i corrected.

1. There was always a song that had nothing to do in my playlists, investigated and saw the error in FillGoodLeaf :

(...)

If i=0, then It should be hold.item(index), instead of index, so the first song that was added was not good, we were taking the index, instead of the real song.id
Yes, of course you are right. Silly mistake!

ElGringo wrote:2. There is an error in the calculation of the dateboost formula, the dateboostcutoff*20 should be dateboostcutoff*10. There were a couple of places wrong
I am not so sure about this one. Please correct me if I am wrong, but the ratings are stored in the database using numbers from 0 (or -1) to 100. So to calculate the number of stars from a rating in the database, you have to divide by 20, and not by ten.

ElGringo wrote:3. By adding, the BoostNewSongsModifier, the actual weight calculated, could go higher than 10, but the algorithm was never picking songs with a weight higher than 10. So, i fixed a couple of places in the script, so that calculated weight higher than 10, are now fixed at 10.

4. There was a bug, when the BoostNewSongsModifier is set to 0... fixed it.
Yes, these are very valid points. Thanks for correcting them.

I am already looking forward to using the new version of this script...

cheers,
popper.

tinny
Posts: 39
Joined: Tue Oct 03, 2006 5:30 am
Contact:

Post by tinny » Wed Nov 01, 2006 3:30 pm

thanks for this excellent script!

Post Reply