Open zuinqstudio opened 4 months ago
Answering my own question, just in case might be useful for someone. I found this.-
https://m.np.playstation.com/api/trophy/v1/users/{userId}|me/titles/trophyTitles?npTitleIds={ids}
Which returns objects that include npTitleId
and npCommunicationId
. This npCommunicationId
can later be used in trophyGroups
methods.
Answering my own question, just in case might be useful for someone. I found this.-
https://m.np.playstation.com/api/trophy/v1/users/{userId}|me/titles/trophyTitles?npTitleIds={ids}
Which returns objects that include
npTitleId
andnpCommunicationId
. ThisnpCommunicationId
can later be used introphyGroups
methods.
what is the best way of getting the titleids for this, i cant seem to find a good way
Hey, if you're looking for something like this, hit me up. games.directory does a lot of mapping between titles and games. Let me know your use-case and I can provide an API endpoint.
Hey @pacMakaveli, I tried to sign up on the games directory, but it seems to ben't working atm. Do you have any tips on how to get the titleId from npCommunicationId?
I am currently struggling to get media and playDuration for PS3 games.
@pradella sorry about that. If you want to, please let me know what didn't work so I can fix it :D
As for the npCommunicationId
to titleId
, it really depends on the use case and what data you've got available. This is a ruby script that I use:
require 'json'
class String
def levenshtein_distance(other)
m, n = self.length, other.length
return m if n == 0
return n if m == 0
d = Array.new(m+1) {Array.new(n+1)}
(0..m).each {|i| d[i][0] = i}
(0..n).each {|j| d[0][j] = j}
(1..n).each do |j|
(1..m).each do |i|
d[i][j] = if self[i-1] == other[j-1]
d[i-1][j-1]
else
[d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1]+1].min
end
end
end
d[m][n]
end
end
def parse_list(content)
content.split("\n").map do |line|
id, name = line.split(': ', 2)
[id.strip, name.strip]
end
end
def normalize_name(name)
name.downcase.gsub(/[^a-z0-9]+/, '')
end
def calculate_accuracy(distance, max_length)
1 - (distance.to_f / max_length)
end
def find_best_match(entry, list)
normalized_entry = normalize_name(entry[1])
best_match = list.max_by do |item|
normalized_item = normalize_name(item[1])
accuracy = calculate_accuracy(
normalized_entry.levenshtein_distance(normalized_item),
[normalized_entry.length, normalized_item.length].max
)
accuracy
end
accuracy = calculate_accuracy(
normalized_entry.levenshtein_distance(normalize_name(best_match[1])),
[normalized_entry.length, normalize_name(best_match[1]).length].max
)
[best_match, accuracy]
end
def check(entry, list)
best_match, accuracy = find_best_match(entry, list)
{
entry: entry[0],
match: best_match[0],
accuracy: accuracy,
labels: [entry[1], best_match[1]]
}
end
def cross_reference(list1, list2)
result = {}
list1.each do |entry|
match = check(entry, list2)
# You may want to adjust this threshold
if match[:accuracy] >= 0.7
key = normalize_name(entry[1])
result[key] ||= []
result[key] << match
end
end
result.map { |k, v| { k => v } }
end
# Function to read file content
def read_file(filename)
File.read(filename)
rescue Errno::ENOENT
puts "File #{filename} not found."
exit
end
# Main execution
if ARGV.length == 2
# Cross-reference two lists
games_content = read_file(ARGV[0])
titles_content = read_file(ARGV[1])
games = parse_list(games_content)
titles = parse_list(titles_content)
result = cross_reference(games, titles)
elsif ARGV.length == 3 && ARGV[0] == 'check'
# Check a single entry against a list
entry_content = read_file(ARGV[1])
list_content = read_file(ARGV[2])
entry = parse_list(entry_content).first
list = parse_list(list_content)
result = check(entry, list)
else
puts "Usage: ruby script.rb [games_file] [titles_file]"
puts " or: ruby script.rb check [entry_file] [list_file]"
exit
end
# Output the result as JSON
puts JSON.pretty_generate(result)
And then these are the files I would give it.
games =>
PPSA01923_00: Fortnite
CUSA01433_00: Rocket League®
CUSA15495_00: Surviving the Aftermath
PPSA01463_00: Borderlands 3
CUSA08025_00: Borderlands 3
titles =>
NPWR21035_00: Borderlands® 3
NPWR28289_00: XDefiant
NPWR28312_00: Crime Boss: Rockay City
NPWR29727_00: The Outer Worlds: Spacer's Choice Edition
The gist of it is that it tried to match them by name. If no match is found, it will use the levenshtein
algorithm to find the closes match, otherwise it will return nothing.
playDuration
is not available for PS3 games. This data was exposed when PS5 was released and AFAIK, there was no tracking of this kind for PS3 titles.
media
, what sort of media are you looking for?
https://splash.games.directory/ https://splash.games.directory/i?q%5Bslug_cont_any%5D=fortnite
has probably more than what you need. It doesn't currently allow you to retrieve via some sort of API, but it's something I can add quickly.
If you want to do it yourself though, you will need to query the concept
API. I don't think this repo has any documentation (not really looked into it tbh).
These are the endpoints I personally use in my projects. They use the same authentication method as the app.
https://m.np.playstation.net/api/catalog/v2/concepts/fetch?country=GB&language=en&age=999&limit=1000&offset=1 https://m.np.playstation.net/api/catalog/v2/concepts/10000470 https://m.np.playstation.net/api/catalog/v2/titles/CUSA18182_00/?country=GB&language=en&age=999
Wow, thanks for sharing those additional endpoints @pacMakaveli !
You're right; PS3 playDuration does not exist (I completely forgot it).
Indeed I could find npCommunicationId
from titleId
; but not the opposite (titleId
from npCommunicationId
).
For example: trophy/v1/users/${accountId}/trophyTitles
brings all games (including PS3, but only npCommunicationId
, no titleId
. And to fetch media Im using this one gamelist/v2/users/${accountId}/titles/${titleId}
I'm guessing you have previously mapped all those npCommunicationId
with titleId
based on the history scan your app made?
@pradella correct. My database spans 9 years of constantly syncing the API and more importantly, users. One easy way to map these is to scan someone that you know has completed/owns lots of games. Hakoom, Ikemenzi etc.. (https://psnprofiles.com/dav1d_123) you can get their account ids from the search
endpoint.
Again, if there's a way I can provide this data to you via games.directory's API, please let me know. I'd be more than happy as one of my goals with this is to provide little snippets of APIs for these kind of use cases. (obviously free, just in case it's not clear)
@pacMakaveli Hi! Sorry to quickly hijack this thread, I would love to get in contact with you about possibly using your api for a pet project of mine and my gamer groups. I think I tried a while ago to send a message on the webpage but it never went through. What's the best wway to get in touch?
vlad [at] games [dot] directory
@pradella correct. My database spans 9 years of constantly syncing the API and more importantly, users. One easy way to map these is to scan someone that you know has completed/owns lots of games. Hakoom, Ikemenzi etc.. (https://psnprofiles.com/dav1d_123) you can get their account ids from the
search
endpoint.Again, if there's a way I can provide this data to you via games.directory's API, please let me know. I'd be more than happy as one of my goals with this is to provide little snippets of APIs for these kind of use cases. (obviously free, just in case it's not clear)
Such an inspiration you are, thanks bro @pacMakaveli
Also, feel free to join https://discord.gg/k4QRsfmA . It'll be easier to communicate and plan ahead.
I'm trying to gather some basic player statistics from
gamelist/v2/users/$accountId/titles
andtrophy/v1/users/$accountId/trophyTitles
. Unfortunately,conceptId
appears only in the first request, and the best field to find a game by its name from the second onetrophyTitleName
, which slightly differs from the original game title in lots of cases.Is there any known api to establish this relationship?