如何使用AppleScript命令将播放列表中的歌曲移动到列表中的其他位置?
我已经有了这首歌和播放列表,而其中一首已经在另一首;我只需改变立场。
我的目标是对用户选择的曲目进行排序;理想情况下,我想将逐个首选的轨道移动到第一个选择轨道的位置,并在第一个按顺序之后立即按顺序排序所有其他选定的轨道。
答案 0 :(得分:8)
move
命令看起来确实非常错误。在我的实验中,似乎无论您将轨道移动到哪个位置,它都会将其移动到播放列表的末尾。如果效率有点低,这应该仍然可以管理。其语法如下:
tell application "iTunes"
move first track of playlist "foo" to end of playlist "foo"
end tell
你应该能够使用其他几种结构而不是“结束”,包括“播放列表开头”foo“',播放列表”foo“'的轨道5之后,以及之前轨道5的播放列表“foo”',但这些都没有按预期工作。但是,如果您基本上按照您想要的方式将列表放入列表中,您应该能够迭代列表,告诉iTunes将每个轨道连续移动到播放列表的末尾,并且您最终会得到完成后排序顺序。
答案 1 :(得分:1)
由于您对Philip Regan的帖子发表评论,我在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
答案 2 :(得分:1)
这是一个解决方案,但它是间接的,因为它导入了一个XML文件。
该脚本会创建一个XML文件,例如从 iTunes 导出播放列表。 当脚本完成创建XML文件后,它会在iTunes中导入文件,iTunes会创建另一个具有相同名称的智能播放列表,脚本会切换到新的播放列表并删除原始文件。 它也适用于非连续选择。
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
,FreeBSD的grep在 Mountain Lion 上速度极慢(根据我的阅读速度慢30到100倍),所以这个脚本也会很慢。
Tunes 11 将AppleScript命令分解为song repeat
播放列表。 song repeat
的值仍可以使用get读取,只是无法设置,因此请在脚本中注释该行。
答案 3 :(得分:1)
这是一个使用GUI Scripting
。
但根据 iTunes 的版本或操作系统的版本,此类脚本很容易被破坏。
用户必须调整delay
(取决于机器的速度)。
用户还必须更改脚本中的本地化标题。
此脚本适用于iTunes 10.6.3
和Mac OS X 10.5.8
,我不知道它是否适用于其他版本。
您必须在Universal Access System首选项窗格中点击标有“启用辅助设备访问权限”的复选框启用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
您必须取消选中智能播放列表中的“实时更新”,否则脚本将无效,因为已禁用“剪切”菜单项。 完成后你可以重新检查它。
答案 4 :(得分:0)
你不能直接这样做,因为词典显示播放列表中曲目的index
属性是get-only,而不是get / set。
我看到的唯一方法是创建一个新的播放列表,按照您希望的顺序将曲目移动到新的播放列表中,然后删除旧曲目。