Как изменить порядок песни в плейлисте iTunes с помощью AppleScript - PullRequest
6 голосов
/ 30 октября 2010

Как переместить песню, находящуюся в списке воспроизведения, в другую позицию в списке с помощью команды AppleScript?

У меня уже есть песня и список воспроизведения, и один уже находится в другом; Мне нужно только изменить позицию.

Моя цель - сортировать треки, которые выбрал пользователь; в идеале, я бы хотел переместить дорожку первого заказа в положение дорожки первого выбора и упорядочить все остальные выбранные дорожки сразу и последовательно после первого заказа.

Ответы [ 5 ]

8 голосов
/ 03 декабря 2012

Команда move выглядит довольно глючной.В моих экспериментах выяснилось, что независимо от того, в какое место вы дадите его для перемещения дорожки, он перемещает его в конец списка воспроизведения.Это все еще должно быть управляемым, хотя, если немного неэффективно.Синтаксис этого выглядит следующим образом:

tell application "iTunes"
move first track of playlist "foo" to end of playlist "foo"
end tell

Есть несколько других конструкций, которые вы должны использовать вместо «end of», включая такие, как «начало списка воспроизведения« foo »», «после дорожки 5 списка воспроизведения "foo" и "до дорожки 5 списка воспроизведения" foo ", но ни один из них не работает должным образом.Но, если вы в основном получаете треки в списке, отсортированном так, как вы хотите, вы сможете просто перебрать список, сказать iTunes, чтобы каждый трек последовательно перемещался в конец плейлиста, и вы получитеотсортированный заказ после того, как все это сделано.

1 голос
/ 02 декабря 2012

Вот скрипт, который использует GUI Scripting.

Но этот вид сценария легко ломается в зависимости от версии iTunes или версии OS . Пользователь должен отрегулировать delay (в зависимости от скорости машины). Пользователь также должен изменить локализованный заголовок в скрипте.

Этот скрипт работает на iTunes 10.6.3 и Mac OS X 10.5.8, я не знаю, работает ли он на других версиях.

Необходимо включить Accessibility Frameworks, установив флажок « Включить доступ для вспомогательных устройств » на панели предпочтений системы универсального доступа.

-- this script work on smart and user playlist
property selPlaylist : missing value
set b to false
tell application "iTunes"
    set oldV to (get version) as string < "11"
    activate
    set selTracks to selection
    if (count selTracks) < 2 then return my displayAlert("Select 2 or more tracks")
    set selPlaylist to container of item 1 of selTracks
    try
        if oldV and class of front window is not in {browser window, playlist window} then --- no  playlist window on iTunes 11
            return my displayAlert("The front window is not a browser or a playlist window")
        else if class of front window is not browser window then
            return my displayAlert("The front window is not a browser window")
        end if
        tell selPlaylist
            set b to special kind is none
            set tShuffle to shuffle
            set isSmart to smart
        end tell
    end try
    if not b then return my displayAlert("This script will not work on special playlist")
    if tShuffle then return my displayAlert("This script will not work when shuffle is ON")

    set {sortOK, gridView, liveUpd} to my checkSortCol()
    if gridView then return my displayAlert("This script will not work on Grid View")
    if not sortOK then return my displayAlert("This script will not work when the sort column is not the 'status' column")
    if isSmart and liveUpd then return my displayAlert("You must uncheck the \"Live updating\" case, (Edit your Smart playlist)")

    --**** do something with these selections  **********
    set sortedTracks to reverse of selTracks -- ***** This example reverses the order. ********

    set dataID to database ID of item 1 of sortedTracks
    my moveSelectedTracks(item 1 of selTracks, sortedTracks, (count sortedTracks))
    reveal (track 1 of selPlaylist whose database ID is dataID)
end tell

on moveSelectedTracks(firstTrack, sortedT, n)
    tell application "System Events"
        tell process "iTunes"
            repeat with i from 1 to n
                my selectRow(item i of sortedT)
                keystroke "c" using command down --  copy track
                my selectRow(firstTrack)
                keystroke "v" using command down -- paste track
                delay 0.1
                tell front window to if subrole is "AXDialog" then click last button -- close dialog
            end repeat
            my selectRow(firstTrack)
            repeat with i from 1 to n
                keystroke "x" using command down -- cut track
                delay 0.1
                tell front window to if subrole is "AXDialog" then click last button -- close dialog
                delay 0.1
            end repeat
        end tell
    end tell
end moveSelectedTracks

on selectRow(i)
    tell application "iTunes" to reveal i
end selectRow

on checkSortCol() -- ****** "status" is the localized title, you need to change it according to your system language ******
    tell application "System Events"
        tell process "iTunes"
            if (exists radio group 2 of window 1) then return {true, true, true} -- grid view without outline
            set s to value of attribute "AXSortDirection" of (button "status" of group 1 of outline 1 of (last scroll area of window 1 whose group 1 of outline 1 is not missing value))

            set x to value of attribute "AXMenuItemMarkChar" of menu item 3 of menu of menu bar item 5 of menu bar 1 -- menu "as Grid"          
            set y to not (enabled of menu item 4 of menu of menu bar item 4 of menu bar 1) -- menu "Cut"
        end tell
    end tell
    return {s is not "AXUnknownSortDirection", x is not {"�"}, y}
end checkSortCol

on displayAlert(t)
    activate
    display alert t
end displayAlert

Вы должны снять флажок « Обновление в реальном времени » в Smart Playlist, иначе скрипт не будет работать, поскольку пункт меню « Cut » отключен. Вы можете перепроверить его после того, как закончите.

1 голос
/ 28 ноября 2012

Из-за вашего комментария к сообщению Филиппа Ригана я посмотрел на команду перемещения в iTunes, и она говорит, что она предназначена для перемещения плейлистов, поэтому она вам не поможет.Однако вы можете сделать это, что в основном будет воссоздавать порядок воспроизведения, как вы хотите в текущем списке воспроизведения.

Я предполагаю, что выбор является последовательным.Если это не так, этот код нужно будет скорректировать, но вы можете использовать эти идеи для достижения этой цели.

ПРИМЕЧАНИЕ: вам нужно ввести код, чтобы выполнить сортировку выбора, потому что я не могу точно сказать, какВы хотите отсортировать выбор из вашего описания.Поместите этот код в область, которую я пометил "ЗДЕСЬ Сортировка".Остальная часть кода должна работать как есть.

Надеюсь, это поможет ... удачи.

-- this is only tested on music tracks, not apps or books etc.
-- do not use this code on your main music library

-- this will sort the selected tracks
-- insert sorting code here: DO YOUR SORTING HERE
-- now the sorting code just reverses the order of the selection

-- it works by getting references of the songs in your playlist from the main library, sorting that,
-- then deleting the tracks from the current playlist and recreating the playlist from the references

tell application "iTunes"
    -- get the selection
    set selectedTracks to the selection
    set selectionCount to count of selectedTracks
    if selectionCount is 0 then
        error "Error: Nothing is selected!"
    else if selectionCount is 1 then
        error "Error: There is nothing to sort because only 1 item is selected!"
    end if

    -- store the player state
    try
        set currentPlaylist to current playlist
    on error
        play
        pause
        set currentPlaylist to current playlist
    end try
    try
        set currentTrack to current track
        set currentTrackID to persistent ID of currentTrack
        set currentTime to player position
        set currentState to player state
    on error
        set currentState to false
    end try

    -- are we in the right type of playlist?
    if (special kind of currentPlaylist is not none) or (smart of currentPlaylist) then
        error ("Error: we cannot use this code on playlist " & name of currentPlaylist & "!")
    end if

    -- is the selection in the current playlist?
    set firstSelectedTrack to item 1 of selectedTracks
    set firstSelectedTrackID to persistent ID of firstSelectedTrack
    try
        first track of currentPlaylist whose persistent ID is firstSelectedTrackID
    on error
        error "Error: the selected tracks are not in the current playlist, so this code won't work!"
    end try

    -- are we dealing with music tracks?
    if class of firstSelectedTrack is not file track then error "Error: this code is only tested on music file tracks!"

    (****** DO YOUR SORTING HERE *********)
    -- sort your selected tracks in a list however you need
    -- in this case I'm just reversing the order for simplicity
    set sortedSelection to reverse of selectedTracks
    (*************************************)

    -- figure out the index of the first selected track out of the entire playlist
    set playlistTracks to tracks of currentPlaylist
    repeat with i from 1 to count of playlistTracks
        if (item i of playlistTracks) is firstSelectedTrack then exit repeat
    end repeat

    -- now we make one big list of the sorted playlist
    set tracksBeforeSelection to {}
    set tracksAfterSelection to {}
    try
        set tracksBeforeSelection to items 1 thru (i - 1) of playlistTracks
    end try
    try
        set tracksAfterSelection to items (i + selectionCount) thru end of playlistTracks
    end try
    set finalTrackList to tracksBeforeSelection & sortedSelection & tracksAfterSelection

    -- now we get references to all these tracks from the main library so we can delete the playlist and reorganize it
    set finalTrackListRefs to {}
    repeat with aTrack in finalTrackList
        set trackID to persistent ID of aTrack
        set refTrack to (first track of library playlist 1 whose persistent ID is trackID)
        set end of finalTrackListRefs to refTrack
    end repeat

    -- remove all the tracks from the playlist
    delete tracks of currentPlaylist

    -- put the playlist back in the new order
    repeat with aTrack in finalTrackListRefs
        duplicate aTrack to currentPlaylist
    end repeat

    -- restore the player state
    if currentState is playing then
        play (first track of currentPlaylist whose persistent ID is currentTrackID)
        set player position to currentTime
    end if
end tell
1 голос
/ 02 декабря 2012

Вот решение, но оно косвенное, потому что оно импортирует файл XML.

Сценарий создает файл XML, например экспортирует список воспроизведения из iTunes . Когда сценарий завершает создание файла XML, он импортирует файл в iTunes, iTunes создает другой Smart Playlist с тем же именем, сценарий переключается на новый список воспроизведения и удаляет оригинал. Работает также на несмежном отборе.

set b to false
tell application "iTunes"
    set selTracks to selection
    if (count selTracks) < 2 then return my displayAlert("Select 2 or more tracks")
    set selPlaylist to container of item 1 of selTracks
    try
        tell selPlaylist to set b to special kind is none and smart is true
    end try
    if not b then return my displayAlert("Not a smart playlist")
    set {oldFindexing, fixed indexing} to {fixed indexing, false}
    set firstIndex to index of item 1 of selTracks
    set fixed indexing to oldFindexing

    --**** do something with these selections  **********
    set sortedTracks to reverse of selTracks -- ***** This example reverses the order. ********
end tell
my moveSelectedTracks(sortedTracks, selPlaylist, firstIndex)

on moveSelectedTracks(selT, selP, n)
    script o
        property tDataIDs : {}
        property sTracks : selT
        property tArgs2 : {}
    end script
    set L to {}
    set tc to count o's sTracks
    tell application "iTunes"
        set o's tDataIDs to database ID of tracks of selP -- get id of the tracks in the playlist
        set theID to persistent ID of selP
        repeat with i from 1 to tc -- get id of the each sorted track
            set item i of o's sTracks to "<key>" & (get database ID of (item i of o's sTracks)) & "<"
        end repeat
    end tell

    set tc to count o's tDataIDs

    --- make arguments
    repeat with i from 1 to tc
        if i = n then set o's tArgs2 to o's tArgs2 & o's sTracks
        set t to "<key>" & item i of o's tDataIDs & "<"
        if t is not in o's sTracks then set end of o's tArgs2 to t
    end repeat

    set {oTID, text item delimiters} to {text item delimiters, linefeed}
    set o's tArgs2 to o's tArgs2 as text --convert a list to text (one argument per line)
    set text item delimiters to oTID

    set xmlLib to my get_iTunes_Library_xml() -- get path of "iTunes Library.xml"
    set tFile to (path to temporary items as string) & "__xzaTemp_Playlist321__"
    set tempF to quoted form of POSIX path of tFile

    try --- write arguments to a temporary file
        set openfile to open for access file (tFile & ".txt") with write permission
        set eof of openfile to 0
        write (o's tArgs2) to openfile starting at eof
        close access openfile
    on error err
        try
            close access file tFile
        end try
        return my displayAlert("Error when writing to a temporary file.\\n" & err)
    end try

    -- ** create the XML file, grep write the beginning of the xml File
    do shell script "/usr/bin/grep -m1 -B40 '   <dict>' " & xmlLib & " > " & (tempF & ".xml")

    (* append to the xmlFile:
    grep read each argument and search track info in "iTunes Library.xml", perl clean up the output 
    grep search the info of the smart playlist and write it
    sed change all arguments to array of dicts (this will be the order of each track in the playlist)  
    echo write the end of the xml File.
    *)
    do shell script "(tmp=" & tempF & ".txt; /usr/bin/grep -A62 -F -f \"$tmp\" " & xmlLib & " |/usr/bin/perl -pe 'undef $/; s|</dict> ((?:(?!</dict>).)*)\\n--|</dict>|sgx; s:</dict>(?!.*</dict>).*|</dict>\\s*</dict>\\s*<key>Playlists</key>.*:</dict>:sx;'; echo '</dict>\\n<key>Playlists</key><array>' ; /usr/bin/grep -m1 -A42 -B3 '>Playlist Persistent ID</key><string>" & theID & "<' " & xmlLib & " | /usr/bin/grep -m1 -B40 '<array>'; /usr/bin/sed 's:$:/integer></dict>:; s:^<key>:<dict><key>Track ID</key><integer>:' \"$tmp\" ; echo '</array></dict></array></dict></plist>') >> " & (tempF & ".xml")

    set tFolder to ""
    set b to false
    tell application "iTunes"
        set {tName, songRepeat} to {name, song repeat} of selP
        add ((tFile & ".xml") as alias) -- import the XML file as Smart Playlist
        try
            set tFolder to parent of selP -- if the smart playlist is in a folder playlist
        end try
        set selP2 to last user playlist whose name is tName and its smart is true -- get the new smart playlist

        if (persistent ID of selP2) is not theID then -- else no importation 
            if tFolder is not "" then move selP2 to tFolder -- move to the folder playlist
            reveal (track n of selP2) -- select the same row in the imported playlist
            try
                tell current track to set {dataID, b} to {database ID, its container = selP}
            end try
            if b then -- the current track is in the smart playlist
                set {tState, tPos} to {player state, player position}
                play (first track of selP2 whose database ID = dataID) -- play the same track
                set player position to (tPos + 0.4) --  same position
                if tState = paused then
                    pause
                else if tState is stopped then
                    stop
                end if
                set song repeat of selP2 to songRepeat -- this line doesn't work on iTunes 11
            end if
            delete selP -- delete the smart playlist (the original)
        end if
    end tell
    do shell script "/bin/rm -f " & tempF & "{.txt,.xml} > /dev/null 2>&1 &" -- delete the temp files
end moveSelectedTracks

on get_iTunes_Library_xml()
    do shell script "/usr/bin/defaults read com.apple.iApps iTunesRecentDatabases |/usr/bin/sed -En 's:^ *\"(.*)\"$:\\1:p' |/usr/bin/perl -MURI -e 'print URI->new(<>)->file;'"
    return quoted form of the result
end get_iTunes_Library_xml

on displayAlert(t)
    activate
    display alert t
end displayAlert

Этот скрипт запускается (Mac OS X 10.4 ... 10.7), (iTunes 7.5 ... 10.6.3).

Скрипт не работает на старых версиях, я не знаю о новых версиях.


Я знаю:

Mountain Lion использует FreeBSD's grep вместо GNU's grep, grep во FreeBSD чрезвычайно медлен на Mountain Lion (в 30-100 раз медленнее, согласно тому, что я прочитал), поэтому этот скрипт также будет медленным

Tunes 11 разбивает команду AppleScript на song repeat список воспроизведения. Значение song repeat все еще можно прочитать с помощью get, его просто нельзя установить, поэтому комментируйте строку в скрипте.

0 голосов
/ 30 октября 2010

Вы не можете сделать это напрямую, потому что Словарь показывает свойство index дорожки в списке воспроизведения только для получения, а не для получения / установки.

Единственный способ, которым я вижу эточтобы создать новый список воспроизведения, переместите дорожки в новую в том порядке, в котором вы хотите, чтобы они были, а затем удалите старую.

...