itunes-controller 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.buildpath +5 -0
- data/.gitignore +3 -0
- data/.project +17 -0
- data/.rvmrc +81 -0
- data/LICENSE +674 -0
- data/README +27 -0
- data/Rakefile +27 -0
- data/TODO +8 -0
- data/bin/addFiles.rb +35 -0
- data/bin/dummyiTunesController.rb +109 -0
- data/bin/itunesController.rb +78 -0
- data/bin/listDeadTracks.rb +45 -0
- data/bin/listNewTracks.rb +88 -0
- data/bin/recreateTrackCache.rb +31 -0
- data/bin/refreshFiles.rb +42 -0
- data/bin/removeDeadTracks.rb +31 -0
- data/bin/removeFiles.rb +42 -0
- data/bin/trackInfo.rb +50 -0
- data/itunes-controller.gemspec +24 -0
- data/jar-stuff.sh +24 -0
- data/lib/itunesController/application.rb +127 -0
- data/lib/itunesController/cachedcontroller.rb +188 -0
- data/lib/itunesController/config.rb +95 -0
- data/lib/itunesController/controller_creator.rb +29 -0
- data/lib/itunesController/controllserver.rb +583 -0
- data/lib/itunesController/database/backend.rb +41 -0
- data/lib/itunesController/database/database.rb +166 -0
- data/lib/itunesController/database/sqlite3_backend.rb +67 -0
- data/lib/itunesController/debug.rb +124 -0
- data/lib/itunesController/dummy_itunes_track.rb +40 -0
- data/lib/itunesController/dummy_itunescontroller.rb +142 -0
- data/lib/itunesController/itunescontroller.rb +90 -0
- data/lib/itunesController/itunescontroller_factory.rb +51 -0
- data/lib/itunesController/kinds.rb +138 -0
- data/lib/itunesController/logging.rb +119 -0
- data/lib/itunesController/macosx_itunescontroller.rb +204 -0
- data/lib/itunesController/platform.rb +53 -0
- data/lib/itunesController/sqlite_creator.rb +35 -0
- data/lib/itunesController/track.rb +39 -0
- data/lib/itunesController/version.rb +25 -0
- data/lib/itunesController/windows_itunescontroller.rb +145 -0
- data/test/base_server_test_case.rb +125 -0
- data/test/dummy_client.rb +44 -0
- data/test/test_server.rb +312 -0
- metadata +185 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
module ItunesController
|
23
|
+
|
24
|
+
class Platform
|
25
|
+
|
26
|
+
def self.getUserHomeDir()
|
27
|
+
homes = ["HOME", "HOMEPATH"]
|
28
|
+
realHome = homes.detect {|h| ENV[h] != nil}
|
29
|
+
if not realHome
|
30
|
+
raise "Could not find home directory"
|
31
|
+
end
|
32
|
+
|
33
|
+
return ENV[realHome]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.isWindows()
|
37
|
+
return RUBY_PLATFORM =~ /mswin|mingw/
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.isMacOSX()
|
41
|
+
return RUBY_PLATFORM =~ /darwin/
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.isLinux()
|
45
|
+
return RUBY_PLATFORM =~ /linux/
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.isJRuby()
|
49
|
+
return RUBY_PLATFORM =~ /java/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
require 'itunesController/database/sqlite3_backend'
|
23
|
+
require 'itunesController/itunescontroller_factory'
|
24
|
+
require 'itunesController/controller_creator'
|
25
|
+
|
26
|
+
module ItunesController
|
27
|
+
|
28
|
+
class SQLLiteControllerCreator < ControllerCreator
|
29
|
+
def createController()
|
30
|
+
dbBackend = ItunesController::SQLite3DatabaseBackend.new(nil)
|
31
|
+
return ItunesController::CachedController.new(ItunesController::ITunesControllerFactory::createController(),dbBackend)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
module ItunesController
|
23
|
+
|
24
|
+
# Used to store track information
|
25
|
+
class Track
|
26
|
+
attr_accessor :location,:databaseId,:title
|
27
|
+
|
28
|
+
def initialize(location,databaseId,title)
|
29
|
+
@location = location
|
30
|
+
@databaseId = databaseId
|
31
|
+
@title = title
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
return "Location: '#{@location}' - Database ID: #{@databaseId} - Title: '#{@title}' "
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
module ItunesController
|
23
|
+
# The itunes-remote-control-server version
|
24
|
+
VERSION = "0.1.0"
|
25
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
require 'itunesController/itunescontroller'
|
23
|
+
require 'itunesController/kinds'
|
24
|
+
require 'itunesController/debug'
|
25
|
+
require 'itunesController/logging'
|
26
|
+
require 'win32ole'
|
27
|
+
|
28
|
+
module ItunesController
|
29
|
+
|
30
|
+
class WindowsITunesController < ItunesController::BaseITunesController
|
31
|
+
|
32
|
+
# The constructor
|
33
|
+
def initialize
|
34
|
+
@iTunes = WIN32OLE.new('iTunes.Application')
|
35
|
+
@libraryPlaylists=@iTunes.LibraryPlaylist
|
36
|
+
end
|
37
|
+
|
38
|
+
# Used to get the iTunes version
|
39
|
+
# @return [String] The itunes version
|
40
|
+
def version
|
41
|
+
return @iTunes.version.to_s+" (Windows)"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Used to tell iTunes to refresh a list of tracks data from the info stored in the files
|
45
|
+
# @param [Array] tracks A list of tracks to fresh
|
46
|
+
def refreshTracks(tracks)
|
47
|
+
tracks.reverse.each do | track |
|
48
|
+
track.UpdateInfoFromFile
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Used to remove tracks from the libaray
|
53
|
+
# @param [Array] tracks A list of tracks to remove from the itunes libaray
|
54
|
+
def removeTracksFromLibrary(tracks)
|
55
|
+
tracks.reverse.each do | track |
|
56
|
+
track.Delete
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Used to add a list of files to the itunes library
|
61
|
+
# @param [Array[String]] A list of files to add to the itunes library
|
62
|
+
# @return [Array[ItunesController::Track]] List of ids of the new tracks once they are in the database
|
63
|
+
def addFilesToLibrary(files)
|
64
|
+
tracks=[]
|
65
|
+
files.each do | file |
|
66
|
+
if (!File.exist?(file))
|
67
|
+
ItunesController::ItunesControllerLogging::error("Unable to find file #{file}")
|
68
|
+
else
|
69
|
+
itracks=@libraryPlaylists.AddFile(file)
|
70
|
+
if (itracks!=nil)
|
71
|
+
itracks=itracks.Tracks
|
72
|
+
for i in (1..itracks.Count())
|
73
|
+
itrack=itracks.Item(i)
|
74
|
+
track=ItunesController::Track.new(file,itrack.TrackDatabaseID,itrack.Name)
|
75
|
+
tracks.push(track)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
ItunesController::ItunesControllerLogging::error("Unable to add file '#{file}'")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
return tracks;
|
84
|
+
end
|
85
|
+
|
86
|
+
def getTracks(&b)
|
87
|
+
ItunesController::ItunesControllerLogging::debug("Retriving track information...")
|
88
|
+
playlist=@libraryPlaylists
|
89
|
+
fileTracks = playlist.Tracks
|
90
|
+
size = fileTracks.Count()
|
91
|
+
count = 1
|
92
|
+
for count in (1..size)
|
93
|
+
track=fileTracks.Item(count)
|
94
|
+
location=track.location
|
95
|
+
dead=false
|
96
|
+
if (location!=nil)
|
97
|
+
if (!File.exist?(location))
|
98
|
+
dead = true
|
99
|
+
end
|
100
|
+
else
|
101
|
+
dead = true
|
102
|
+
end
|
103
|
+
if (count % 1000 == 0)
|
104
|
+
ItunesController::ItunesControllerLogging::debug("Found tracks #{count} of #{size}")
|
105
|
+
end
|
106
|
+
if (track.name!=nil)
|
107
|
+
b.call(ItunesController::Track.new(location,track.TrackDatabaseID,track.name),count,size,dead)
|
108
|
+
end
|
109
|
+
count=count+1
|
110
|
+
end
|
111
|
+
ItunesController::ItunesControllerLogging::debug("Found tracks #{count-1} of #{size}")
|
112
|
+
return size
|
113
|
+
end
|
114
|
+
|
115
|
+
# Used to find the number of tracks in the library
|
116
|
+
# @return [Number] The number of tracks
|
117
|
+
def getTrackCount()
|
118
|
+
return @libraryPlaylists.Tracks.Count()
|
119
|
+
end
|
120
|
+
|
121
|
+
# Used to search the itunes library
|
122
|
+
# @param term The search term
|
123
|
+
# @return [Array] a list of iTunes track that match the search term
|
124
|
+
def searchLibrary(term)
|
125
|
+
tracks=[]
|
126
|
+
playlist=@libraryPlaylists
|
127
|
+
foundTracks = playlist.Search(term,1)
|
128
|
+
if (foundTracks!=nil)
|
129
|
+
foundTracks.each do | t |
|
130
|
+
tracks.push(t)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
return tracks
|
135
|
+
end
|
136
|
+
|
137
|
+
# Used to get the database of a itunes track
|
138
|
+
# @abstract Must be overridden
|
139
|
+
# @param track the track
|
140
|
+
# @return The database id
|
141
|
+
def getTrackDatabaseId(track)
|
142
|
+
return track.TrackDatabaseID()
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
|
2
|
+
require 'tempfile'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'socket'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
require 'itunesController/platform'
|
8
|
+
require 'itunesController/config'
|
9
|
+
require 'itunesController/dummy_itunescontroller'
|
10
|
+
require 'itunesController/controllserver'
|
11
|
+
require 'itunesController/cachedcontroller'
|
12
|
+
require 'itunesController/controller_creator'
|
13
|
+
|
14
|
+
require 'itunesController/database/sqlite3_backend'
|
15
|
+
|
16
|
+
class DummyControllerCreator < ItunesController::ControllerCreator
|
17
|
+
|
18
|
+
def initialize(controller)
|
19
|
+
@controller = controller
|
20
|
+
end
|
21
|
+
|
22
|
+
def createController()
|
23
|
+
return @controller
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class BaseServerTest < Test::Unit::TestCase
|
28
|
+
|
29
|
+
USER="test"
|
30
|
+
PASSWORD="testpass"
|
31
|
+
MIN_PORT_NUMBER = 5000
|
32
|
+
MAX_PORT_NUMBER = 9000
|
33
|
+
|
34
|
+
attr_accessor :server,:port
|
35
|
+
|
36
|
+
def portOpen?(ip, port, seconds=1)
|
37
|
+
Timeout::timeout(seconds) do
|
38
|
+
begin
|
39
|
+
TCPSocket.new(ip, port).close
|
40
|
+
true
|
41
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
rescue Timeout::Error
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def findAvaliablePort
|
50
|
+
port=BaseServerTest::MIN_PORT_NUMBER
|
51
|
+
while (port < BaseServerTest::MAX_PORT_NUMBER )
|
52
|
+
if !portOpen?("127.0.0.1",port)
|
53
|
+
return port
|
54
|
+
end
|
55
|
+
port+=1
|
56
|
+
end
|
57
|
+
return -1
|
58
|
+
end
|
59
|
+
|
60
|
+
def setupServer(tracks=nil)
|
61
|
+
ItunesController::DummyITunesController::resetCommandLog()
|
62
|
+
ItunesController::DummyITunesController::resetTracks()
|
63
|
+
if (tracks!=nil)
|
64
|
+
tracks.each do | track |
|
65
|
+
ItunesController::DummyITunesController::forceAddTrack(track)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
ItunesController::DummyITunesController::getCommandLog().take_while {
|
70
|
+
| el |
|
71
|
+
}
|
72
|
+
@dbFile = Tempfile.new('dummyDatabase.db')
|
73
|
+
itunes = ItunesController::DummyITunesController.new
|
74
|
+
dbBackend = ItunesController::SQLite3DatabaseBackend.new(@dbFile.path)
|
75
|
+
controller = ItunesController::CachedController.new(itunes,dbBackend)
|
76
|
+
config=ItunesController::ServerConfig.new
|
77
|
+
config.port = findAvaliablePort
|
78
|
+
@port = config.port
|
79
|
+
config.username = BaseServerTest::USER
|
80
|
+
config.password = BaseServerTest::PASSWORD
|
81
|
+
@server=ItunesController::ITunesControlServer.new(config,config.port,DummyControllerCreator.new(controller))
|
82
|
+
end
|
83
|
+
|
84
|
+
def teardownServer
|
85
|
+
@server.stop
|
86
|
+
while !@server.stopped?
|
87
|
+
end
|
88
|
+
@server.join
|
89
|
+
@dbFile.unlink
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_dummy
|
93
|
+
end
|
94
|
+
|
95
|
+
def assertCommandLog(expected)
|
96
|
+
sleep(2)
|
97
|
+
@server.waitForEmptyJobQueue()
|
98
|
+
actual=ItunesController::DummyITunesController::getCommandLog()
|
99
|
+
error=false
|
100
|
+
if (expected.size()!=actual.size())
|
101
|
+
puts("Worng number of command log entries")
|
102
|
+
error = true
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
for i in (0..expected.size())
|
107
|
+
if (expected[i]!=actual[i])
|
108
|
+
error=true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
if (error)
|
112
|
+
puts("Command log did not match expected.")
|
113
|
+
puts("Expected:")
|
114
|
+
expected.each do | line |
|
115
|
+
puts(" "+line)
|
116
|
+
end
|
117
|
+
puts("Actual:")
|
118
|
+
actual.each do | line |
|
119
|
+
puts(" "+line)
|
120
|
+
end
|
121
|
+
raise("Command log did not match expected.")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
require 'net/telnet'
|
3
|
+
|
4
|
+
require 'itunesController/controllserver'
|
5
|
+
|
6
|
+
class DummyClient
|
7
|
+
|
8
|
+
DEFAULT_TIMEOUT=40
|
9
|
+
|
10
|
+
def connect(hostname,port)
|
11
|
+
@client=Net::Telnet::new('Host' => hostname,
|
12
|
+
'Port' => port,
|
13
|
+
'Telnetmode' => false)
|
14
|
+
@client.waitfor('String'=>"001 hello")
|
15
|
+
sendCommand("HELO",220)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def disconnect
|
20
|
+
@client.close
|
21
|
+
end
|
22
|
+
|
23
|
+
def login(username,password)
|
24
|
+
sendCommand(ItunesController::CommandName::LOGIN+":"+username,222);
|
25
|
+
sendCommand(ItunesController::CommandName::PASSWORD+":"+password,223);
|
26
|
+
end
|
27
|
+
|
28
|
+
def sendCommand(cmd, expectedCode,timeout=DummyClient::DEFAULT_TIMEOUT)
|
29
|
+
@client.cmd(cmd) do | response |
|
30
|
+
if (response!=nil)
|
31
|
+
response.each_line do | line |
|
32
|
+
if ( line =~ /(\d+).*/)
|
33
|
+
code=$1
|
34
|
+
if (code.to_i==expectedCode)
|
35
|
+
return;
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
raise "Did not recive expected response from server"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|