diff --git a/File Metadata.go b/File Metadata.go index bf3bae9..3e080a5 100644 --- a/File Metadata.go +++ b/File Metadata.go @@ -14,13 +14,15 @@ import ( "time" ) -// List of defined file tags. +// List of defined file tags. Virtual tags are generated at runtime and are read-only. They cannot be stored on the blockchain. const ( - TagName = 0 // Name of file. - TagFolder = 1 // Folder name. - TagDescription = 2 // Arbitrary description of the file. May contain hashtags. - TagDateShared = 3 // When the file was published on the blockchain. Cannot be set manually (virtual read-only tag). - TagDateCreated = 4 // Date when the file was originally created. This may differ from the date in the block record, which indicates when the file was shared. + TagName = 0 // Name of file. + TagFolder = 1 // Folder name. + TagDescription = 2 // Arbitrary description of the file. May contain hashtags. + TagDateShared = 3 // When the file was published on the blockchain. Virtual. + TagDateCreated = 4 // Date when the file was originally created. This may differ from the date in the block record, which indicates when the file was shared. + TagSharedByCount = 5 // Count of peers that share the file. Virtual. + TagSharedByGeoIP = 6 // GeoIP data of peers that are sharing the file. CSV encoded with header "latitude,longitude". Virtual. ) // Future tags to be defined for audio/video: Artist, Album, Title, Length, Bitrate, Codec @@ -43,9 +45,18 @@ func (tag *BlockRecordFileTag) Text() string { return string(tag.Data) } +// Number returns the tags data as uint64. It returns 0 if the data cannot be decoded. +func (tag *BlockRecordFileTag) Number() uint64 { + if len(tag.Data) != 8 { + return 0 + } + + return binary.LittleEndian.Uint64(tag.Data[0:8]) +} + // IsVirtual checks if the tag is virtual. func (tag *BlockRecordFileTag) IsVirtual() bool { - return tag.Type == TagDateShared + return IsTagVirtual(tag.Type) } // TagFromDate returns a tag from date @@ -60,3 +71,21 @@ func TagFromDate(Type uint16, Date time.Time) BlockRecordFileTag { func TagFromText(Type uint16, Text string) BlockRecordFileTag { return BlockRecordFileTag{Type: Type, Data: []byte(Text)} } + +// TagFromNumber returns a tag from a number +func TagFromNumber(Type uint16, Number uint64) BlockRecordFileTag { + var tempDate [8]byte + binary.LittleEndian.PutUint64(tempDate[0:8], Number) + + return BlockRecordFileTag{Type: Type, Data: tempDate[:]} +} + +// IsTagVirtual checks if the tag is a virtual one. +func IsTagVirtual(Type uint16) bool { + switch Type { + case TagDateShared, TagSharedByCount, TagSharedByGeoIP: + return true + default: + return false + } +} diff --git a/webapi/Blockchain.go b/webapi/Blockchain.go index 09dc73e..819b3f3 100644 --- a/webapi/Blockchain.go +++ b/webapi/Blockchain.go @@ -91,9 +91,10 @@ type apiFileMetadata struct { Type uint16 `json:"type"` // See core.TagX constants. Name string `json:"name"` // User friendly name of the metadata type. Use the Type fields to identify the metadata as this name may change. // Depending on the exact type, one of the below fields is used for proper encoding: - Text string `json:"text"` // Text value. UTF-8 encoding. - Blob []byte `json:"blob"` // Binary data - Date time.Time `json:"date"` // Date + Text string `json:"text"` // Text value. UTF-8 encoding. + Blob []byte `json:"blob"` // Binary data + Date time.Time `json:"date"` // Date + Number uint64 `json:"number"` // Number } // apiBlockRecordFile is the metadata of a file published on the blockchain @@ -249,6 +250,12 @@ func blockRecordFileToAPI(input core.BlockRecordFile) (output apiBlockRecordFile date, _ := tag.Date() output.Metadata = append(output.Metadata, apiFileMetadata{Type: tag.Type, Name: "Date Created", Date: date}) + case core.TagSharedByCount: + output.Metadata = append(output.Metadata, apiFileMetadata{Type: tag.Type, Name: "Shared By Count", Number: tag.Number()}) + + case core.TagSharedByGeoIP: + output.Metadata = append(output.Metadata, apiFileMetadata{Type: tag.Type, Name: "Shared By GeoIP", Text: tag.Text()}) + default: output.Metadata = append(output.Metadata, apiFileMetadata{Type: tag.Type, Blob: tag.Data}) } @@ -271,8 +278,12 @@ func blockRecordFileFromAPI(input apiBlockRecordFile) (output core.BlockRecordFi } for _, meta := range input.Metadata { + if core.IsTagVirtual(meta.Type) { // Virtual tags are not mapped back. They are read-only. + continue + } + switch meta.Type { - case core.TagName, core.TagFolder, core.TagDescription, core.TagDateShared: + case core.TagName, core.TagFolder, core.TagDescription: // auto mapped tags case core.TagDateCreated: output.Tags = append(output.Tags, core.TagFromDate(meta.Type, meta.Date)) diff --git a/webapi/Search Temp.go b/webapi/Search Temp.go index cd2ec8e..6e7e3e7 100644 --- a/webapi/Search Temp.go +++ b/webapi/Search Temp.go @@ -10,6 +10,7 @@ package webapi import ( "encoding/hex" + "fmt" "math/rand" "sync" "time" @@ -284,20 +285,48 @@ func createTestResult(fileType int) (file core.BlockRecordFile) { extension = ".bin" } - file.Tags = append(file.Tags, core.TagFromText(core.TagName, TempFileName("", extension))) + file.Tags = append(file.Tags, core.TagFromText(core.TagName, tempFileName("", extension))) //file.Tags = append(file.Tags, core.TagFromText(core.TagFolder, "not set")) file.Tags = append(file.Tags, core.TagFromDate(core.TagDateShared, time.Now().UTC())) + sharedByCount := uint64(rand.Intn(10)) + file.Tags = append(file.Tags, core.TagFromNumber(core.TagSharedByCount, sharedByCount)) + + if sharedByCount > 0 { + var sharedByGeoIP string + for n := uint64(0); n < sharedByCount; n++ { + latitude, longitude := randomGeoIP() + if n > 0 { + sharedByGeoIP += "\n" + } + + sharedByGeoIP += fmt.Sprintf("%.4f", latitude) + "," + fmt.Sprintf("%.4f", longitude) + } + + file.Tags = append(file.Tags, core.TagFromText(core.TagSharedByGeoIP, sharedByGeoIP)) + } + return } -// TempFileName generates a temporary filename for use in testing or whatever -func TempFileName(prefix, suffix string) string { +// tempFileName generates a temporary filename for use in testing +func tempFileName(prefix, suffix string) string { randBytes := make([]byte, 16) rand.Read(randBytes) return prefix + hex.EncodeToString(randBytes) + "." + suffix } +// randomGeoIP generates random geo IP coordinates +func randomGeoIP() (latitude, longitude float32) { + // Latitude generates latitude (from -90.0 to 90.0) + latitude = rand.Float32()*180 - 90 + + // Longitude generates longitude (from -180 to 180) + longitude = rand.Float32()*360 - 180 + + return +} + // queryRecentShared returns recently shared files on the network. fileType = -1 for any. func queryRecentShared(fileType, limit int) (files []*core.BlockRecordFile) { for n := 0; n < limit; n++ { diff --git a/webapi/readme.md b/webapi/readme.md index 4f345df..de530e9 100644 --- a/webapi/readme.md +++ b/webapi/readme.md @@ -202,21 +202,24 @@ type apiFileMetadata struct { Type uint16 `json:"type"` // See core.TagX constants. Name string `json:"name"` // User friendly name of the metadata type. Use the Type fields to identify the metadata as this name may change. // Depending on the exact type, one of the below fields is used for proper encoding: - Text string `json:"text"` // Text value. UTF-8 encoding. - Blob []byte `json:"blob"` // Binary data - Date time.Time `json:"date"` // Date + Text string `json:"text"` // Text value. UTF-8 encoding. + Blob []byte `json:"blob"` // Binary data + Date time.Time `json:"date"` // Date + Number uint64 `json:"number"` // Number } ``` -Below is the list of defined metadata types. Undefined types may be used by clients, but are always mapped into the `blob` field. +Below is the list of defined metadata types. Undefined types may be used by clients, but are always mapped into the `blob` field. Virtual tags are generated at runtime and are read-only. They cannot be stored on the blockchain. -| Type | Constant | Encoding | Info | -|------|----------------|----------|------------------------------------------------------------------------------------------------------------------------| -| 0 | TagName | Text | Mapped into Name field. Name of file. | -| 1 | TagFolder | Text | Mapped into Folder field. Folder name. | -| 2 | TagDescription | Text | Mapped into Description field. Arbitrary description of the file. May contain hashtags. | -| 3 | TagDateShared | Date | Mapped into Date field. When the file was published on the blockchain. Cannot be set manually (virtual read-only tag). | -| 4 | TagDateCreated | Date | Date when the file was originally created. | +| Type | Constant | Encoding | Virtual | Info | +|------|----------------|----------|---------|------------------------------------------------------------------------------------------------------------------------| +| 0 | TagName | Text | | Mapped into Name field. Name of file. | +| 1 | TagFolder | Text | | Mapped into Folder field. Folder name. | +| 2 | TagDescription | Text | | Mapped into Description field. Arbitrary description of the file. May contain hashtags. | +| 3 | TagDateShared | Date | x | Mapped into Date field. When the file was published on the blockchain. | +| 4 | TagDateCreated | Date | | Date when the file was originally created. | +| 5 | TagSharedByCount | Number | x | Count of peers that share the file. | +| 6 | TagSharedByGeoIP | Text/CSV | x | GeoIP data of peers that are sharing the file. CSV encoded with header "latitude,longitude". | ### Add File @@ -570,7 +573,21 @@ Example response with dummy data: "description": "", "date": "2021-09-23T00:00:00Z", "nodeid": "j4yHzmCXiXqg4DPhowj0DIOuuyJxQflo2QSNG3yhCK8=", - "metadata": [] + "metadata": [{ + "type": 5, + "name": "Shared By Count", + "text": "", + "blob": null, + "date": "0001-01-01T00:00:00Z", + "number": 7 + }, { + "type": 6, + "name": "Shared By GeoIP", + "text": "25.7766,-178.1275\n-46.4041,8.0066\n84.4478,8.2417\n14.1721,-9.7539\n-67.2364,127.6007\n-75.1604,106.7583\n70.5132,-133.4146", + "blob": null, + "date": "0001-01-01T00:00:00Z", + "number": 0 + }] }] } ```