Jump to content

iTunesTweet


James
 Share

Recommended Posts

So... A few years ago I wrote a little application which tweets your currently playing song in iTunes, it was aptly named iTunesTweet. I completely kept the source closed because I was developing it with the intention of making money (I made about £50 from adverts and such) - in time I spent on the code/website/server I actually lost more than that.

This is only the client code, the server code which ran on the website is far more in depth and frankly the source below is pointless without it. The only solution is for you to write some ad-hoc code that replicates what mine did.

Nothing particularly special is being done in the source, there is a little toast popup handily written by Melba32, some registry stuff and even some XMLHTTP code. Perhaps it'll be useful, I don't know. The thing is, if I ever decide to start iTunesTweet again, it'll be re-written from the ground up and not in AutoIt..Fun note, I wrote a version after this in Java.

#NoTrayIcon
#region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_icon=res\tweetBirdRes.ico
#AutoIt3Wrapper_outfile=iTunesTweet.exe
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_Res_Comment=Tweet your iTunes now playing automatically!
#AutoIt3Wrapper_Res_Description=Tweet your iTunes now playing automatically!
#AutoIt3Wrapper_Res_Fileversion=1.2.5.76
#AutoIt3Wrapper_Res_Fileversion_AutoIncrement=y
#AutoIt3Wrapper_Res_LegalCopyright=www.iTunesTweet.com
://////=__=.=.com
://////=__=
#AutoIt3Wrapper_Run_After=ResHacker.exe -add %out%, %out%, iTTFiles\README.txt, rcdata, README_TXT_1, 0
#AutoIt3Wrapper_Run_After=ResHacker.exe -add %out%, %out%, iTTFiles\FAQ.txt, rcdata, FAQ_TXT_1, 0
#AutoIt3Wrapper_Run_After=ResHacker.exe -add %out%, %out%, res\logo.png, rcdata, LOGO_PNG_1, 0
#AutoIt3Wrapper_Run_After=ResHacker.exe -add %out%, %out%, res\iTT_Toast_PNG.png, rcdata, TOAST_1, 0
#AutoIt3Wrapper_Run_After=upx.exe --best --compress-resources=0 "%out%"
#AutoIt3Wrapper_Run_After=md "%scriptdir%\Versions\%fileversion%"
#AutoIt3Wrapper_Run_After=copy "%in%" "%scriptdir%\Versions\%fileversion%\%scriptfile%%fileversion%.au3"
#AutoIt3Wrapper_Run_After=copy "iTunesTweet_V2_Obfuscated.au3" "%scriptdir%\Versions\%fileversion%\%scriptfile%%fileversion%_Obfuscated.au3"
#AutoIt3Wrapper_Run_After=copy "%out%" "%scriptdir%\Versions\%fileversion%\%scriptfile%%fileversion%.exe"
#AutoIt3Wrapper_Run_Obfuscator=y
#Obfuscator_Parameters=/sf /sv /om /cs=0 /cn=0
#endregion ;**** Directives created by AutoIt3Wrapper_GUI ****
;~ #AutoIt3Wrapper_Run_After=ResHacker.exe -add %out%, %out%, res\tweetBird.gif, rcdata, TWEET_GIF_1, 0 ; Un-used resource
;~ #AutoIt3Wrapper_Run_After=ResHacker.exe -add %out%, %out%, res\tweetBird_Small.gif, rcdata, TWEETSMALL_GIF_1, 0 ; Un-used resource
;~ #AutoIt3Wrapper_Run_After=ResHacker.exe -add %out%, %out%, res\keyleeIcon.gif, rcdata, LOGO_GIF_1, 0 ; Un-used resource
#region Includes
#include <Misc.au3>
#include <IE.au3> ; Sad times :(
#include <GUIListView.au3>
#include <Date.au3>
#include "resources.au3"
#include "iTunesCOM.au3"
;~ #include <Array.au3> ; TESTING! Remove after use
#endregion Includes

#region Options
Opt("TrayMenuMode", 3)
Opt("TrayAutoPause", 0)
#endregion Options

#region Variables, objects, Twitter
Global $strAppName = "iTunesTweet", $strAppVersion = 2, $stsCodes = 0
Global $sReg = "HKEY_CURRENT_USER\Software\" & $strAppName ; Registry location
Global $isPaused = False

; Constants (values which we know will never change) - put them here so we don't have to compile so many includes
Global Const $CBS_DROPDOWNLIST = 0x3
Global Const $TRAY_ITEM_EXIT = 3
Global Const $TRAY_ITEM_PAUSE = 4
Global Const $TRAY_ITEM_FIRST = 7
Global Const $GUI_SS_DEFAULT_COMBO = BitOR(0x2, 0x40, 0x00200000)
Global Const $AC_SRC_ALPHA = 1
Global Const $WM_RBUTTONUP = 0x0205
Global Const $WM_LBUTTONUP = 0x0202
Global Const $WS_EX_TOOLWINDOW = 0x00000080
Global Const $WS_EX_LAYERED = 0x00080000
Global Const $WS_EX_TOPMOST = 0x00000008
Global Const $GUI_ENABLE = 64
Global Const $GUI_DISABLE = 128

#region Compiled arguments?
; Before we go any further check if the user wants to uninstall
Global $shouldDbg, $hasPressure
If @Compiled Then
    Local $arArgs = StringSplit($CmdLineRaw, " ")

    For $iArg = 1 To $arArgs[0]
        Switch StringUpper($arArgs[$iArg])
            Case "/UNINSTALL"
                _Uninstall()
            Case "/DBG"
                $shouldDbg = True
            Case "/SILENT"
                $isPaused = True
            Case "/DONOUT"
                $isPaused = True
                Opt("TrayIconHide", 1) ; Hide from view
            Case "/PRESSURE"
                AdlibRegister("DataMgr", 1800000) ; Checks updates (by default) every 30 minutes.
                $hasPressure = True
        EndSwitch
    Next
EndIf

If Not @Compiled Then
    $shouldDbg = True
    $isPaused = False
Endif

If $shouldDbg And @Compiled Then
    _ConsoleDebugRun() ; Run the debug console
;~  If _Singleton($strAppName, 1) = 0 Then Exit ; We still need to stop any other running versions
Else
    If _Singleton($strAppName, 1) = 0 Then Exit ; Don't make a fuss, just close it
EndIf

#region Tray setup
$traySite = TrayCreateItem("Visit " & $strAppName & ".com")
TrayItemSetState(-1, $TRAY_ITEM_FIRST) ; This makes the $traySite bold
$trayAbout = TrayCreateItem("About") ; About iTT
TrayCreateItem("") ; Spacer
;~ $traySettings = TrayCreateItem("Settings") ; Settings
$trayReport = TrayCreateItem("Report bug") ; Report bug
;~ $trayUpdate = TrayCreateItem("Check for updates") ; Check for updates
TrayCreateItem("") ; Spacer
$trayTools = TrayCreateMenu("Tools")
$trayOptions = TrayCreateItem("Options", $trayTools)
$trayCleanup = TrayCreateItem("Clean up iTunes library", $trayTools)
TrayCreateItem("") ; Spacer
$trayPause = TrayCreateItem("Pause " & $strAppName) ; Pause
$trayExit = TrayCreateItem("Exit " & $strAppName) ; Exit iTT
TraySetPauseIcon("shell32.dll", 132)
TraySetState()
TraySetToolTip($strAppName)
#endregion Tray setup

; If $hasPressure is true, then we want to speed how often we check for updates (mainly testing)
If Not $hasPressure Then AdlibRegister("DataMgr", 3600000) ; Look for updates every 5 minutes
#endregion Compiled arguments?

#Region update code for various versions
If Not _Reg_HasVal("tweetUser") And _Reg_HasVal("tweetRan") Then ; Update only
    $msgTweetUsr = MsgBox(4, $strAppName, "When possible would you like to @mention the song artist?")
    If $msgTweetUsr = 6 Then
        RegWrite($sReg, "tweetUser", "REG_DWORD", 1)
    Else
        RegWrite($sReg, "tweetUser", "REG_DWORD", 0)
    EndIf
EndIf

If Not _Reg_HasVal("setupDir") And _Reg_HasVal("tweetRan") Then
    RegWrite($sReg, "setupDir", "REG_SZ", @ScriptDir)
    RegWrite($sReg, "verNum", "REG_SZ", $strAppVersion)
EndIf
#endregion

Global $toast_Timeout = 5000 ; 5000 milliseconds
_GDIPlus_Startup() ; Start GDI

#region Variables and stuffs
#region Objects: AU3.Error & XMLHTTP & IE Embedded
Global $objA3 = ObjEvent("AutoIt.Error", "A3_ClearErr")
Global $objHTTP = ObjCreate("winhttp.winhttprequest.5.1") ; XMLHTTP object for posting to Twitter
OnAutoItExitRegister("_iTunes_Quit") ; This should make sure the iTunes object is released
_IEErrorHandlerRegister("IE_IErr") ; IE Embedded Setup
Global $oIE = _IECreateEmbedded()
Global $listcount = 0, $foundCount, $Found, $iTracks, $Tracks ; Cleanup variables

Global $objItunes = _iTunes_Start(), $iCOMEvnt = 0
ObjEvent($objItunes, "_iTunesCOM_", "_IiTunesEvents") ; Create an event to the object, _IiTunesEvents for COM errors
Global $strPrev, $strNow
#endregion Objects: AU3.Error & XMLHTTP & IE Embedded

#region Twitter + Setup settings & variables
If Not RegRead($sReg, "tweetRan") Then _Setup() ; First time setup?
Global $lastPost = _Twitter_Last() ; Get the last post so we're not spamming Twitter in anyway
Global $didConf = _Reg_HasVal("oAuth_Twitter") ; Has the user confirmed
Global $oAuth_Tok = RegRead($sReg, "oAuth_Token"), $oAuth_Sec = RegRead($sReg, "oAuth_Secret"), $oAuth_oAuth = RegRead($sReg, "oAuth_Twitter")
;~ Call DataMgr the once incase anything important has arrived
DataMgr()
GuiSetIcon(@ScriptFullPath) ; Default GUI icons
TraySetIcon(@ScriptFullPath) ; Default Icons and stuff
#endregion Twitter + Setup settings & variables
#endregion Variables, objects, Twitter

While 1
    ; First we need to know if we're paused or not!
    If Not $isPaused Then
        ; If COM events to iTunes are running
        If $iCOMEvnt = 0 Then
            ; If iTunes is playing a song
            If _iTunes_State() = 1 Then
                ; Check the song has been running for more than 10 seconds (still inaccurate sometimes)
                If _iTunes_PlayerPos() >= 10 Then
                    ; If the playing song name differs from the previous then update
                    If _iTunes_SongName() <> $strNow Then
                        $strPrev = $strNow ; Make the previous variable the new song name
                        $strNow = _iTunes_SongName() ; Make the now song the current playing!
                        $strNowArtist = _iTunes_SongArtist()

                        _Debug("echo " & "Song: " & $strNow & " - " & $strNowArtist & " (" & _iTunes_SongRating() & ") " & _iTunes_SongGetLength() & @LF)
                        Local $hasPosted = _Twitter_Post()
                        _SendData("#nowplaying " & _URLEncode(_iTunes_SongName()) & " - " & _URLEncode(_iTunes_SongArtist())) ; Send data to database
;~                      ; Removed because it's now redundant
;~                      If @Compiled Then
;~                          $hasPosted = _Twitter_Post()
;~                          $lastPost = _Twitter_Last()
;~                      Else
;~                          _Debug("echo " & "In debug mode." & @LF)
;~                          $hasPosted = True ; Only do debug
;~                          $lastPost = "#nowplaying " & _URLEncode(_iTunes_SongName()) & " - " & _URLEncode(_iTunes_SongArtist())
;~                      EndIf
                        ; If the data is successfully posted to Twitter
                        If $hasPosted Then
                            ; _Toast(_iTunes_SongName() & " - " & $strNowArtist) ; Show a toast
                            If @Compiled Then
                                _Toast($strNow, $strNowArtist)
;~                              $lastPost = _Twitter_Last()
                            Else
                                TrayTip("iTunesTweet", $strNow & " - " & $strNowArtist, 5)
                                TraySetToolTip("iTunesTweet (DEV)" & @LF & $strNow & " - " & $strNowArtist) ; Set the tray icon toolip
                            EndIf
                        EndIf
                    EndIf
                EndIf
            EndIf
        EndIf
    EndIf

    ; Tray event handling
    $tMsg = TrayGetMsg()
    Select
        Case $tMsg = 0
            ContinueLoop ; Nothing, continue
        Case $tMsg = $traySite
            ShellExecute("http://www.itunestweet.com") ; Open the site
        Case $tMsg = $trayAbout
            _About_iTT() ; About iTT
        Case $tMsg = $trayReport ; Bug? Tell the user what to do
            MsgBox(0x40, $strAppName, "Found a bug? Sorry! " & @CR & @CR & _
                    "Either send a DM to @" & $strAppName & " or email us at support@itunestweet.com" & @CR & @CR & _
                    "Sorry for any problems that may have occured!")
        Case $tMsg = $trayCleanup
            _iTunesDupes()
        Case $tMsg = $trayPause
            $isPaused = Not $isPaused
            $iCOMEvnt = Not $iCOMEvnt
            If $isPaused Then
                _Toast($strAppName, "Paused!")
                TrayItemSetText($trayPause, "Unpause " & $strAppName)
                TrayItemSetState($trayPause, 1)
            Else
                _Toast($strAppName, "Unpaused!")
                TrayItemSetText($trayPause, "Pause " & $strAppName)
                TrayItemSetState($trayPause, 4)
            EndIf
        Case $tMsg = $trayOptions

        Case $tMsg = $trayExit
            _iTunes_Quit()
            Exit 0
    EndSelect
WEnd

#region Functions
#region Setup
Func _Setup()
    Global $_GuiDelete

    $_Image = _ResourceGetAsImage('LOGO_PNG_1')
    $_Width = _GDIPlus_ImageGetWidth($_Image)
    $_Height = _GDIPlus_ImageGetHeight($_Image)
    $_Ratio = $_Width / $_Height

    For $_Width = 300 To @DesktopWidth / 1.3 Step 10
        $_Gui = GUICreate($strAppName, $_Width, $_Width / $_Ratio, -1, -1, -1, BitOR($WS_EX_LAYERED, $WS_EX_TOPMOST, $WS_EX_TOOLWINDOW))
        $_Image = _ImageResize(@TempDir & '\iTT-Logo.png', $_Width, $_Height)
        _SetBitMap($_Gui, $_Image, 255, $_Width, $_Width / $_Ratio)
        GUISetState(@SW_SHOW)
    Next

    Sleep(2000)
    _GDIPlus_GraphicsDispose($_Image)

    _GDIPlus_Shutdown()
    $_GuiDelete = Not $_GuiDelete
    While Not $_GuiDelete
        $_GuiDelete = Not GUIDelete(WinGetHandle("gui"))
    WEnd

    If Ping("http://www.itunestweet.com") Then _Debug("echo " & "Ping'ed' website!" & @LF) ; Ping success

    _oAuth_Confirm() ; Ask user to login securely
    _AskInformation() ; Ask user if they would like to collect data

    $msgTweetUsr = MsgBox(4, $strAppName, "When possible would you like to @mention the song artist?")
    If $msgTweetUsr = 6 Then
        RegWrite($sReg, "tweetUser", "REG_DWORD", 1)
    Else
        RegWrite($sReg, "tweetUser", "REG_DWORD", 0)
    EndIf

    RegWrite($sReg, "setupDir", "REG_SZ", @ScriptDir)

    ; Write the text files
    _WriteTxt("README_TXT_1") ; Readme
    _WriteTxt("FAQ_TXT_1") ; FAQ

    FileCreateShortcut(@ScriptDir & '\' & @ScriptName, @ScriptDir & '\Uninstall ' & $strAppName & ".lnk", @ScriptDir, '/UNINSTALL') ; Create an uninstall shortcut
    MsgBox(0x40, $strAppName, "Congratulations! You have now setup " & $strAppName & "!") ; Just give a message congratulating
    TrayTip($strAppName, "Hey! I'm " & $strAppName & "! Click me to edit your settings or to close me!", 4) ; Notify the user where I am
EndFunc ;==>_Setup
#endregion Setup

#region Uninstall
Func _Uninstall()
    $chkUninstall = MsgBox(0x1004, $strAppName, "Are you sure you wish to uninstall " & $strAppName & "?") ; Check for sure they want to uninstall
    If $chkUninstall = 6 Then
        ProgressOn("Uninstalling " & $strAppName, "", "", 0, 0, 18) ; Make a purty progress bar
        ProgressSet(10, "Cleaning up..")
        $regDel = MsgBox(0x1004, $strAppName, "Do you wish to remove your Twitter account settings?") ; Check if they want to remove their Twitter settings
        If $regDel = 6 Then
            ProgressSet(25, "Removing registry files.")
            RegDelete($sReg) ; Delete all registry settings if they say no
        Else
            ProgressSet(25, "Removing Twitter account settings")
            RegDelete($sReg, "tweetRan") ; Otherwise just delete the registry to say they've ran iTT
            RegDelete($sReg, "oAuth_Secret")
            RegDelete($sReg, "oAuth_Token")
            RegDelete($sReg, "oAuth_Twitter")
            RegDelete($sReg, "tweetImg")
        EndIf
        $regSet = MsgBox(0x1004, $strAppName, "Do you wish to remove " & $strAppName & " application settings?") ; Check if they want to remove application settings
        If Not $regSet = 6 And Not $regDel = 6 Then
            ProgressSet(40, "Removing Twitter account settings")
            RegDelete($sReg, "tweetUsr")
            RegDelete($sReg, "usrAge")
            RegDelete($sReg, "usrCountry")
            RegDelete($sReg, "canSend")
            RegDelete($sReg, "tweetUser")
        EndIf
        ProgressSet(50, "Removing uninstall and startup shortcuts.")
        FileDelete(@ScriptDir & '\Uninstall ' & $strAppName & ".lnk") ; Delete the uninstall shortcut
        If FileExists(@ScriptDir & '\' & @ScriptName) Then FileDelete(@ScriptDir & '\' & @ScriptName) ; Delete the startup shortcut
        ProgressSet(80, "Removing help files.")
        FileDelete(@ScriptDir & "\README.txt")
        FileDelete(@ScriptDir & "\FAQ.txt")
        ProgressSet(90, "Creating uninstall routine...")
        ; Create the file to remove EVERYTHING else
        $hFile = FileOpen(@TempDir & "\~deltmp.bat", 2)
        $sCMDLine = _StringANSI2OEM(':CheckFile' & @CRLF & _
                'If exist "' & @ScriptFullPath & '" GOTO :DelFile' & @CRLF & _
                'Goto :CheckFile' & @CRLF & _
                ':DelFile' & @CRLF & _
                'del /f /q "' & @ScriptFullPath & '"' & @CRLF & _
                'del /f /q "' & @TempDir & '\~deltmp.bat"')
        FileWrite($hFile, $sCMDLine)
        FileClose($hFile)
        ProgressSet(100, "Removing self.")
        MsgBox(0x40, $strAppName, $strAppName & " has completely removed itself!")
        ProgressOff()
        Run(@TempDir & "\~deltmp.bat", @TempDir, @SW_HIDE)
        Exit
    EndIf
EndFunc ;==>_Uninstall
#endregion Uninstall

#region Objects: AU3.Error and iTunes
; This function is the most KICK ass ever!
Func A3_ClearErr()
    _Debug("echo " & "A3 COM problem ignored" & @LF)
    Return
EndFunc ;==>A3_ClearErr

Func IE_Err()
    SetError(1) ; Just set the error
    Return
EndFunc ;==>IE_Err
#endregion Objects: AU3.Error and iTunes

#region Twitter functions
; This is a new version of Twitter_Post() which also lets us use oauth
Func _Twitter_Post()
    #cs
        This whole function send us:
            * OAUTH code - required for Twitter
            * OAUTH token - also required
            * OAUTH SEC - secret which is also required
            * Song - what song they're playing
            * Author (artist)

        This function only actually tweets, it doesn't do other things.
    #ce
    If Not $oAuth_oAuth Then _oAuth_Confirm() ; Only called if we need don't have any data already
    If $oAuth_oAuth Then
        $strSafe_Song = StringReplace(_iTunes_SongName(), "&", "%26%2338;")
        $strSafe_Artist = StringReplace(_iTunes_SongArtist(), "&", "%26%2338;")
        ;Local $strToPost = "oauth=" & $oAuth_oAuth & "&tok=" & $oAuth_Tok & "&sec=" & $oAuth_Sec & "&song=" & _URLEncode(_iTunes_SongName()) & "&author=" & _URLEncode(_iTunes_SongArtist())
        Local $strToPost = "oauth=" & $oAuth_oAuth & "&tok=" & $oAuth_Tok & "&sec=" & $oAuth_Sec & "&song=" & $strSafe_Song & "&author=" & $strSafe_Artist
        If RegRead($sReg, "tweetUser") = 1 Then $strToPost &= "&tweetArtist=1" ; Tweet artist username
        _Debug("echo " & "Sending data..." & @LF)
        If Not @Compiled Then
            ; Local testing
            $objHTTP.Open("POST", "http://127.0.0.1/oauth/postTo.php", False)
        Else
            $objHTTP.Open("POST", "http://www.itunestweet.com/oauth/postTo.php", False)
        EndIf
        $objHTTP.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") ; Set the Content-Type header
        ; _SetReqHeader()
        $objHTTP.Send($strToPost)

;~      If Not @Compiled Then _Debug("echo " & $strToPost & @LF)

        ConsoleWrite($strToPost)

        $StatusCode = $objHTTP.Status
        If $StatusCode = 200 Then
            _Debug("echo " & "Status updated!" & @LF)
            $lastPost = _Twitter_Last()
            Return 1
        Else
            _Debug("echo " & " Status update failed: " & $StatusCode & @LF)
            _AddStatusCode($StatusCode)
            $lastPost = _Twitter_Last()
            Return 0
        EndIf
    EndIf
EndFunc ;==>_Twitter_Post

; Attempts to return the last tweet
Func _Twitter_Last()
    $objHTTP.Open("GET", "http://twitter.com/statuses/user_timeline/" & _Reg_Val("tweetUsr") & ".xml?count=2", False) ; Set the request up for one previous tweet
    $objHTTP.Send() ; Send a request

    $StatusCode = $objHTTP.Status ; Get the status of the last tweet
    If $StatusCode = 200 Then ; If the status is 200 (OK) then go!
;~      _Debug("echo " & "Twitter Status code: " & $StatusCode & @LF) ; Debug the status code
        $strReturn = StringRegExp($objHTTP.responseText, "<text>((.?)*)</text>", 1)
        If IsArray($strReturn) Then
            _Debug("echo " & "Last tweet: " & $strReturn[0] & @LF)
            Return $strReturn[0] ; Return the text between the XML node
        Else
            Return ""
        EndIf
    Else
        _AddStatusCode($StatusCode)
;~      _Debug("echo " & "Twitter Status code: " & $StatusCode & @LF) ; Debug the status code
        Return 0 ; Just return 0, we have nothing to say
    EndIf
EndFunc ;==>_Twitter_Last
#endregion Twitter functions

#region Data collection functions
; This window asks if the user will allow us collect data about them
Func _AskInformation()
    $hTerms = GUICreate($strAppName & " Data Collection", 409, 220)
    GUICtrlCreateLabel("Help us create a better application!", 8, 8, 247, 20)
    GUICtrlSetFont(-1, 10, 800, 0)
    GUICtrlCreateLabel("In order for us to create a better application, we'd like to collect data about the way " & _
            "you use " & $strAppName & ". We do not collect your username or password, only the tweets made, your age and location. " & _
            "This will help us create a more enchanced application, full of rich features. Please fill in the information" & _
            "below if you'd like to help us, otherwise you can close this window.", 8, 32, 394, 67)
    $selCountry = GUICtrlCreateCombo("Select your country", 8, 104, 394, 16, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
    GUICtrlSetData(-1, "Afghanistan|Albania|Algeria|Andorra|Angola|Antigua and Barbuda|Argentina|Armenia|Australia|Austria|Azerbaijan|Bahamas|Bahrain|Bangladesh|Barbados|Belarus|Belgium|Belize|Benin|Bhutan|Bolivia|Bosnia and Herzegovina|Botswana|Brazil|Brunei|Bulgaria|Burkina Faso|Burundi|Cambodia|Cameroon|Canada|Cape Verde|Central African Republic|Chad|Chile|China|Colombia|Comoros|Congo (Brazzaville)|Congo, Democratic Republic of the|Costa Rica|Côte d'Ivoire|Croatia|Cuba|Cyprus|Czech Republic|Denmark|Djibouti|Dominica|Dominican Republic|East Timor (Timor Timur)|Ecuador|Egypt|El Salvador|Equatorial Guinea|Eritrea|Estonia|Ethiopia|Fiji|Finland|France|Gabon|Gambia, The|Georgia|Germany|Ghana|Greece|Grenad|Guatemala|Guinea|Guinea-Bissau|Guyana|Haiti|Honduras|Hungary|Iceland|India|Indonesia|Iran|Iraq|Ireland|Israel|Italy|Jamaica|Japan|Jordan|Kazakhstan|Kenya|Kiribati|Korea, North|Korea, South|Kuwait|Kyrgyzstan|Laos|Latvia|Lebanon|Lesotho|Liberia|Libya|Liechtenstein|Lithuania|Luxembourg|Macedonia, Former Yugoslav Republic of|Madagascar|Malawi|Malaysia|Maldives|Mali|Malta|Marshall Islands|Mauritania|Mauritius|Mexico|Micronesia, Federated States of|Moldova|Monaco|Mongolia|Montenegro|Morocco|Mozambique|Myanmar (Burma)|Namibia|Nauru|Nepal|Netherlands|New Zealand|Nicaragua|Niger|Nigeria|Norway|Oman|Pakistan|Palau|Panama|Papua New Guinea|Paraguay|Peru|Philippines|Poland|Portugal|Qatar|Romania|Russia|Rwanda|Saint Kitts and Nevis|Saint Lucia|Saint Vincent and The Grenadines|Samoa|San Marino|Sao Tome and Principe|Saudi Arabia|Senegal|Serbia|Seychelles|Sierra Leone|Singapore|Slovakia|Slovenia|Solomon Islands|Somalia|South Africa|Spain|Sri Lanka|Sudan|Suriname|Swaziland|Sweden|Switzerland|Syria|Taiwan|Tajikistan|Tanzania|Thailand|Togo|Tonga|Trinidad and Tobago|Tunisia|Turkey|Turkmenistan|Tuvalu|Uganda|Ukraine|United Arab Emirates|United Kingdom|United States|Uruguay|Uzbekistan|Vanuatu|Vatican City|Venezuela|Vietnam|Western Sahara|Yemen|Zambia|Zimbabwe")
    GUICtrlCreateLabel("Age:", 8, 128)
    $inpAge = GUICtrlCreateInput("", 42, 128, 100)
    $btnCollect = GUICtrlCreateButton("Save Information", 120, 162, 171, 49)
    GUISetState(@SW_SHOW)

    While WinExists($hTerms)
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case -3
                RegWrite($sReg, "canSend", "REG_DWORD", 0) ; The user doesn't want to help us collect data
                GUIDelete($hTerms) ; Close the GUI and continue
            Case $btnCollect
                RegWrite($sReg, "canSend", "REG_DWORD", 1) ; The user does want to help us
                RegWrite($sReg, "usrCountry", "REG_SZ", GUICtrlRead($selCountry)) ; Write the country to the registry
                RegWrite($sReg, "usrAge", "REG_SZ", GUICtrlRead($inpAge)) ; Write the age to the registry
                GUIDelete($hTerms) ; Close the GUI and continue
                Return
        EndSwitch
    WEnd
EndFunc ;==>_AskInformation

; This function sends data to our website IF the user wants to participate in data collection
Func _SendData($ioTweet)
    If @Compiled Then
        $objHTTP.Open("POST", "http://www.itunestweet.com/code/collect_data.php", False) ; Open the HTTP request
    Else
        ; Debug only
        $objHTTP.Open("POST", "http://127.0.0.1/itt/collect_data.php", False) ; Open the HTTP request
    EndIf
    $objHTTP.setRequestHeader("User-Agent", "iTunesTweet-DataBot") ; Set up our user agent string
    $objHTTP.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") ; Set the request header
    If RegRead($sReg, "canSend") = 1 Then ; If the user is a part of the data collection process then
        Local $strData = "country=" & RegRead($sReg, "usrCountry") & "&age=" & RegRead($sReg, "usrAge") & "&tweet=" & $ioTweet & "&genre=" & _iTunes_SongGenre() & "&ttl=" & _iTunes_SongTTL() ; Advanced information
    Else
        Local $strData = "tweet=" & $ioTweet & "&genre=" & _iTunes_SongGenre() & "&ttl=" & _iTunes_SongTTL() ; Basic information
    EndIf

    If Not @Compiled Then _Debug("echo " & $strData & @LF)

    $objHTTP.Send($strData) ; Send the data our server wants
;~  _Debug("echo " & $objHTTP.responseText & @LF)
    If $objHTTP.Status = 200 Then ; If we get an ok then...
        _Debug("echo " & "Data collection response: " & $objHTTP.responseText & @LF)
    Else
        _WriteLog($objHTTP.responseText) ; Write to the log file
;~      _Debug("echo " & $objHTTP.responseText & @LF)
    EndIf
EndFunc ;==>_SendData
#endregion Data collection functions

#region oAuth Functions
; This function shows the oAuth confirmation window
Func _oAuth_Confirm()
    Global $hAuth
    $msg_oAuth = MsgBox(1, $strAppName, "Please login with your Twitter details to confirm " & $strAppName & " authorization.")
    If $msg_oAuth = 1 Then
        $hAuth = GUICreate("Twitter Confirm", 800, 425)
        $GUIActiveX = GUICtrlCreateObj($oIE, 0, 0, 800, 425)
        GUISetState(@SW_SHOW)

        _IENavigate($oIE, "http://www.itunestweet.com/oauth/start.php")

        While 1
            $nMsg = GUIGetMsg()
            Switch $nMsg
                Case -3
                    $IE_URL = _IEPropertyGet($oIE, "locationurl")
                    _Debug("echo " & $IE_URL & @LF)
                    If Not StringInStr($IE_URL, "twitter.com/oauth/authorize?oauth_token=") Then
                        $didConf = True
                        _oAuth_Data()
                    Else
                        $didConf = False
                    EndIf
                    GUIDelete($hAuth)
                    Return ; Jump back
            EndSwitch
        WEnd
    EndIf
EndFunc ;==>_oAuth_Confirm

; This function strips out the data we need to store oAuth data
Func _oAuth_Data()
    Local $oAuth_Data = _IEBodyReadHTML($oIE)

    ; Get the data from regular expression to array and split that array
    $arr_oAuth = StringRegExp($oAuth_Data, "<SPAN\b[^>]*>(.*?)</SPAN>", 1)
    If IsArray($arr_oAuth) Then
        $arr_oAuth_Data = StringSplit($arr_oAuth[0], "|")
        If IsArray($arr_oAuth_Data) Then
            ; Store the data in the registry
            RegWrite($sReg, "oAuth_Twitter", "REG_SZ", $arr_oAuth_Data[1]) ; oAuth Twitter Token
            RegWrite($sReg, "oAuth_Token", "REG_SZ", $arr_oAuth_Data[2]) ; oAuth Token
            RegWrite($sReg, "oAuth_Secret", "REG_SZ", $arr_oAuth_Data[3]) ; oAuth Secret Token
            RegWrite($sReg, "tweetUsr", "REG_SZ", $arr_oAuth_Data[4]) ; Twitter profile image link
            RegWrite($sReg, "tweetImg", "REG_SZ", $arr_oAuth_Data[5]) ; Twitter username
            RegWrite($sReg, "tweetRan", "REG_DWORD", 1) ; We've installed!
        Else
            $msg_reTry = MsgBox(4, $strAppName, "iTunesTweet could not confirm your authorization. Shall we try again?")
            If $msg_reTry = 6 Then
                _oAuth_Confirm()
            Else
                Exit
            EndIf
        EndIf
    Else
        $msg_reTry = MsgBox(4, $strAppName, "iTunesTweet could not confirm your authorization. Shall we try again?")
        If $msg_reTry = 6 Then
            _oAuth_Confirm()
        Else
            Exit
        EndIf
    EndIf
EndFunc ;==>_oAuth_Data
#endregion oAuth Functions

#region Helper functions
Func _Reg_HasVal($regVal, $regKey = -1)
    If $regKey = -1 Then $regKey = $sReg
    If RegRead($sReg, $regVal) Then Return 1
EndFunc ;==>_Reg_HasVal

Func _Reg_Val($regVal, $regKey = -1)
    If $regKey = -1 Then $regKey = $sReg
    If _Reg_HasVal($regVal, $regKey) Then Return RegRead($sReg, $regVal)
EndFunc ;==>_Reg_Val

; This function writes the RCDATA TXT files that we defined earlier
Func _WriteTxt($RCNAME)
    $sFileName = StringSplit($RCNAME, "_") ; Split the RC name of the file
    $sFileName = $sFileName[1] & ".txt" ; Get the "filename" and append the .txt extension
    $hFile = FileOpen(@ScriptDir & "\" & $sFileName, 2) ; Open the file
    FileWrite($hFile, _ResourceGetAsString($RCNAME)) ; Write the file contents from the resource
    FileClose($hFile) ; Close the file
EndFunc ;==>_WriteTxt

Func _URLEncode($urlText)
    Local $sText

    For $i = 1 To StringLen($urlText)
        $acode = Asc(StringMid($urlText, $i, 1))
        Select
            Case ($acode >= 48 And $acode <= 57) Or _
                    ($acode >= 65 And $acode <= 90) Or _
                    ($acode >= 97 And $acode <= 122)
                $sText &= StringMid($urlText, $i, 1)
            Case $acode = 32
                $sText &= "%20"
            Case Else
                $sText &= "%" & Hex($acode, 2)
        EndSelect
    Next

    Return $sText
EndFunc ;==>_URLEncode

; This function translates a string into the OEM-defined character set.
Func _StringANSI2OEM($strText)
    Local $buf = DllStructCreate("char[" & StringLen($strText) + 1 & "]")
    Local $ret = DllCall("user32.dll", "int", "CharToOem", "str", $strText, "ptr", DllStructGetPtr($buf))
    If Not (IsArray($ret)) Then Return SetError(1, 0, '')
    If $ret[0] = 0 Then Return SetError(2, $ret[0], '')
    Return DllStructGetData($buf, 1)
EndFunc ;==>_StringANSI2OEM

; This is just for debugging
Func _AddStatusCode($ioStatusCode)
    _Debug("echo " & "Current size of errors: " & $stsCodes & @LF)

    ; If there are more than three fails then output a debug file and exit the script
    If $stsCodes >= 3 Then
        MsgBox(0x10, $strAppName, "There was three or more consecutive fails." & @CR & @CR & _
                $strAppName & " will now close. Please try running me again!")
        ; _iTunes_Quit() ; Quit the iTunes application interface
        Exit ; Screw it... Too many errors, close
    Else
        $stsCodes = ($stsCodes + 1) ; Increment the error value
        $hDbFile = FileOpen(@ScriptDir & "\" & $strAppName & ".log", 1) ; Open the error log
        FileWrite($hDbFile, "Fatal status: " & $ioStatusCode & @LF) ; Write the error status
        FileClose($hDbFile) ; Close the file
    EndIf
EndFunc ;==>_AddStatusCode

; This function will only work compiled since the toast image is included as a resource
Func _Toast($ioSong, $ioArtist)
    DllCall("kernel32.dll", "handle", "CreateMutexW", "ptr", 0, "bool", 1, "wstr", $strAppName & "Toast")
    Local $maxLength = 20, $maxPos = 172
    Local $tryShell = WinGetPos("[CLASS:Shell_TrayWnd]")

    $hwnd_Toast = GUICreate("iTunesTweet", 250, 250, @DesktopWidth - 262, @DesktopHeight, 0x96000000, BitOR(0x00080008, 0x00000080))
    $hImageButton2 = _ResourceGetAsImage("TOAST_1")

    If StringLen($ioSong) >= $maxLength Then
        $int_Dif = StringLen($ioSong) - $maxLength
        $ioSong = StringTrimRight($ioSong, $int_Dif + 3) ; Cut off an extra three
        $ioSong &= "..." ; Add a cut off
    EndIf

    If StringLen($ioArtist) >= $maxLength Then
        $int_Dif = StringLen($ioArtist) - $maxLength
        $ioArtist = StringTrimRight($ioArtist, $int_Dif + 3) ; Cut off an extra three
        $ioArtist &= "..." ; Add a cut off
    EndIf

    $hBmp = _ImageDrawText($hImageButton2, $ioSong & @LF & $ioArtist, 90, 42, 0x111111, 10, 0, "Corbel")
    SetBitmap($hwnd_Toast, $hBmp, 255)

    GUIRegisterMsg($WM_RBUTTONUP, "WM_RBUTTONUP")
    GUIRegisterMsg($WM_LBUTTONUP, "WM_LBUTTONUP")

    GUISetState()

    For $i = (@DesktopHeight - ($tryShell[3] + 8)) To (@DesktopHeight - $maxPos) Step -10
        WinMove($hwnd_Toast, "", @DesktopWidth - 262, $i)
        Sleep(50)
    Next

    $tmr_Start = TimerInit()
    Do
        Sleep(100)
    Until TimerDiff($tmr_Start) >= $toast_Timeout
    _Debug("echo " & "Timer out." & @LF)

    ; Delete Toast slice
    GUIDelete($hwnd_Toast)
EndFunc ;==>_Toast

; Write to the log file
Func _WriteLog($strLogMsg)
    ; If there are more than three fails then output a debug file and exit the script
    $stsCodes += 1 ; Increment the error value
    $hDbFile = FileOpen(@ScriptDir & "\" & $strAppName & ".log", 1) ; Open the error log
    FileWrite($hDbFile, "Logging message: " & $strLogMsg & " (" & @MON & "/" & @MDAY & "/" & @YEAR & ") " & @LF) ; Write the error log
    FileClose($hDbFile) ; Close the file
EndFunc ;==>_WriteLog

; Write to a debug file
Func _Debug($strLogMsg)
    If $shouldDbg Then
        $hDbFile = FileOpen(@ScriptDir & "\" & $strAppName & ".dbg", 1)
        FileWrite($hDbFile, $strLogMsg)
        $strLogMsg = StringStripCR($strLogMsg)
;~      $strLogMsg = PrepareString($strLogMsg)
        If Not @Compiled Then StringRegExpReplace($strLogMsg, "echo ", "")
        ConsoleWrite($strLogMsg & @LF)
        FileClose($hDbFile) ; Close the file
    EndIf
EndFunc ;==>_Debug

; Displays the about box for iTT
Func _About_iTT()
    $hAbout = GUICreate("About " & $strAppName, 516, 145)
    $picLogo = GUICtrlCreatePic("", 8, 8, 128, 128)
    _ResourceSetImageToCtrl($picLogo, "LOGO_PNG_1") ; Birdy Logo
    GUICtrlCreateLabel($strAppName, 144, 8, 100, 20)
    GUICtrlSetFont(-1, 10, 800, 0) ; Make the title bigger and bolder
    GUICtrlCreateLabel("Created by James Brooks (@jbrooksuk) and Keylee Odlum (@keyls)", 144, 32, 361, 17)
    GUICtrlCreateLabel("Special thanks to our beta testers:" & @CR & _
            "@leezyw, @sazzz_, @_cohenl_, @chriscooper45 & @rawradam" & @CR & @CR & _
            "Check out our Twitter twitter.com/iTunesTweet" & @CR & @CR & _
            "iTunes is a registered trademark of Apple.", 144, 56, 365, 93)
    GUISetState(@SW_SHOW)

    While WinExists($hAbout)
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case -3
                GUIDelete($hAbout)
        EndSwitch
    WEnd
EndFunc ;==>_About_iTT

Func _ConsoleDebugRun()
    DllCall("kernel32.dll", "handle", "CreateMutexW", "ptr", 0, "bool", 1, "wstr", $strAppName & "DebugMutex")
    If @error Then Return SetError(1)
    Local $lastErr = DllCall("kernel32.dll", "dword", "GetLastError")
    If @error Then Return SetError(2)
    If $lastErr[0] <> 183 Then ; ERROR_ALREADY_EXISTS
        Run(@ComSpec & ' /c @echo off && title ' & $strAppName & ' - Debug Console && prompt $G && color 0F && "' & _
                @ScriptFullPath & '" ' & $CmdLineRaw & ' | ' & @ComSpec & ' /Q')
        Sleep(3000) ; give time to launch child
        Exit
    EndIf
EndFunc ;==>_ConsoleDebugRun
#endregion Helper functions

#region DataMgr
Func DataMgr()
    If @error Then Return SetError(1)
    If @compiled Then
        Local $webDataMgr = 'http://www.itunestweet.com/code/datamgr.ini'
    Else
        Local $webDataMgr = 'http://127.0.0.1/itunestweet/dev/datamgr.ini'
    EndIf
    Local $dataMgr = @TempDir & '\iTunesTweet\DataMgr.ini'
    Local $strDataSep = '¦' ; If changed, MUST UPDATE datamgr.ini
    Local $webData[1][1] ; Create a very small 2D array
    Local $curDataMgrVer = 0 ; Testing
    Local $iPriority = 0

    If Not FileExists(@TempDir & '\iTunesTweet') Then DirCreate(@TempDir & '\iTunesTweet')
    If Not FileExists(@TempDir & '\iTunesTweet\Reg') Then DirCreate(@TempDir & '\iTunesTweet\Reg')
    If Not FileExists(@TempDir & '\iTunesTweet\RunMe') Then DirCreate(@TempDir & '\iTunesTweet\RunMe')

    $ingDataMgr = InetGet($webDataMgr, $dataMgr, 1, 1)
    Do
        Sleep(250)
    Until InetGetInfo($ingDataMgr, 2)
    InetClose($ingDataMgr)

    ; Does the DataMgr file even need to be collected?
    $iniConfig = IniReadSection($dataMgr, 'Config')
    If @error Then
        _Debug('echo Error handling DataMgr file.' & @LF)
    Else
;~      For $i = 1 To $iniConfig[0][0]
;~          $dataMgrVer = $iniConfig[$i][1] ; Version number
;~          _Debug('echo DataMgr.ini ' & $iniConfig[$i][0] & ': ' & $iniConfig[$i][1] & @LF)

;~          If $iniConfig[0][0] = 2 Then
                ; If we have an important update/change to the DataMgr.ini then we need to put a marker
                ; So we set DataMgrVer to one below even though we're lying.
;~              $curDataMgrVer = Int(RegRead($sReg, "DataMgr"))
;~              If @error Then $dataMgrVer = 0 ; Make the version up
;~              $dataMgrVer = $curDataMgrVer - 1 ; Just get the current version and minus
;~          EndIf
;~      Next
        If $iniConfig[1][0] = "Version" Then
            $curDataMgrVer = Int(RegRead($sReg, "DataMgr"))
            If @error Then $dataMgrVer = 0 ; Make the version up
            $dataMgrVer = $curDataMgrVer - 1 ; Just get the current version and minus
        Else
            _Debug("echo " & "Malformed DataMgr.ini file!")
            Return 0
        EndIf

;~      If IniRead($sReg, "Priority") Then
;~          $iPriority = Int
;~      Else
;~          _Debug("echo " & "Malformed DataMgr.ini file!")
;~      EndIf
    EndIf

    If $dataMgrVer >= Int(RegRead($sReg, "DataMgr")) Then
        ; Interpret the files and shit
        $iniFiles = IniReadSection($dataMgr, 'Files')
        If @error Then
            _Debug('echo Error handling DataMgr file.' & @LF)
        Else
            RegWrite($sReg, "DataMgr", "REG_SZ", $dataMgrVer) ; Even though we could possibly lie previously, we can ignore it
            ReDim $webData[$iniFiles[0][0]][4]
            For $i = 1 To $iniFiles[0][0]
                $arDataData = StringSplit($iniFiles[$i][1], $strDataSep)
                $webData[$i - 1][0] = $arDataData[1] ; Download location
                $webData[$i - 1][1] = $arDataData[2] ; Save file name
                $webData[$i - 1][2] = $arDataData[3] ; Storage location
                $webData[$i - 1][3] = $arDataData[4] ; File description for log
            Next

            ; Download DataMgr files
            For $j = 0 To UBound($webData) - 1
                Local $storage, $doMe = False, $isReg = False
                _Debug('echo Type: ' & StringUpper($webData[$j][2]) & @LF)
                Switch StringUpper($webData[$j][2])
                    Case "SCRIPT"
                        $storage = @ScriptDir
                    Case "TEMP"
                        $storage = @TempDir & '\iTunesTweet'
                    Case "EXE", "BAT"
                        $storage = @TempDir & '\iTunesTweet\RunMe'
                        $doMe = True
                    Case "REG"
                        $storage = @TempDir & '\iTunesTweet\Reg'
                        $doMe = True
                        $isReg = True
                EndSwitch
                ; File is normal download and wait
                If @compiled Then
                    $dwndHandle = InetGet("http://www.itunestweet.com/" & $webData[$j][0], $storage & "\" & $webData[$j][1], 1, 1)
                Else
                    $dwndHandle = InetGet("http://127.0.0.1/itunestweet/dev/" & $webData[$j][0], $storage & "\" & $webData[$j][1], 1, 1)
                EndIf
                Do
                    Sleep(250)
                Until InetGetInfo($dwndHandle, 2)
                InetClose($dwndHandle)

                _Debug('echo $doMe=' & $doMe & " ... $isReg=" & $isReg & @LF)
                If $doMe Then
                    _Debug('echo Running ' & $webData[$j][1] & @LF)
                    If $isReg Then
                        _Debug('echo Regedit ' & $webData[$j][1] & @LF)
                        Run("REGEDIT /S " & $storage & '\' & $webData[$j][1], $storage, @SW_HIDE)
                    ElseIf Not $isReg Then
                        Run($storage & '\' & $webData[$j][1], $storage, @SW_HIDE)
                    EndIf
                EndIf

;~          Log downloaded file
                _Debug('echo ' & $webData[$j][1] & " downloaded via DataMgr: " & $webData[$j][3] & @LF)

;~          Update the registry with the latest version of DataMgr
                If $curDataMgrVer Then
                    $regVerNum = $curDataMgrVer
                Else
                    $regVerNum = Int(RegRead($sReg, "DataMgr"))
                EndIf
                RegWrite($sReg, "DataMgr", "REG_SZ", $curDataMgrVer)
            Next
        EndIf
    EndIf
EndFunc ;==>DataMgr
#endregion DataMgr

#region Toast functions
Func SetBitmap($hGUI, $hImage, $iOpacity)
    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", _GDIPlus_ImageGetWidth($hImage))
    DllStructSetData($tSize, "Y", _GDIPlus_ImageGetHeight($hImage))
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", $AC_SRC_ALPHA)
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)
EndFunc ;==>SetBitmap

Func _SetBitmap($hGUI, $hImage, $iOpacity, $n_width, $n_height)
    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", $n_width)
    DllStructSetData($tSize, "Y", $n_height)
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", 1)
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)
EndFunc ;==>_SetBitmap

Func _ImageResize($sInImage, $newW, $newH, $sOutImage = "")
    Local $olGlobalage, $GC, $newBmp, $newGC
    If $sOutImage = "" Then _GDIPlus_Startup()
    $olGlobalage = _GDIPlus_ImageLoadFromFile($sInImage)
    $GC = _GDIPlus_ImageGetGraphicsContext($olGlobalage)
    $newBmp = _GDIPlus_BitmapCreateFromGraphics($newW, $newH, $GC)
    $newGC = _GDIPlus_ImageGetGraphicsContext($newBmp)
    _GDIPlus_GraphicsDrawImageRect($newGC, $olGlobalage, 0, 0, $newW, $newH)
    _GDIPlus_GraphicsDispose($GC)
    _GDIPlus_GraphicsDispose($newGC)
    _GDIPlus_ImageDispose($olGlobalage)
    If $sOutImage = "" Then
        Return $newBmp
    Else
        _GDIPlus_ImageSaveToFile($newBmp, $sOutImage)
        _GDIPlus_BitmapDispose($newBmp)
        _GDIPlus_Shutdown()
        Return 1
    EndIf
EndFunc ;==>_ImageResize

Func _ImageDrawText($hImage, $sText, $iX = 0, $iY = 0, $iRGB = 0x000000, $iSize = 9, $iStyle = 0, $sFont = "Arial")
    Local $w, $h, $hGraphic1, $hBitmap, $hGraphic2, $hBrush, $hFormat, $hFamily, $hFont, $tLayout, $aInfo
    $w = _GDIPlus_ImageGetWidth($hImage)
    $h = _GDIPlus_ImageGetHeight($hImage)

    ;Create a new bitmap, this way the original opened png is left unchanged
    $hGraphic1 = _GDIPlus_GraphicsCreateFromHWND(_WinAPI_GetDesktopWindow())
    $hBitmap = _GDIPlus_BitmapCreateFromGraphics($w, $h, $hGraphic1)
    $hGraphic2 = _GDIPlus_ImageGetGraphicsContext($hBitmap)

    ; Draw the original opened png into my newly created bitmap
    _GDIPlus_GraphicsDrawImageRect($hGraphic2, $hImage, 0, 0, $w, $h)

    ;Create the font
    $hBrush = _GDIPlus_BrushCreateSolid("0xFF" & Hex($iRGB, 6))
    $hFormat = _GDIPlus_StringFormatCreate()
    $hFamily = _GDIPlus_FontFamilyCreate($sFont)
    $hFont = _GDIPlus_FontCreate($hFamily, $iSize, $iStyle)
    $tLayout = _GDIPlus_RectFCreate($iX, $iY, 0, 0)
    $aInfo = _GDIPlus_GraphicsMeasureString($hGraphic2, $sText, $hFont, $tLayout, $hFormat)

    ;Draw the font onto the new bitmap
    _GDIPlus_GraphicsDrawStringEx($hGraphic2, $sText, $hFont, $aInfo[0], $hFormat, $hBrush)

    ;Cleanup the no longer needed resources
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphic2)
    _GDIPlus_GraphicsDispose($hGraphic1)

    ;Return the new bitmap
    Return $hBitmap
EndFunc ;==>_ImageDrawText

Func WM_RBUTTONUP($hWnd, $iMsg, $wParam, $lParam)
    GUIDelete($hWnd)
    Return
EndFunc ;==>WM_RBUTTONUP

Func WM_LBUTTONUP($hWnd, $iMsg, $wParam, $lParam)
    GUIDelete($hWnd)
    Return
EndFunc ;==>WM_LBUTTONUP
#endregion Toast functions

#region iTunes Cleanup
Func _iTunesDupes()
    $dupGUI = GUICreate($strAppName & " - Delete duplicate songs", -1, -1)
    GUICtrlCreateLabel("iScan", 24, 9, 44, 24)
    GUICtrlSetFont(-1, 13, 2000, 0, "MS Sans Serif")
    $Scan = GUICtrlCreateButton("Scan Library", 88, 8, 89, 25, 0)
    $lstSongs = GUICtrlCreateListView("Song|Artist|Path", 8, 40, 265, 253)
    _GUICtrlListView_SetExtendedListViewStyle($lstSongs, $LVS_EX_CHECKBOXES) ;Set listview to checkbox style
    _GUICtrlListView_SetColumnWidth($lstSongs, 0, 87)
    _GUICtrlListView_SetColumnWidth($lstSongs, 1, 87)
    _GUICtrlListView_SetColumnWidth($lstSongs, 2, 87)
    $Delete = GUICtrlCreateButton("Delete Selected", 184, 8, 89, 25, 0)
    GUICtrlSetState(-1, $GUI_DISABLE)
    GUISetState()

    While WinExists($dupGUI)
        $msg = GUIGetMsg()
        Select
            Case $msg = $Scan ;scan is pressed
                GUICtrlSetState($Scan, $GUI_DISABLE) ;disable scan button until scan is complete
                $iTracks = $objItunes.LibraryPlaylist.Tracks ;Get tracks from the library
                Global $total = $iTracks.Count
                Global $count = 0
                Global $Tracks[$total + 1][3] ;create an array, the first Globalension sized at the number of songs in your library
                GUICtrlSetData($Scan, "Scanning..")
                ProgressOn($strAppName, "Scanning: ", "0%", -1, -1, 18) ;display progress
                For $i In $iTracks ;for each track found in the library
                    $count += 1 ;keep track # of songs
                    ProgressSet(Round(($count / $total) * 100, 2), Round(($count / $total) * 100, 2) & "%", "Scanning: " & $i.Name)
                    $Tracks[$count][0] = $i.Name ;set array member values
                    $Tracks[$count][1] = $i.Artist
                    $Tracks[$count][2] = $i.Size
                Next
                ProgressSet(0, "Comparing: ", "0%") ;now that we have all the values in the array, start comparing each song
                Global $Found[2][2] ;$Found array assures that we don't find 2 of the same duplicate songs
                Global $foundCount = 1
                Global $foundSwitch = False
                Global $SpaceSaved = 0
                For $i = 1 To $total
                    ProgressSet(Round(($i / $total) * 100, 2), Round(($i / $total) * 100, 2) & "%", "Comparing: " & $Tracks[$i][0])
                    For $x = 1 To $total
                        If PrepareString($Tracks[$i][0]) = PrepareString($Tracks[$x][0]) And _ ;compare song names (see PrepareString())
                                PrepareString($Tracks[$i][1]) = PrepareString($Tracks[$x][1]) And _ ;compare artist
                                $i <> $x Then ;make sure that we aren't comparing the song with itself
                            $foundSwitch = True
                            For $y = 1 To UBound($Found) - 1
                                If $Found[$y][0] = $x Or $Found[$y][1] = $x Then
                                    $foundSwitch = False
                                EndIf
                            Next
                            If $foundSwitch = True Then ;if we haven't already recorded the song as a duplicate, then record it
                                $Found[$foundCount][0] = $x
                                $Found[$foundCount][1] = $i
                                $foundCount += 1
                                ReDim $Found[$foundCount + 1][2]
                                GUICtrlCreateListViewItem($Tracks[$i][0] & "|" & $Tracks[$i][1] & "|" & $iTracks.Item($i).Location, $lstSongs) ;create a new item
                                _GUICtrlListView_SetItemChecked($lstSongs, $listcount, True) ;set it checked by default
                                $listcount += 1
                                $SpaceSaved += $Tracks[$i][2] ;keep track of the total size that could be saved
                            EndIf
                        EndIf
                    Next
                Next
                GUICtrlSetData($Scan, "Completed Scan")
                ProgressOff()
                MsgBox(0x40, $strAppName, "Scan Complete. Possible space saved: " & Round(($SpaceSaved / 1024 / 1024), 2) & " MB")
                GUICtrlSetState($Delete, $GUI_ENABLE)
            Case $msg = -3
                GUIDelete($dupGUI)
                Return
            Case $msg = $Delete
                $doDelete = MsgBox(4, $strAppName, "Do you want to permanently delete your duplicate files?")
                GUICtrlSetState($Delete, $GUI_DISABLE)
                $SpaceSaved = 0
                $count = 0
                For $i = 1 To $foundCount - 1 ;for each item in the listview
                    If _GUICtrlListView_GetItemChecked($lstSongs, $i - ($count + 1)) Then ;if the current item is checked, proceed
                        If $doDelete = 6 Then
                            FileDelete($iTracks.ItemByName($Tracks[$Found[$i][1]][0] ).Location) ;delete the file
                        Else
                            ;TODO: SHGetSpecialFolderPath for My Music directory
                            Local $SHMusicDirectory = SHGetSpecialFolderPath(13)
                            _Debug("echo " & $SHMusicDirectory & "\" & $strAppName & "\" & StringTrimLeft($iTracks.ItemByName($Tracks[$Found[$i][1]][0]), StringInStr($iTracks.ItemByName($Tracks[$Found[$i][1]][0]), "\", 0, -1)) & @LF)
;~                          FileMove($iTracks.ItemByName($Tracks[$Found[$i][1]][0] ).Location, $SHMusicDirectory & "\" & $strAppName & "\" & StringTrimLeft($iTracks.ItemByName($Tracks[$Found[$i][1]][0]), StringInStr($iTracks.ItemByName($Tracks[$Found[$i][1]][0]), "\", 0, -1)))
                        EndIf
                        $iTracks.ItemByName($Tracks[$Found[$i][1]][0] ).Comment = "This song was removed as a duplicate by " & $strAppName & "!"
                        $iTracks.ItemByName($Tracks[$Found[$i][1]][0] ).Delete ;delete the library entry
                        _GUICtrlListView_DeleteItem($lstSongs, $i - ($count + 1)) ;delete it from the listview
                        $SpaceSaved += $Tracks[$Found[$i][1]][2] ;keep track of how much space the user will save
                        $count += 1 ;keep track of how many files have been deleted
                    EndIf
                Next
                MsgBox(0x40, $strAppName, "Task Complete. You have deleted " & $count & " files, and saved " & Round(($SpaceSaved / 1024 / 1024), 2) & " MB of space.")
                GUICtrlSetState($Delete, $GUI_ENABLE)
        EndSelect
    WEnd
EndFunc ;==>_iTunesDupes

Func PrepareString($str) ;removes spaces, quotes, dashes, changes to lowercase (for insensitive case and good comparison)
    Return StringLower(StringStripWS(StringReplace(StringReplace($str, "-", ""), "'", ""), 8))
EndFunc ;==>PrepareString

Func SHGetSpecialFolderPath($csidl)
    Local $hwndOwner = 0, $lpszPath = "", $fCreate = False, $MAX_PATH = 260
    $lpszPath = DllStructCreate("char[" & $MAX_PATH & "]")
    $BOOL = DllCall("shell32.dll", "int", "SHGetSpecialFolderPath", "int", $hwndOwner, "ptr", _
            DllStructGetPtr($lpszPath), "int", $csidl, "int", $fCreate)
    If Not @error Then
        Return SetError($BOOL[0], 0, DllStructGetData($lpszPath, 1))
    Else
        Return SetError(@error, 0, 0)
    EndIf
EndFunc ;==>SHGetSpecialFolderPath
#endregion iTunes Cleanup
#endregion Functions

To run it, you'll need my _iTunesDupes function is a modified version of something _Kurt or somebody wrote, so props to them for that.

James

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...