implement thubnail loading from tmdb
fix lots of errors if ' char occurs in path strings correct reverse proxy for websocket
This commit is contained in:
		@@ -72,4 +72,21 @@ WHERE tvshow_episodes.id=%d`, le.ID)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		return jsonify(ret)
 | 
							return jsonify(ret)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var rtn struct {
 | 
				
			||||||
 | 
							Id int
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						AddHandler("readThumbnail", TVShowNode, &rtn, func() []byte {
 | 
				
			||||||
 | 
							var pic []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							query := fmt.Sprintf("SELECT thumbnail FROM tvshow WHERE id=%d", rtn.Id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := database.QueryRow(query).Scan(&pic)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Printf("the thumbnail of movie id %d couldn't be found", rtn.Id)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return pic
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,7 +44,7 @@ func getVideoHandlers() {
 | 
				
			|||||||
	AddHandler("readThumbnail", VideoNode, &rtn, func() []byte {
 | 
						AddHandler("readThumbnail", VideoNode, &rtn, func() []byte {
 | 
				
			||||||
		var pic []byte
 | 
							var pic []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		query := fmt.Sprintf("SELECT thumbnail FROM videos WHERE movie_id='%d'", rtn.Movieid)
 | 
							query := fmt.Sprintf("SELECT thumbnail FROM videos WHERE movie_id=%d", rtn.Movieid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err := database.QueryRow(query).Scan(&pic)
 | 
							err := database.QueryRow(query).Scan(&pic)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,12 +9,15 @@ import (
 | 
				
			|||||||
	"openmediacenter/apiGo/database"
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
	"openmediacenter/apiGo/static"
 | 
						"openmediacenter/apiGo/static"
 | 
				
			||||||
	"openmediacenter/apiGo/videoparser"
 | 
						"openmediacenter/apiGo/videoparser"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/videoparser/tmdb"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	fmt.Println("init OpenMediaCenter server")
 | 
						fmt.Println("init OpenMediaCenter server")
 | 
				
			||||||
	port := 8081
 | 
						port := 8081
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tmdb.SearchTVShow("Arrow")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db, verbose, pathPrefix := handleCommandLineArguments()
 | 
						db, verbose, pathPrefix := handleCommandLineArguments()
 | 
				
			||||||
	// todo some verbosity logger or sth
 | 
						// todo some verbosity logger or sth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"openmediacenter/apiGo/api/types"
 | 
						"openmediacenter/apiGo/api/types"
 | 
				
			||||||
	"openmediacenter/apiGo/database"
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/videoparser/tmdb"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
@@ -23,8 +24,8 @@ func startTVShowReindex(files []Show, sett types.SettingsType) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func insertEpisodesIfNotExisting(show Show) {
 | 
					func insertEpisodesIfNotExisting(show Show) {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT tvshow_episodes.name, season, episode FROM tvshow_episodes JOIN tvshow t on t.id = tvshow_episodes.tvshow_id WHERE t.name='%s'", show.Name)
 | 
						query := "SELECT tvshow_episodes.name, season, episode FROM tvshow_episodes JOIN tvshow t on t.id = tvshow_episodes.tvshow_id WHERE t.name=?"
 | 
				
			||||||
	rows := database.Query(query)
 | 
						rows := database.Query(query, show.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var dbepisodes []string
 | 
						var dbepisodes []string
 | 
				
			||||||
	for rows.Next() {
 | 
						for rows.Next() {
 | 
				
			||||||
@@ -47,7 +48,7 @@ func insertEpisodesIfNotExisting(show Show) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Println("diff is...")
 | 
						fmt.Println("diff is...")
 | 
				
			||||||
	fmt.Println(diff)
 | 
						fmt.Println(len(diff))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func insertEpisode(path string, ShowName string) {
 | 
					func insertEpisode(path string, ShowName string) {
 | 
				
			||||||
@@ -55,21 +56,27 @@ func insertEpisode(path string, ShowName string) {
 | 
				
			|||||||
	episodeRegex := regexp.MustCompile("E[0-9][0-9]")
 | 
						episodeRegex := regexp.MustCompile("E[0-9][0-9]")
 | 
				
			||||||
	matchENDPattern := regexp.MustCompile(" S[0-9][0-9]E[0-9][0-9].+$")
 | 
						matchENDPattern := regexp.MustCompile(" S[0-9][0-9]E[0-9][0-9].+$")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seasonStr := seasonRegex.FindString(path)[1:]
 | 
						seasonStr := seasonRegex.FindString(path)
 | 
				
			||||||
	episodeStr := episodeRegex.FindString(path)[1:]
 | 
						episodeStr := episodeRegex.FindString(path)
 | 
				
			||||||
	extString := matchENDPattern.FindString(path)
 | 
						extString := matchENDPattern.FindString(path)
 | 
				
			||||||
 | 
						// handle invalid matches
 | 
				
			||||||
 | 
						if len(seasonStr) != 3 || len(episodeStr) != 3 || len(extString) < 8 {
 | 
				
			||||||
 | 
							fmt.Printf("Error inserting episode: %s  -- %s/%s/%s\n", path, seasonStr, episodeStr, extString)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	name := strings.TrimSuffix(path, extString)
 | 
						name := strings.TrimSuffix(path, extString)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	season, err := strconv.ParseInt(seasonStr, 10, 8)
 | 
						season, err := strconv.ParseInt(seasonStr[1:], 10, 8)
 | 
				
			||||||
	episode, err := strconv.ParseInt(episodeStr, 10, 8)
 | 
						episode, err := strconv.ParseInt(episodeStr[1:], 10, 8)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println(err.Error())
 | 
							fmt.Println(err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	query := fmt.Sprintf(`
 | 
						query := `
 | 
				
			||||||
INSERT INTO tvshow_episodes (name, season, poster, tvshow_id, episode, filename)
 | 
					INSERT INTO tvshow_episodes (name, season, poster, tvshow_id, episode, filename)
 | 
				
			||||||
VALUES ('%s', %d, '%s', (SELECT tvshow.id FROM tvshow WHERE tvshow.name='%s'), %d, '%s')`, name, season, "", ShowName, episode, path)
 | 
					VALUES (?, ?, ?, (SELECT tvshow.id FROM tvshow WHERE tvshow.name=?), ?, ?)`
 | 
				
			||||||
	err = database.Edit(query)
 | 
						err = database.Edit(query, name, season, "", ShowName, episode, path)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println(err.Error())
 | 
							fmt.Println(err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -99,10 +106,18 @@ func insertShowIfNotExisting(show Show, allShows *[]string) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// todo load tmdb pic
 | 
						// insert empty thubnail if tmdb fails
 | 
				
			||||||
 | 
						thubnail := ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// load tmdb infos
 | 
				
			||||||
 | 
						tmdbInfo := tmdb.SearchTVShow(show.Name)
 | 
				
			||||||
 | 
						if tmdbInfo != nil {
 | 
				
			||||||
 | 
							thubnail = tmdbInfo.Thumbnail
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// currently the foldernamme == name which mustn't necessarily be
 | 
						// currently the foldernamme == name which mustn't necessarily be
 | 
				
			||||||
	query := fmt.Sprintf("INSERT INTO tvshow (name, thumbnail, foldername) VALUES ('%s', '%s', '%s')", show.Name, "", show.Name)
 | 
						query := "INSERT INTO tvshow (name, thumbnail, foldername) VALUES (?, ?, ?)"
 | 
				
			||||||
	err := database.Edit(query)
 | 
						err := database.Edit(query, show.Name, thubnail, show.Name)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println(err.Error())
 | 
							fmt.Println(err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,12 @@ type VideoTMDB struct {
 | 
				
			|||||||
	GenreIds  []int
 | 
						GenreIds  []int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TVShowTMDB struct {
 | 
				
			||||||
 | 
						Thumbnail string
 | 
				
			||||||
 | 
						Overview  string
 | 
				
			||||||
 | 
						GenreIds  []int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type tmdbVidResult struct {
 | 
					type tmdbVidResult struct {
 | 
				
			||||||
	Poster_path       string
 | 
						Poster_path       string
 | 
				
			||||||
	Adult             bool
 | 
						Adult             bool
 | 
				
			||||||
@@ -89,7 +95,7 @@ func SearchVideo(MovieName string, year int) *VideoTMDB {
 | 
				
			|||||||
	// continue label
 | 
						// continue label
 | 
				
			||||||
cont:
 | 
					cont:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	thumbnail := fetchPoster(tmdbVid)
 | 
						thumbnail := fetchPoster(tmdbVid.Poster_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result := VideoTMDB{
 | 
						result := VideoTMDB{
 | 
				
			||||||
		Thumbnail: *thumbnail,
 | 
							Thumbnail: *thumbnail,
 | 
				
			||||||
@@ -101,8 +107,64 @@ cont:
 | 
				
			|||||||
	return &result
 | 
						return &result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func fetchPoster(vid tmdbVidResult) *string {
 | 
					type tmdbTvResult struct {
 | 
				
			||||||
	url := fmt.Sprintf("%s%s", pictureBase, vid.Poster_path)
 | 
						PosterPath       string   `json:"poster_path"`
 | 
				
			||||||
 | 
						Popularity       int      `json:"popularity"`
 | 
				
			||||||
 | 
						Id               int      `json:"id"`
 | 
				
			||||||
 | 
						BackdropPath     string   `json:"backdrop_path"`
 | 
				
			||||||
 | 
						VoteAverage      int      `json:"vote_average"`
 | 
				
			||||||
 | 
						Overview         string   `json:"overview"`
 | 
				
			||||||
 | 
						FirstAirDate     string   `json:"first_air_date"`
 | 
				
			||||||
 | 
						OriginCountry    []string `json:"origin_country"`
 | 
				
			||||||
 | 
						GenreIds         []int    `json:"genre_ids"`
 | 
				
			||||||
 | 
						OriginalLanguage string   `json:"original_language"`
 | 
				
			||||||
 | 
						VoteCount        int      `json:"vote_count"`
 | 
				
			||||||
 | 
						Name             string   `json:"name"`
 | 
				
			||||||
 | 
						OriginalName     string   `json:"original_name"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func SearchTVShow(Name string) *TVShowTMDB {
 | 
				
			||||||
 | 
						fmt.Printf("Searching TMDB for: TVShow: %s\n", Name)
 | 
				
			||||||
 | 
						queryURL := fmt.Sprintf("%ssearch/tv?api_key=%s&query=%s", baseUrl, apiKey, url.QueryEscape(Name))
 | 
				
			||||||
 | 
						resp, err := http.Get(queryURL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Println(err.Error())
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						body, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Println(err.Error())
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var t struct {
 | 
				
			||||||
 | 
							Results []tmdbTvResult `json:"results"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = json.Unmarshal(body, &t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println(len(t.Results))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(t.Results) == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res := TVShowTMDB{
 | 
				
			||||||
 | 
							Thumbnail: "",
 | 
				
			||||||
 | 
							Overview:  t.Results[0].Overview,
 | 
				
			||||||
 | 
							GenreIds:  t.Results[0].GenreIds,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						thumbnail := fetchPoster(t.Results[0].PosterPath)
 | 
				
			||||||
 | 
						if thumbnail != nil {
 | 
				
			||||||
 | 
							res.Thumbnail = *thumbnail
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func fetchPoster(posterPath string) *string {
 | 
				
			||||||
 | 
						url := fmt.Sprintf("%s%s", pictureBase, posterPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp, err := http.Get(url)
 | 
						resp, err := http.Get(url)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,4 +16,11 @@ server {
 | 
				
			|||||||
    location ~* ^/(api/|token) {
 | 
					    location ~* ^/(api/|token) {
 | 
				
			||||||
        proxy_pass http://127.0.0.1:8081;
 | 
					        proxy_pass http://127.0.0.1:8081;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    location /subscribe {
 | 
				
			||||||
 | 
					        proxy_pass http://127.0.0.1:8081;
 | 
				
			||||||
 | 
					        proxy_http_version 1.1;
 | 
				
			||||||
 | 
					        proxy_set_header Upgrade $http_upgrade;
 | 
				
			||||||
 | 
					        proxy_set_header Connection "Upgrade";
 | 
				
			||||||
 | 
					        proxy_set_header Host $host;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,12 @@ class DynamicContentContainer<T> extends React.Component<Props<T>, state<T>> {
 | 
				
			|||||||
    // stores current index of loaded elements
 | 
					    // stores current index of loaded elements
 | 
				
			||||||
    loadindex: number = 0;
 | 
					    loadindex: number = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    readonly InitialLoadNR = this.props.initialLoadNr
 | 
				
			||||||
 | 
					        ? this.props.initialLoadNr === -1
 | 
				
			||||||
 | 
					            ? this.props.data.length
 | 
				
			||||||
 | 
					            : this.props.initialLoadNr
 | 
				
			||||||
 | 
					        : 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(props: Props<T>) {
 | 
					    constructor(props: Props<T>) {
 | 
				
			||||||
        super(props);
 | 
					        super(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,14 +36,14 @@ class DynamicContentContainer<T> extends React.Component<Props<T>, state<T>> {
 | 
				
			|||||||
    componentDidMount(): void {
 | 
					    componentDidMount(): void {
 | 
				
			||||||
        document.addEventListener('scroll', this.trackScrolling);
 | 
					        document.addEventListener('scroll', this.trackScrolling);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.loadPreviewBlock(this.props.initialLoadNr ? this.props.initialLoadNr : 16);
 | 
					        this.loadPreviewBlock(this.InitialLoadNR);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    componentDidUpdate(prevProps: Props<T>): void {
 | 
					    componentDidUpdate(prevProps: Props<T>): void {
 | 
				
			||||||
        // when source props change force update!
 | 
					        // when source props change force update!
 | 
				
			||||||
        if (prevProps.data.length !== this.props.data.length) {
 | 
					        if (prevProps.data.length !== this.props.data.length) {
 | 
				
			||||||
            this.clean();
 | 
					            this.clean();
 | 
				
			||||||
            this.loadPreviewBlock(this.props.initialLoadNr ? this.props.initialLoadNr : 16);
 | 
					            this.loadPreviewBlock(this.InitialLoadNR);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,6 +42,7 @@ class EpisodePage extends React.Component<Props, State> {
 | 
				
			|||||||
                <DynamicContentContainer
 | 
					                <DynamicContentContainer
 | 
				
			||||||
                    renderElement={(el): JSX.Element => <EpisodeTile key={el.ID} episode={el} />}
 | 
					                    renderElement={(el): JSX.Element => <EpisodeTile key={el.ID} episode={el} />}
 | 
				
			||||||
                    data={this.episodes}
 | 
					                    data={this.episodes}
 | 
				
			||||||
 | 
					                    initialLoadNr={-1}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            </>
 | 
					            </>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import Preview from '../../elements/Preview/Preview';
 | 
					import Preview from '../../elements/Preview/Preview';
 | 
				
			||||||
import {APINode, callAPI} from '../../utils/Api';
 | 
					import {APINode, callAPI, callAPIPlain} from '../../utils/Api';
 | 
				
			||||||
import {TVShow} from '../../types/ApiTypes';
 | 
					import {TVShow} from '../../types/ApiTypes';
 | 
				
			||||||
import DynamicContentContainer from '../../elements/DynamicContentContainer/DynamicContentContainer';
 | 
					import DynamicContentContainer from '../../elements/DynamicContentContainer/DynamicContentContainer';
 | 
				
			||||||
import {Route, Switch, useRouteMatch} from 'react-router-dom';
 | 
					import {Route, Switch, useRouteMatch} from 'react-router-dom';
 | 
				
			||||||
@@ -33,11 +33,21 @@ class TVShowPage extends React.Component<Props, State> {
 | 
				
			|||||||
                    <Preview
 | 
					                    <Preview
 | 
				
			||||||
                        key={elem.Id}
 | 
					                        key={elem.Id}
 | 
				
			||||||
                        name={elem.Name}
 | 
					                        name={elem.Name}
 | 
				
			||||||
                        picLoader={(callback): void => callback('')}
 | 
					                        picLoader={(callback: (pic: string) => void): void => {
 | 
				
			||||||
 | 
					                            callAPIPlain(
 | 
				
			||||||
 | 
					                                APINode.TVShow,
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    action: 'readThumbnail',
 | 
				
			||||||
 | 
					                                    Id: elem.Id
 | 
				
			||||||
 | 
					                                },
 | 
				
			||||||
 | 
					                                (result) => callback(result)
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
 | 
					                        }}
 | 
				
			||||||
                        linkPath={'/tvshows/' + elem.Id}
 | 
					                        linkPath={'/tvshows/' + elem.Id}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
                data={this.state.loading ? [] : this.data}
 | 
					                data={this.state.loading ? [] : this.data}
 | 
				
			||||||
 | 
					                initialLoadNr={20}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user