more code
This commit is contained in:
parent
3bf56a1d1f
commit
9d422c3ddb
@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"baseURL": "http://dvr.linuxrocker.com",
|
"baseURL": "http://dvr.linuxrocker.com",
|
||||||
"plexPyContext": "/plexpy",
|
"plexPyContext": "/plexpy",
|
||||||
"plexPyAPIKey": "6b707d0439a2449a9bcaed8c6f042de0"
|
"plexPyAPIKey": "6b707d0439a2449a9bcaed8c6f042de0",
|
||||||
|
"plexToken": "K1WCALqRK5HVzSQ1J3bM",
|
||||||
|
"plexHost": "http://172.19.0.105",
|
||||||
|
"plexPort": 32400,
|
||||||
|
"telegramToken": "568753422:AAEpsvhrB4RQA4QGEFwutFmTFkE_zjjzHSQ"
|
||||||
}
|
}
|
1
pkg/communicator/communicator.go
Normal file
1
pkg/communicator/communicator.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package communicator
|
@ -12,6 +12,10 @@ type Config struct {
|
|||||||
BaseURL string `json:"baseURL"`
|
BaseURL string `json:"baseURL"`
|
||||||
PlexPyContext string `json:"plexPyContext"`
|
PlexPyContext string `json:"plexPyContext"`
|
||||||
PlexPyAPIKey string `json:"plexPyAPIKey"`
|
PlexPyAPIKey string `json:"plexPyAPIKey"`
|
||||||
|
PlexToken string `json:"plexToken"`
|
||||||
|
PlexHost string `json:"plexHost"`
|
||||||
|
PlexPort int `json:"plexPort"`
|
||||||
|
TelegramToken string `json:"telegramToken"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//GetConfig gets the configuration values for the api using the file in the supplied configPath.
|
//GetConfig gets the configuration values for the api using the file in the supplied configPath.
|
||||||
|
@ -2,11 +2,14 @@ package locator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
|
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/config"
|
||||||
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/model"
|
"git.linuxrocker.com/mattburchett/Housekeeper/pkg/model"
|
||||||
@ -45,7 +48,7 @@ func GetCount(config config.Config, sectionID int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTitles will gather a list of information from the media in the library, based on the previous count.
|
// GetTitles will gather a list of information from the media in the library, based on the previous count.
|
||||||
func GetTitles(config config.Config, sectionID int, days int) string {
|
func GetTitles(config config.Config, sectionID int, days int) {
|
||||||
count := GetCount(config, sectionID)
|
count := GetCount(config, sectionID)
|
||||||
|
|
||||||
titlesURL := fmt.Sprintf("%s%s%s%s%s%d%s%d", config.BaseURL, config.PlexPyContext, "/api/v2?apikey=", config.PlexPyAPIKey, "&cmd=get_library_media_info§ion_id=", sectionID, "&order_column=last_played&refresh=true&order_dir=asc&length=", count)
|
titlesURL := fmt.Sprintf("%s%s%s%s%s%d%s%d", config.BaseURL, config.PlexPyContext, "/api/v2?apikey=", config.PlexPyAPIKey, "&cmd=get_library_media_info§ion_id=", sectionID, "&order_column=last_played&refresh=true&order_dir=asc&length=", count)
|
||||||
@ -75,13 +78,19 @@ func GetTitles(config config.Config, sectionID int, days int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := titleModel.Response.Data.Data
|
data := titleModel.Response.Data.Data
|
||||||
// titles := make([]string, 0)
|
titles := make([]string, 0)
|
||||||
|
ids := make([]int, 0)
|
||||||
|
|
||||||
epoch := util.SubtractedEpoch(days)
|
epoch := util.SubtractedEpoch(days)
|
||||||
|
|
||||||
for _, i := range data {
|
for _, i := range data {
|
||||||
if i.LastPlayed < epoch {
|
if i.LastPlayed < epoch {
|
||||||
fmt.Println(i.Title)
|
titles = append(titles, i.Title)
|
||||||
|
strirk, err := strconv.Atoi(i.RatingKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
ids = append(ids, strirk)
|
||||||
}
|
}
|
||||||
if i.LastPlayed < 0 {
|
if i.LastPlayed < 0 {
|
||||||
stri, err := strconv.Atoi(i.AddedAt)
|
stri, err := strconv.Atoi(i.AddedAt)
|
||||||
@ -89,11 +98,57 @@ func GetTitles(config config.Config, sectionID int, days int) string {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if int64(stri) < epoch {
|
if int64(stri) < epoch {
|
||||||
fmt.Println(i.Title)
|
titles = append(titles, i.Title)
|
||||||
|
strirk, err := strconv.Atoi(i.RatingKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
ids = append(ids, strirk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sort.Strings(titles)
|
||||||
|
|
||||||
|
LookupFileLocation(config, ids)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return string(epoch)
|
func LookupFileLocation(config config.Config, ids []int) {
|
||||||
|
// fileLocations := make([]string, 0)
|
||||||
|
|
||||||
|
for _, i := range ids {
|
||||||
|
plexURL := fmt.Sprintf("%s:%d%s%d%s%s", config.PlexHost, config.PlexPort, "/library/metadata/", i, "/?X-Plex-Token=", config.PlexToken)
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, plexURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := http.Client{}
|
||||||
|
req.Header.Set("User-Agent", "Housekeeper")
|
||||||
|
|
||||||
|
res, getErr := httpClient.Do(req)
|
||||||
|
if getErr != nil {
|
||||||
|
log.Fatal(getErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, readErr := ioutil.ReadAll(res.Body)
|
||||||
|
if readErr != nil {
|
||||||
|
log.Fatal(readErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
plexModel := model.XMLPlexAPI{}
|
||||||
|
xml.Unmarshal(body, &plexModel)
|
||||||
|
|
||||||
|
fileList := strings.Split(plexModel.Video.Media.Part.File, "/")
|
||||||
|
|
||||||
|
fmt.Printf("/%v/%v/%v/%v/%v\n", fileList[1], fileList[2], fileList[3], fileList[4], fileList[5])
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://172.19.0.105:32400/library/metadata/9/?X-Plex-Token=K1WCALqRK5HVzSQ1J3bM
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
// PlexPyLibraryInfo will gather all the library related info. We really just need the count from this...
|
// PlexPyLibraryInfo will gather all the library related info. We really just need the count from this...
|
||||||
type PlexPyLibraryInfo struct {
|
type PlexPyLibraryInfo struct {
|
||||||
Response struct {
|
Response struct {
|
||||||
@ -60,3 +62,267 @@ type PlexPyMediaInfo struct {
|
|||||||
Result string `json:"result"`
|
Result string `json:"result"`
|
||||||
} `json:"response"`
|
} `json:"response"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XMLPlexAPI - This is the XML version of the struct below it.
|
||||||
|
type XMLPlexAPI struct {
|
||||||
|
XMLName xml.Name `xml:"MediaContainer"`
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Size string `xml:"size,attr"`
|
||||||
|
AllowSync string `xml:"allowSync,attr"`
|
||||||
|
Identifier string `xml:"identifier,attr"`
|
||||||
|
LibrarySectionID string `xml:"librarySectionID,attr"`
|
||||||
|
LibrarySectionTitle string `xml:"librarySectionTitle,attr"`
|
||||||
|
LibrarySectionUUID string `xml:"librarySectionUUID,attr"`
|
||||||
|
MediaTagPrefix string `xml:"mediaTagPrefix,attr"`
|
||||||
|
MediaTagVersion string `xml:"mediaTagVersion,attr"`
|
||||||
|
Video struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
RatingKey string `xml:"ratingKey,attr"`
|
||||||
|
Key string `xml:"key,attr"`
|
||||||
|
Guid string `xml:"guid,attr"`
|
||||||
|
LibrarySectionTitle string `xml:"librarySectionTitle,attr"`
|
||||||
|
LibrarySectionID string `xml:"librarySectionID,attr"`
|
||||||
|
LibrarySectionKey string `xml:"librarySectionKey,attr"`
|
||||||
|
Studio string `xml:"studio,attr"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Title string `xml:"title,attr"`
|
||||||
|
ContentRating string `xml:"contentRating,attr"`
|
||||||
|
Summary string `xml:"summary,attr"`
|
||||||
|
Rating string `xml:"rating,attr"`
|
||||||
|
AudienceRating string `xml:"audienceRating,attr"`
|
||||||
|
Year string `xml:"year,attr"`
|
||||||
|
Tagline string `xml:"tagline,attr"`
|
||||||
|
Thumb string `xml:"thumb,attr"`
|
||||||
|
Art string `xml:"art,attr"`
|
||||||
|
Duration string `xml:"duration,attr"`
|
||||||
|
OriginallyAvailableAt string `xml:"originallyAvailableAt,attr"`
|
||||||
|
AddedAt string `xml:"addedAt,attr"`
|
||||||
|
UpdatedAt string `xml:"updatedAt,attr"`
|
||||||
|
AudienceRatingImage string `xml:"audienceRatingImage,attr"`
|
||||||
|
ChapterSource string `xml:"chapterSource,attr"`
|
||||||
|
PrimaryExtraKey string `xml:"primaryExtraKey,attr"`
|
||||||
|
RatingImage string `xml:"ratingImage,attr"`
|
||||||
|
Media struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
VideoResolution string `xml:"videoResolution,attr"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Duration string `xml:"duration,attr"`
|
||||||
|
Bitrate string `xml:"bitrate,attr"`
|
||||||
|
Width string `xml:"width,attr"`
|
||||||
|
Height string `xml:"height,attr"`
|
||||||
|
AspectRatio string `xml:"aspectRatio,attr"`
|
||||||
|
AudioChannels string `xml:"audioChannels,attr"`
|
||||||
|
AudioCodec string `xml:"audioCodec,attr"`
|
||||||
|
VideoCodec string `xml:"videoCodec,attr"`
|
||||||
|
Container string `xml:"container,attr"`
|
||||||
|
VideoFrameRate string `xml:"videoFrameRate,attr"`
|
||||||
|
AudioProfile string `xml:"audioProfile,attr"`
|
||||||
|
VideoProfile string `xml:"videoProfile,attr"`
|
||||||
|
Part struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Key string `xml:"key,attr"`
|
||||||
|
Duration string `xml:"duration,attr"`
|
||||||
|
File string `xml:"file,attr"`
|
||||||
|
Size string `xml:"size,attr"`
|
||||||
|
AudioProfile string `xml:"audioProfile,attr"`
|
||||||
|
Container string `xml:"container,attr"`
|
||||||
|
VideoProfile string `xml:"videoProfile,attr"`
|
||||||
|
Stream []struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
StreamType string `xml:"streamType,attr"`
|
||||||
|
Default string `xml:"default,attr"`
|
||||||
|
Codec string `xml:"codec,attr"`
|
||||||
|
Index string `xml:"index,attr"`
|
||||||
|
Bitrate string `xml:"bitrate,attr"`
|
||||||
|
Language string `xml:"language,attr"`
|
||||||
|
LanguageCode string `xml:"languageCode,attr"`
|
||||||
|
BitDepth string `xml:"bitDepth,attr"`
|
||||||
|
ChromaLocation string `xml:"chromaLocation,attr"`
|
||||||
|
ChromaSubsampling string `xml:"chromaSubsampling,attr"`
|
||||||
|
FrameRate string `xml:"frameRate,attr"`
|
||||||
|
HasScalingMatrix string `xml:"hasScalingMatrix,attr"`
|
||||||
|
Height string `xml:"height,attr"`
|
||||||
|
Level string `xml:"level,attr"`
|
||||||
|
Profile string `xml:"profile,attr"`
|
||||||
|
RefFrames string `xml:"refFrames,attr"`
|
||||||
|
ScanType string `xml:"scanType,attr"`
|
||||||
|
Title string `xml:"title,attr"`
|
||||||
|
Width string `xml:"width,attr"`
|
||||||
|
DisplayTitle string `xml:"displayTitle,attr"`
|
||||||
|
Selected string `xml:"selected,attr"`
|
||||||
|
Channels string `xml:"channels,attr"`
|
||||||
|
AudioChannelLayout string `xml:"audioChannelLayout,attr"`
|
||||||
|
SamplingRate string `xml:"samplingRate,attr"`
|
||||||
|
Key string `xml:"key,attr"`
|
||||||
|
} `xml:"Stream"`
|
||||||
|
} `xml:"Part"`
|
||||||
|
} `xml:"Media"`
|
||||||
|
Genre []struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Filter string `xml:"filter,attr"`
|
||||||
|
Tag string `xml:"tag,attr"`
|
||||||
|
} `xml:"Genre"`
|
||||||
|
Director struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Filter string `xml:"filter,attr"`
|
||||||
|
Tag string `xml:"tag,attr"`
|
||||||
|
} `xml:"Director"`
|
||||||
|
Writer []struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Filter string `xml:"filter,attr"`
|
||||||
|
Tag string `xml:"tag,attr"`
|
||||||
|
} `xml:"Writer"`
|
||||||
|
Producer []struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Filter string `xml:"filter,attr"`
|
||||||
|
Tag string `xml:"tag,attr"`
|
||||||
|
} `xml:"Producer"`
|
||||||
|
Country struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Filter string `xml:"filter,attr"`
|
||||||
|
Tag string `xml:"tag,attr"`
|
||||||
|
} `xml:"Country"`
|
||||||
|
Role []struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Filter string `xml:"filter,attr"`
|
||||||
|
Tag string `xml:"tag,attr"`
|
||||||
|
Role string `xml:"role,attr"`
|
||||||
|
Thumb string `xml:"thumb,attr"`
|
||||||
|
} `xml:"Role"`
|
||||||
|
} `xml:"Video"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlexAPI - This contains the information returned from Plex's API
|
||||||
|
type PlexAPI struct {
|
||||||
|
MediaContainer struct {
|
||||||
|
Video struct {
|
||||||
|
Media struct {
|
||||||
|
Part struct {
|
||||||
|
Stream []struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
StreamType string `json:"_streamType"`
|
||||||
|
Default string `json:"_default,omitempty"`
|
||||||
|
Codec string `json:"_codec"`
|
||||||
|
Index string `json:"_index,omitempty"`
|
||||||
|
Bitrate string `json:"_bitrate,omitempty"`
|
||||||
|
Language string `json:"_language"`
|
||||||
|
LanguageCode string `json:"_languageCode"`
|
||||||
|
BitDepth string `json:"_bitDepth,omitempty"`
|
||||||
|
ChromaLocation string `json:"_chromaLocation,omitempty"`
|
||||||
|
ChromaSubsampling string `json:"_chromaSubsampling,omitempty"`
|
||||||
|
FrameRate string `json:"_frameRate,omitempty"`
|
||||||
|
HasScalingMatrix string `json:"_hasScalingMatrix,omitempty"`
|
||||||
|
Height string `json:"_height,omitempty"`
|
||||||
|
Level string `json:"_level,omitempty"`
|
||||||
|
Profile string `json:"_profile,omitempty"`
|
||||||
|
RefFrames string `json:"_refFrames,omitempty"`
|
||||||
|
ScanType string `json:"_scanType,omitempty"`
|
||||||
|
Title string `json:"_title,omitempty"`
|
||||||
|
Width string `json:"_width,omitempty"`
|
||||||
|
DisplayTitle string `json:"_displayTitle"`
|
||||||
|
Selected string `json:"_selected,omitempty"`
|
||||||
|
Channels string `json:"_channels,omitempty"`
|
||||||
|
AudioChannelLayout string `json:"_audioChannelLayout,omitempty"`
|
||||||
|
SamplingRate string `json:"_samplingRate,omitempty"`
|
||||||
|
Key string `json:"_key,omitempty"`
|
||||||
|
} `json:"Stream"`
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Key string `json:"_key"`
|
||||||
|
Duration string `json:"_duration"`
|
||||||
|
File string `json:"_file"`
|
||||||
|
Size string `json:"_size"`
|
||||||
|
AudioProfile string `json:"_audioProfile"`
|
||||||
|
Container string `json:"_container"`
|
||||||
|
VideoProfile string `json:"_videoProfile"`
|
||||||
|
} `json:"Part"`
|
||||||
|
VideoResolution string `json:"_videoResolution"`
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Duration string `json:"_duration"`
|
||||||
|
Bitrate string `json:"_bitrate"`
|
||||||
|
Width string `json:"_width"`
|
||||||
|
Height string `json:"_height"`
|
||||||
|
AspectRatio string `json:"_aspectRatio"`
|
||||||
|
AudioChannels string `json:"_audioChannels"`
|
||||||
|
AudioCodec string `json:"_audioCodec"`
|
||||||
|
VideoCodec string `json:"_videoCodec"`
|
||||||
|
Container string `json:"_container"`
|
||||||
|
VideoFrameRate string `json:"_videoFrameRate"`
|
||||||
|
AudioProfile string `json:"_audioProfile"`
|
||||||
|
VideoProfile string `json:"_videoProfile"`
|
||||||
|
} `json:"Media"`
|
||||||
|
Genre []struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Filter string `json:"_filter"`
|
||||||
|
Tag string `json:"_tag"`
|
||||||
|
} `json:"Genre"`
|
||||||
|
Director struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Filter string `json:"_filter"`
|
||||||
|
Tag string `json:"_tag"`
|
||||||
|
} `json:"Director"`
|
||||||
|
Writer []struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Filter string `json:"_filter"`
|
||||||
|
Tag string `json:"_tag"`
|
||||||
|
} `json:"Writer"`
|
||||||
|
Producer []struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Filter string `json:"_filter"`
|
||||||
|
Tag string `json:"_tag"`
|
||||||
|
} `json:"Producer"`
|
||||||
|
Country struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Filter string `json:"_filter"`
|
||||||
|
Tag string `json:"_tag"`
|
||||||
|
} `json:"Country"`
|
||||||
|
Role []struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Filter string `json:"_filter"`
|
||||||
|
Tag string `json:"_tag"`
|
||||||
|
Role string `json:"_role"`
|
||||||
|
Thumb string `json:"_thumb"`
|
||||||
|
} `json:"Role"`
|
||||||
|
RatingKey string `json:"_ratingKey"`
|
||||||
|
Key string `json:"_key"`
|
||||||
|
GUID string `json:"_guid"`
|
||||||
|
LibrarySectionTitle string `json:"_librarySectionTitle"`
|
||||||
|
LibrarySectionID string `json:"_librarySectionID"`
|
||||||
|
LibrarySectionKey string `json:"_librarySectionKey"`
|
||||||
|
Studio string `json:"_studio"`
|
||||||
|
Type string `json:"_type"`
|
||||||
|
Title string `json:"_title"`
|
||||||
|
ContentRating string `json:"_contentRating"`
|
||||||
|
Summary string `json:"_summary"`
|
||||||
|
Rating string `json:"_rating"`
|
||||||
|
AudienceRating string `json:"_audienceRating"`
|
||||||
|
Year string `json:"_year"`
|
||||||
|
Tagline string `json:"_tagline"`
|
||||||
|
Thumb string `json:"_thumb"`
|
||||||
|
Art string `json:"_art"`
|
||||||
|
Duration string `json:"_duration"`
|
||||||
|
OriginallyAvailableAt string `json:"_originallyAvailableAt"`
|
||||||
|
AddedAt string `json:"_addedAt"`
|
||||||
|
UpdatedAt string `json:"_updatedAt"`
|
||||||
|
AudienceRatingImage string `json:"_audienceRatingImage"`
|
||||||
|
ChapterSource string `json:"_chapterSource"`
|
||||||
|
PrimaryExtraKey string `json:"_primaryExtraKey"`
|
||||||
|
RatingImage string `json:"_ratingImage"`
|
||||||
|
} `json:"Video"`
|
||||||
|
Size string `json:"_size"`
|
||||||
|
AllowSync string `json:"_allowSync"`
|
||||||
|
Identifier string `json:"_identifier"`
|
||||||
|
LibrarySectionID string `json:"_librarySectionID"`
|
||||||
|
LibrarySectionTitle string `json:"_librarySectionTitle"`
|
||||||
|
LibrarySectionUUID string `json:"_librarySectionUUID"`
|
||||||
|
MediaTagPrefix string `json:"_mediaTagPrefix"`
|
||||||
|
MediaTagVersion string `json:"_mediaTagVersion"`
|
||||||
|
} `json:"MediaContainer"`
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user