<cfsetting enablecfoutputonly=true> <cfprocessingdirective pageencoding="utf-8"> <!---
name : now_playing.cfm
version : 0.93
author : steven erat
license : now_playing.cfm Pod for BlogCFC, Copyright © 2006 Steven Erat, licensed under
a Creative Commons Attribution-NonCommercial-ShareAlike License
(
http://creativecommons.org/licenses/by-nc-sa/2.5/)
created : monday, feb 20,
2006 updated : saturday, feb 25 2006 - bug fixes, added Amazon/Apple URL and image features, utf-8 fix
history : saw a now playing pod on matt woodward's (blogfusion) blog.
thought blogcfc should have it, too. built to work with xml exported
from brandon fuller's now playing plugin for itunes, available at
http://brandon.fuller.name./archives/hacks/nowplaying/ purpose : show what's playing on itunes on your blogcfc, and maybe make some profit with Amazon
assumptions : (
1)assumes the plug-in data file is called 'now_playing.xml' (default)
(
2)assumes that the file is written to a subdirectory called 'nowplaying/'
in the blog root directory (same level as includes/ and tags/)
usage : (
1)install the plug-in for itunes (purchase required)
(
2)create a subdirectory called 'nowplaying' such as {blog_root}/nowplaying/,
this subdirectory is recommended since the artwork jpg files will be uploaded too.
(
3)configure plugin to upload (ftp) the now_playing.xml file to that directory
(
4)customize the few the settings below, and save this file to {blog_root}/includes/pods/
(
5)configure tags/layout.cfm to display this pod betwixt the other pods
(
6)once itunes stops playing for a while and starts playing again,
it will take snooze(minutes) that much time to realize itunes is active again
(
7)the Windows version of plug-in has more features than on Mac, and allows for
usage of Amazon album cover, Amazon URLs with Associate ID, or Apple Music Store URLs
--->
<!--- CUSTOMIZE POD SETTINGS HERE ---> <!--- name of the plug-in data file that was uploaded --->
<cfset myNowPlayingFilename = "now_playing.xml"> <!--- starting at top of document root, what is the directory containing
the blog files so start path with a forward slash like "/blog",
but if blog is at top of doc root use just empty string "" ---> <cfset myBlogRootDir = "/blog"> <!--- name of the subdirectory where data file resides ---> <!--- this setting will be used by itself and also combined
with myBlogRootDir to form an absolute path such as /blog/nowplaying/ ---> <cfset myNowPlayingSubDir = "nowplaying"> <!--- "\" for Windows, or "/" for Unix,Linux,Mac ---> <!--- this refers to the ColdFusion server's OS, not your iTune's OS ---> <cfset myFileSeparator = "\"> <!--- this will display as the pod title (styled by .menuTitle) ---> <cfset myPodTitle = "playing on itunes"> <!--- what text should be displayed if iTunes is stopped ---> <cfset myIdleMessage = "recently played"> <!--- to cut down on checking the filesystem for updates,
set the following parameters according to your preferences,
may want to set longest time higher if you want your podcasts
to show for the full duration ---> <cfset myAverageSongPlayTime = 3> <cfset myLongestPlayTime = 10> <!--- END ALL CUSTOMIZATIONS, NO REAL NEED TO EDIT FURTHER ---> <!--- redirect to website root dir if pod is viewed directly outside of blog ---> <cfif cgi.script_name contains "/includes/pods/"> <cflocation addtoken="No" url="#variables.myBlogRootDir#"> </cfif> <!--- start custom functions ---> <cffunction name="checkinterval"
access="public"
description="check if n minutes have passed since last song was uploaded"
hint="helps to reduce the frequency of checking timestamp of now_playing.xml"
returntype="struct"
output="no"> <cfargument name="timestamp" required="yes" type="date"> <cfargument name="min" required="yes" type="numeric"> <cfargument name="max" required="yes" type="numeric"> <cfset var minutessincelastupdate = datediff('n',arguments.timestamp,now())> <cfset var result = structNew()> <cfset result.minutes = minutessincelastupdate> <cfif (minutessincelastupdate gte arguments.min and minutessincelastupdate lte arguments.max)> <cfset result.status = true> <cfreturn result> <cfelse> <cfset result.status = false> <cfreturn result> </cfif> </cffunction> <cffunction name="snooze"
access="public"
description="return true (snoozing) until n minutes have passed (not snoozing)."
hint="This function dependent on application scope, and improves efficiency for when iTunes is idle"
returntype="boolean"
output="no"> <cfargument name="minutes" required="yes" type="numeric"> <cfif structkeyexists(application,"nowplaying") and structkeyexists(application.nowplaying,"snooze") and structkeyexists(application.nowplaying.snooze,"start#arguments.minutes#")> <cfif datediff('n',application.nowplaying.snooze["start#arguments.minutes#"],now()) gte arguments.minutes> <!--- reset the alarm clock ---> <cfset application.nowplaying.snooze["start#arguments.minutes#"] = now()> <!--- wake up ---> <cfreturn false> <cfelse> <!--- snooze for a while ---> <cfreturn true> </cfif> <cfelse> <!--- get here on startup or reinit ---> <cfset application.nowplaying.snooze["start#arguments.minutes#"] = now()> <!--- snooze for a while --->
<cfreturn true> </cfif> <cfreturn true> </cffunction> <cffunction name="refreshplaylist"
access="public"
description="check to see if now_playing.xml has changed"
returntype="struct"
output="no"> <cfargument name="nowplayingdir" required="yes" type="string"> <cfargument name="nowplayingfile" required="yes" type="string"> <cfargument name="lastsongtimestamp" required="yes" type="date"> <cfset var nowplayingfiledata = ""> <cfset var minutessincelastupdate = ""> <cfset var result = structnew()> <cftry> <cfdirectory name="nowplayingfiledata" action="list" directory="#arguments.nowplayingdir#" filter="#arguments.nowplayingfile#"> <!--- if current xml file has timestamp newer than timestamp in memory, update the in memory version ---> <cfif arguments.lastsongtimestamp lt nowplayingfiledata.datelastmodified> <cfset result.datelastmodified = nowplayingfiledata.datelastmodified> <cfset result.status = true> <cfreturn result> <cfelse> <cfset result.datelastmodified = ""> <cfset result.status = false> <cfreturn result> </cfif> <cfcatch> <cfset result.datelastmodified = ""> <cfset result.status = false> <cfreturn result> </cfcatch> </cftry> </cffunction> <cffunction name="getcurrentsong"
access="public"
description="use with now playing plug-in for itunes"
returntype="struct"
output="no"> <cfargument name="nowplayingdir" required="yes" type="string"> <cfargument name="nowplayingfile" required="yes" type="string"> <cfset var nowplayingxml=""> <cfset var nowplayingobj=""> <cfset var song = structnew()> <cfset var nowplayingfullfilepath = "#arguments.nowplayingdir##arguments.nowplayingfile#"> <cfif fileexists(nowplayingfullfilepath)> <cftry> <cflock type="exclusive" name="nowplayingfilelock" timeout="10" throwontimeout="no"> <cffile action="read" file="#nowplayingfullfilepath#" variable="nowplayingxml" charset="utf-8"> </cflock> <!--- wish I didn't have to do this, but with the xml declaration xmlparse barfs ---> <cfset nowplayingxml = replace(nowplayingxml,"<?xml version='1.0' encoding='utf-8'?>",'',
"one")>
<cfset nowplayingobj = xmlparse(nowplayingxml)> <cfset song.isplaying = nowplayingobj.now_playing.xmlattributes.playing> <cfif song.isplaying> <!--- basic information found in Mac and Windows plug-in for iTunes --->
<cfset song.artist = nowplayingobj.now_playing.song.artist.xmltext> <cfset song.title = nowplayingobj.now_playing.song.title.xmltext> <cfset song.album= nowplayingobj.now_playing.song.album.xmltext> <cfset song.genre = nowplayingobj.now_playing.song.genre.xmltext> <cfset song.comments = nowplayingobj.now_playing.song.comments.xmltext> <cfset song.artworkid = nowplayingobj.now_playing.song.artworkid.xmltext> <!--- intialize optional values ---> <cfset song.imageURL = ""> <cfset song.urlAmazon = ""> <cfset song.urlApple = ""> <!--- optional information available in the iTunes plugin for Windows ---> <cfif isdefined("nowplayingobj.now_playing.song.image.xmltext")> <cfset song.imageURL = nowplayingobj.now_playing.song.image.xmltext> </cfif> <cfif isdefined("nowplayingobj.now_playing.song.urlAmazon.xmltext")> <cfset song.urlAmazon = nowplayingobj.now_playing.song.urlAmazon.xmltext> </cfif> <cfif isdefined("nowplayingobj.now_playing.song.urlApple.xmltext")> <cfset song.urlApple = nowplayingobj.now_playing.song.urlApple.xmltext> </cfif> </cfif> <cfcatch> <cfset song.isplaying = 0> </cfcatch> </cftry> <cfelse> <cfset song.isplaying = 0> </cfif> <cfreturn song> </cffunction> <!--- end custom functions ---> <!--- set a debug flag to clear stored pod data ---> <cfif isdefined("url.reinit") and isuserinrole("admin")> <cfset structdelete(application,"nowplaying")> </cfif> <cfif not isdefined("application.nowplaying.datafile") or not isdefined("application.nowplaying.datadir")> <cfset application.nowplaying = structnew()> <!--- set the name of the xml file uploaded by the itunes plugin ---> <cfset application.nowplaying.datafile = variables.myNowPlayingFilename> <!--- assumes that a directory called "nowplaying" exists in the blog's root dir
and this is where you have configured the itunes plugin to upload to ---> <cfset application.nowplaying.datadir = getdirectoryfrompath(cgi.path_translated) & variables.myNowPlayingSubDir & variables.myFileSeparator> </cfif> <cfset variables.nowplaying = structnew()> <cfset variables.nowplaying.datafile = application.nowplaying.datafile> <cfset variables.nowplaying.datadir = application.nowplaying.datadir> <cfset variables.nowplaying.idle = false> <cfif not structkeyexists(application.nowplaying,"idle")> <cflock name="applicationnowplayingidle" type="exclusive" timeout="5" throwontimeout="no"> <cfset application.nowplaying.idle = false> </cflock> </cfif> <!--- on startup or reinit this will get called to satisfy the need for last song timestamp ---> <cfif not isdefined("application.nowplaying.lastsong.datelastmodified")> <!--- lock for race condition ---> <cflock name="applicationnowplayinglastsongdatelastmodified" type="exclusive" timeout="5" throwontimeout="no"> <cfset application.nowplaying.lastsong.datelastmodified = now()> <cfset variables.nowplaying.lastsong.datelastmodified = duplicate(application.nowplaying.lastsong.datelastmodified)> </cflock> <cfelse> <!--- lock for race condition, and read from cached app scope into local scope ---> <cflock name="applicationnowplayinglastsongdatelastmodified" type="readonly" timeout="5" throwontimeout="no"> <cfset variables.nowplaying.lastsong.datelastmodified = duplicate(application.nowplaying.lastsong.datelastmodified)> </cflock> </cfif> <cfset variables.nowplaying.readsongfromcache = false> <!--- check to see if current song has changed and do refreshplaylist():
- checkinterval() will only attempt to refreshplaylist() (by checking now_playing.xml timestamp)
between the min and max timespan.
- if more than n minutes have passed, exceeding checkinteval's max value,
then no refreshplaylist() will be attempted because it is assumed iTunes is not playing anymore
- snooze(n) is used to periodically wake up and refreshplaylist(),
and is useful to break out of the idle time when iTunes has stopped but is now playing again.
- Example: checkinterval(variables.nowplaying.lastsong.datelastmodified,
3,
10) or not snooze(
5)
- Here refreshplaylist() will check now_playing.xml 3 minutes after the last song was started
- refreshplaylist() will stop checking 10 minutes after last song started (assuming long podcast)
- and snooze(
5) will let refreshplaylist() check the time on now_playing.xml every 5 minutes
where snooze() will break out of the lull when iTunes idle time exceeds checkinterval's max time of 10 minutes. --->
<cfset currentinterval = checkinterval(variables.nowplaying.lastsong.datelastmodified,variables.myAverageSongPlayTime,variables.myLongestPlayTime)> <cfif currentinterval.status is true or (currentinterval.minutes GT variables.myLongestPlayTime and snooze(5) is not true)> <cfset variables.nowplaying.refreshresult = refreshplaylist(variables.nowplaying.datadir,variables.nowplaying.datafile,variables.nowplaying.lastsong.datelastmodified)> <cfif variables.nowplaying.refreshresult.status is true> <!--- lock for race condition while updating cached timestamp of last song (equal to timestamp of current song) ---> <cflock name="applicationnowplayinglastsongdatelastmodified" type="exclusive" timeout="5" throwontimeout="no"> <cfset application.nowplaying.lastsong.datelastmodified = variables.nowplaying.refreshresult.datelastmodified> </cflock> <!--- set or reset the local current song struct data ---> <cfset variables.nowplaying.currentsong = getcurrentsong("#variables.nowplaying.datadir#","#variables.nowplaying.datafile#")> <!--- do not overwrite app var if song is not playing since "last song" info will be displayed anyway---> <cfif variables.nowplaying.currentsong.isplaying is true> <!--- lock for race condition while updating cache of current song data ---> <cflock name="applicationnowplayingcurrentsong" type="exclusive" timeout="5" throwontimeout="no"> <cfset application.nowplaying.currentsong = variables.nowplaying.currentsong> </cflock> </cfif> <cflock name="applicationnowplayingidle" type="exclusive" timeout="5" throwontimeout="no"> <cfset application.nowplaying.idle = false> </cflock> <!--- only clear on index.cfm since others like stats.cfm don't use caching ---> <cfif cgi.script_name contains "/index.cfm"> <!--- clear the scopecache to refresh blog view with new song shown in pod ---> <cfmodule template="../../tags/scopecache.cfm" scope="application" clearall="true"> </cfif> <cfset variables.nowplaying.currentsong.isplaying = true> <cfelse> <cfset variables.nowplaying.readsongfromcache = true> <cfset variables.nowplaying.currentsong.isplaying = true> </cfif> <cfelse> <cfset variables.nowplaying.readsongfromcache = true> <cfset variables.nowplaying.currentsong.isplaying = true> </cfif> <!--- test for first ocassion exceeding the max long play time,
and clear scopecache to show no song is playing ---> <cfif currentinterval.minutes GTE variables.myLongestPlayTime> <cflock name="applicationnowplayingidle" type="readonly" timeout="5" throwontimeout="no"> <cfset variables.nowplaying.idle = duplicate(application.nowplaying.idle)> </cflock> <cfif variables.nowplaying.idle is false> <cflock name="applicationnowplayingidle" type="exclusive" timeout="5" throwontimeout="no"> <cfset application.nowplaying.idle = true> <cfset variables.nowplaying.idle = true> <!--- only clear on index.cfm since others like stats.cfm don't use caching --->
<cfif cgi.script_name contains "/index.cfm"> <!--- clear the scopecache to refresh blog view to show no song is playing ---> <cfmodule template="../../tags/scopecache.cfm" scope="application" clearall="true"> </cfif> </cflock> </cfif> </cfif> <!--- should only go into this when starting up ---> <cfif not isdefined("application.nowplaying.currentsong")> <!--- lock for race condition while updating cached timestamp of last song (equal to time of current song) ---> <cflock name="applicationnowplayinglastsongdatelastmodified" type="exclusive" timeout="5" throwontimeout="no"> <cfset application.nowplaying.lastsong.datelastmodified = now()> </cflock> <!--- set or reset the local current song struct data --->
<cfset variables.nowplaying.currentsong = getcurrentsong("#variables.nowplaying.datadir#","#variables.nowplaying.datafile#")> <!--- lock for race condition while updating cache of current song data --->
<cflock name="applicationnowplayingcurrentsong" type="exclusive" timeout="5" throwontimeout="no"> <cfset application.nowplaying.currentsong = variables.nowplaying.currentsong> </cflock> </cfif> <cfif variables.nowplaying.readsongfromcache or variables.nowplaying.idle> <cflock name="applicationnowplayingcurrentsong" type="readonly" timeout="5" throwontimeout="no"> <cfset variables.nowplaying.currentsong = duplicate(application.nowplaying.currentsong)> </cflock> </cfif> <!--- the myIdleMessage text will appear myLongestPlayTime minutes ---> <cfif variables.nowplaying.idle> <!--- change the pod title from now playing message to recently playing message ---> <cfset variables.myPodTitle = variables.myIdleMessage> </cfif> <!--- this is the meat of the pod, what gets shown on screen ---> <cfmodule template="../../tags/podlayout.cfm" title="#variables.myPodTitle#"> <cfoutput> <table> <cftry> <tr><td> <strong>#variables.nowplaying.currentsong.title#
</strong> <br/>by #variables.nowplaying.currentsong.artist#
<br/>on
<cfif variables.nowplaying.currentsong.album neq ""> <!--- throw an Amazon link around album title text if no artwork available ---> <cfif variables.nowplaying.currentsong.imageURL eq ""
and variables.nowplaying.currentsong.artworkID eq ""
and variables.nowplaying.currentsong.urlAmazon neq ""> <a href="#variables.nowplaying.currentsong.urlAmazon#"
title="Get #variables.nowplaying.currentsong.urlAmazon# by #variables.nowplaying.currentsong.artist# on Amazon">#variables.nowplaying.currentsong.album#
</a> <cfelse> #variables.nowplaying.currentsong.album#
</cfif> </cfif> </td></tr> <!--- show the uploaded artwork in amazon link with associate id if present ---> <cfif variables.nowplaying.currentsong.urlAmazon neq "" and fileexists("#variables.nowplaying.datadir#now_playing-#variables.nowplaying.currentsong.artworkid#.jpg")> <tr><td> <a href="#variables.nowplaying.currentsong.urlAmazon#"><img src="#variables.myBlogRootDir#/#myNowPlayingSubDir#/now_playing-#variables.nowplaying.currentsong.artworkid#.jpg"
border="0" width="80" height="80" align="left"
alt="Get #variables.nowplaying.currentsong.album# by #variables.nowplaying.currentsong.artist# on Amazon"
title="Get #variables.nowplaying.currentsong.album# by #variables.nowplaying.currentsong.artist# on Amazon"></a> </td></tr> <!--- show amazon artwork and amazon link with associate id if present ---> <cfelseif variables.nowplaying.currentsong.imageURL neq ""> <tr><td> <cfif variables.nowplaying.currentsong.urlAmazon neq ""> <a href="#variables.nowplaying.currentsong.urlAmazon#"><img src="#variables.nowplaying.currentsong.imageURL#"
border="0" width="80" height="80" align="left"
alt="Get #variables.nowplaying.currentsong.album# by #variables.nowplaying.currentsong.artist# on Amazon"
title="Get #variables.nowplaying.currentsong.album# by #variables.nowplaying.currentsong.artist# on Amazon"></a> <cfelse> <img src="#variables.nowplaying.currentsong.imageURL#"
border="0" width="80" height="80" align="left"
alt="#variables.nowplaying.currentsong.album# by #variables.nowplaying.currentsong.artist#"
title="#variables.nowplaying.currentsong.album# by #variables.nowplaying.currentsong.artist#"> </cfif> </td></tr> <!--- show artwork if available; display at half size 80x80 instead of 160x160 ---> <cfelseif fileexists("#variables.nowplaying.datadir#now_playing-#variables.nowplaying.currentsong.artworkid#.jpg")> <tr><td> <cfif variables.nowplaying.currentsong.urlApple neq ""> <a href="#variables.nowplaying.currentsong.urlApple#"><img
src="#variables.myBlogRootDir#/#myNowPlayingSubDir#/now_playing-#variables.nowplaying.currentsong.artworkid#.jpg"
border="0" width="80" height="80" align="left"
alt="Get #variables.nowplaying.currentsong.album# by #variables.nowplaying.currentsong.artist# on Apple's iTunes Music Store"
title="Get #variables.nowplaying.currentsong.album# by #variables.nowplaying.currentsong.artist# on Apple's iTunes Music Store"></a> <cfelse> <img src="#variables.myBlogRootDir#/#myNowPlayingSubDir#/now_playing-#variables.nowplaying.currentsong.artworkid#.jpg"
border="0" width="80" height="80" align="left"
alt="#variables.nowplaying.currentsong.album#, #variables.nowplaying.currentsong.artist#"
title="#variables.nowplaying.currentsong.album#, #variables.nowplaying.currentsong.artist#"> </cfif> </td></tr> </cfif> <!---
<cfif variables.nowplaying.currentsong.comments neq ""> <tr><td> <p style="font-size:smaller;"><i>#variables.nowplaying.currentsong.comments#
</i></p> </td></tr> </cfif> --->
<cfcatch> oops!
<!-- #cfcatch.message#, #cfcatch.detail#, --> </cfcatch> </cftry> <tr><td> <p><a href="http://brandon.fuller.name./archives/hacks/nowplaying/"><img src="#variables.myBlogRootDir#/includes/button_now_playing.gif" width="80" height="15" alt="now playing, a plug-in for itunes" title="now playing, a plug-in for itunes" border="0" align="left"></a></p> </td></tr> </table> <!-- Now_playing.cfm Pod for BlogCFC, Copyright (c)
2006 Steven Erat, (
http://www.talkingtree.com/)
released under a Creative Commons Attribution-NonCommercial-ShareAlike License -->
<!-- Creative Commons License --> <!--
<rdf:RDF xmlns="http://web.resource.org/cc/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns##"> <Work rdf:about=""> <license rdf:resource="http://creativecommons.org/licenses/by-nc-sa/2.5/" /> </Work> <License rdf:about="http://creativecommons.org/licenses/by-nc-sa/2.5/"> <requires rdf:resource="http://web.resource.org/cc/Attribution" /> <permits rdf:resource="http://web.resource.org/cc/Reproduction" /> <permits rdf:resource="http://web.resource.org/cc/Distribution" /> <permits rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> <requires rdf:resource="http://web.resource.org/cc/ShareAlike" /> <prohibits rdf:resource="http://web.resource.org/cc/CommercialUse" /> <requires rdf:resource="http://web.resource.org/cc/Notice" /> </License> </rdf:RDF> -->
</cfoutput> </cfmodule> <cfsetting enablecfoutputonly=false>