Bienvenido a Tecnohackers

Tecnohackers » Programacion » Area de Programacion » Programacion a Bajo Nivel. APIs, Hooking, ASM, C/C++, etc.
 » 

SpotDown v2.0: Spotify Music Downloader [C#]



Autor Tema: SpotDown v2.0: Spotify Music Downloader [C#]  (Leído 2623 veces)

Desconectado zolo

  • Consigliere
  • Master
  • *****
  • Mensajes: 22377
  • Un Mes, Un Año o Toda Una Vida, Da Igual, Estare
SpotDown v2.0: Spotify Music Downloader [C#]
« en: Diciembre 26, 2015, 09:11:37 am »
SpotDown V2
Descarga musica Spotify

SpotDown es un programa para descargar música Spotify, es tan simple como eso!


Caracteristicas:

Ahora Obtiene Spotify: Nombre del álbum, obras de arte del álbum y nombre del artista (No más buscando álbumes de música desordenados)
Actualmente obtiene en esta ocasión la más alta tasa de bits de la canción.
Obtiene todas las canciones (la última versión no fue capaz de descargar algunas canciones si fueron encontrados)
Arrastrar y soltar canciones.
Fácil de usar
Código abierto

Como compilar

Para compilar esto tu nesecitarás YouTube API key, lo puedes descargar desde You are not allowed to view links. Register or Login

Nota

Esto funciona de la misma manera que trabajó SpotyDL, en términos muy básicos se pone nombres de las canciones y luego se encuentra la canción y lo descarga desde sitios web externos del motor de búsqueda de más alta calidad. Esto no recibe la canción de spotify en sí, ya que es impossable.
Los dos surces son MP3Clan y YouTube, con MP3Clan siendo la fuente principal

Codigo Fuente

[code]using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using Google.Apis.Services;
using Google.Apis.YouTube.v3;
using Newtonsoft.Json.Linq;
using SpotDown_V2.Classes;
using TagLib;
using SpotDown_V2.Properties;
using File = System.IO.File;

namespace SpotDown_V2
{
    /// <summary>
    /// Please give credit to me if you use this or any part of it.
    /// HF: You are not allowed to view links. Register or Login
    /// GitHub: You are not allowed to view links. Register or Login
    /// Website: You are not allowed to view links. Register or Login
    /// Twitter: You are not allowed to view links. Register or Login
    /// </summary>

    //ToDo
    //-Fix this mess of code.
    //-Does not always start downloading first time. Restart program and try again.
    //-Progress bar can be glitchy.

    public partial class MainForm : Form
    {
        private const string ApiKey = "{Enter Your YouTube Key Here}";
        string _dir = "songs/";
        string _tempDir = "songs/temp/";
        int maxRunning = 10;
        int _running = 0;
        int _songs = 0;
        int _youtubeNum = 0;
        int _youtubeDownloadedNum = 0;
        int _mp3ClanNum = 0;
        int _mp3ClanDownloadedNum = 0;
        int _totalQuedNum = 0;
        int _totalFinished = 0;
        int _current = 0;
        bool _debug = false;
        string _ffmpegPath;

        ListViewData[] downloadData = new ListViewData[10000];
        private int[] songsArray = new int[10000];
        private PassArguments[][] songArray = new PassArguments[10000][];

        private const string Website = @"You are not allowed to view links. Register or Login";
        private readonly string _spotDownUa = "SpotDown " + Assembly.GetExecutingAssembly().GetName().Version + " " + Environment.OSVersion;

        YouTubeDownloader youTubeDownloader = new YouTubeDownloader();

        public MainForm()
        {
            CheckForIllegalCrossThreadCalls = false;
            InitializeComponent();

            CheckUpdate();
            Size = new Size(597, 448);
            SongListView.SmallImageList = imageList1;

            SetupFfmpeg();
            SetupDir();
           
            Log("Started");
        }

        void SetupDir()
        {
            if (Settings.Default.SaveDir.Length < 1)
            {
                downloadDirTextBox.Text = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            }
            else
            {
                downloadDirTextBox.Text = Settings.Default.SaveDir;
            }
            _dir = downloadDirTextBox.Text + "/";
            _tempDir = _dir + "temp/";
            if (!Directory.Exists(_tempDir))
            {
                DirectoryInfo di = Directory.CreateDirectory(_tempDir);
                di.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
            }
        }

        void SetupFfmpeg()
        {
            try
            {
                _ffmpegPath = Path.Combine(Path.GetTempPath(), "ffmpeg.exe");
                File.WriteAllBytes(_ffmpegPath, Resources.ffmpeg);
            }
            catch (Exception)
            {

            }
        }

        public void Log(string text, bool debugLog = false)
        {
            const string logFormat = "[{0}] >> {1}\n";

            if (string.IsNullOrWhiteSpace(text))
                throw new ArgumentException("Log text can not be empty!");

            var logText = text;
            if (debugLog && _debug)
            {
                textBox1.AppendText(string.Format(logFormat, DateTime.Now.ToLongTimeString(), logText));
            }
            else if (debugLog == false)
            {
                textBox1.AppendText(string.Format(logFormat, DateTime.Now.ToLongTimeString(), logText));
            }
        }

        private void CheckUpdate()
        {
            WebClient wc = new WebClient();
            int latest = 0;
            try
            {
                latest = Convert.ToInt32(wc.DownloadString("You are not allowed to view links. Register or Login"));
            }
            catch (WebException ex)
            {
                Log("Could not reach update server.");
            }
           
            int currentVersion = Convert.ToInt32(Application.ProductVersion.Replace(".", ""));
            labelVersion.Text = "Version: " + Assembly.GetExecutingAssembly().GetName().Version;
            if (currentVersion < latest )
            {
                if (
                    MessageBox.Show("There is a newer version of SpotDown available. Would you like to upgrade?",
                        "SpotDown", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    Process.Start(Website);
                    Application.Exit();
                }
            }
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_running != 0)
            {
                DialogResult dialogResult = MessageBox.Show("Are you sure you want to quit? Some songs are sill being downloaded.", "Are you sure?", MessageBoxButtons.YesNo);
                if (dialogResult == DialogResult.Yes)
                {
                    Process.GetCurrentProcess().Kill();
                }
                else if (dialogResult == DialogResult.No)
                {
                    e.Cancel = true;
                }
            }
            else
            {
                if (Directory.Exists(_tempDir))
                {
                    Directory.Delete(_tempDir, true);
                }
            }
            try
            {
                File.Delete(_ffmpegPath);
            }
            catch (Exception)
            {
            }
        }

        private void DownloadDirTextBox_TextChanged(object sender, EventArgs e)
        {
            _dir = downloadDirTextBox.Text + "/";
            _tempDir = _dir + "temp/";
            if (!Directory.Exists(_tempDir))
            {
                DirectoryInfo di = Directory.CreateDirectory(_tempDir);
                di.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
            }
        }

        private void SongListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            if (e.IsSelected)
                e.Item.Selected = false;
        }

        private void SongListView_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.StringFormat))
            {
                e.Effect = DragDropEffects.Copy;
            }
        }

        private void SongListView_DragDrop(object sender, DragEventArgs e)
        {
            try
            {
                if (e.Data.GetDataPresent(DataFormats.StringFormat))
                {
                    _current++;
                    songArray[_current] = new PassArguments[10000];
                    string data = (string)e.Data.GetData(DataFormats.StringFormat);
                    data = data.Replace("You are not allowed to view links. Register or Login", "");
                    string[] strArrays = data.Split(new char[] { '\n' });
                    _songs = _songs + strArrays.Length;
                    songsArray[_current] = strArrays.Length;
                    Log("Loading " + strArrays.Length + " songs...");
                    foreach (string str in strArrays)
                    {
                        if (str.Length > 1)
                        {
                            BackgroundWorker backgroundWorkerStart = new BackgroundWorker();
                            backgroundWorkerStart.DoWork += BackgroundWorkerStart_DoWork;
                            backgroundWorkerStart.RunWorkerAsync(new PassArguments
                            {
                                PassedSpotCode = str,
                                PassedSession = _current
                            });
                        }
                    }
                }
            }
            catch (Exception ex) { MessageBox.Show(this, ex.Message, ex.Source, MessageBoxButtons.OK, MessageBoxIcon.Error); }
        }

        private void DownloadDirOpenButton_Click(object sender, EventArgs e)
        {
            Process.Start(downloadDirTextBox.Text);
        }

        private void DownloadDirBrowseButton_Click(object sender, EventArgs e)
        {
            using (var fbd = new FolderBrowserDialog())
            {
                if (fbd.ShowDialog() == DialogResult.OK)
                {
                    var directory = fbd.SelectedPath;

                    downloadDirTextBox.Text = directory;
                    Settings.Default["SaveDir"] = downloadDirTextBox.Text;
                    Settings.Default.Save();
                }
            }
        }

        private void BackgroundWorkerStart_DoWork(object sender, DoWorkEventArgs e)
        {
            PassArguments result = (PassArguments)e.Argument;
            e.Result = result;
            newProgressBar1.CustomText = "Loading...";
            SearchSpotify(result.PassedSpotCode, result.PassedSession);
        }

        private void SearchSpotify(string code, int session)
        {
            int num = 0;
            PassArguments spotifyName = GetSpotifyName(code);
            bool add = true;

            foreach (PassArguments[] songArrayInArray in songArray)
            {
                if (songArrayInArray != null)
                {
                    foreach (var songThing in songArrayInArray)
                    {
                        if (songThing != null)
                        {
                            if (songThing.PassedFileName.Equals(spotifyName.PassedSong + " - " + spotifyName.PassedArtist))
                            {
                                //File already in list
                                _songs--;
                                songsArray[_current]--;
                                add = false;
                                Log("[Attention] The song " + spotifyName.PassedSong + " - " + spotifyName.PassedArtist +
                                    " was already added.");
                            }
                        }
                    }
                }
            }
            if (File.Exists(_dir + EscapeFilename(spotifyName.PassedFileName) + ".mp3"))
            {
                //File already exsists/Downloaded
                _songs--;
                songsArray[_current]--;
                add = false;
            }

            try
            {
                if (add)
                {
                    {
                        SongListView.BeginUpdate();
                        string[] row = { "Waiting", spotifyName.PassedSong + " - " + spotifyName.PassedArtist };
                        var listViewItem = new ListViewItem(row);
                        listViewItem.ImageIndex = 1;
                        SongListView.Items.Add(listViewItem);
                        SetLabelVisible(false);
                        num = listViewItem.Index;
                        SongListView.EndUpdate();

                        songArray[session][num] = (new PassArguments
                        {
                            PassedSong = spotifyName.PassedSong,
                            PassedArtist = spotifyName.PassedArtist,
                            PassedNum = num,
                            PassedFileName = spotifyName.PassedSong + " - " + spotifyName.PassedArtist,
                            PassedAlbum = spotifyName.PassedAlbum,
                            PassedAlbumId = spotifyName.PassedAlbumId,
                            PassedLength = spotifyName.PassedLength,
                            PassedLengthMs = spotifyName.PassedLengthMs,
                            PassedimageURL = spotifyName.PassedimageURL
                        });
                    }
                }

//                if (SongListView.Items.Count == songs)
                int result = songArray[session].Count(s => s != null);
//                Log(result + " | " + songsArray[current]);
                if (result == songsArray[_current])
                {
                    Log(songsArray[_current] + " songs added. Total songs: " + _songs);
                    SearchSongArray(session);
                }
            }
            catch (Exception ex)
            {
                Log("[Error: x1] " + ex.Message + Environment.NewLine + num, true);
            }
        }

        public void SearchSongArray(int session)
        {
            foreach (PassArguments songInfo in songArray[session])
            {
                if (songInfo != null)
                {
                    try
                    {
                        DownloadMP3Clan(songInfo.PassedNum, session);
                    }
                    catch (Exception ex)
                    {
                        Log("[Error: x2] " + ex.Message + " " + songInfo.PassedNum + " | " + songInfo.PassedFileName, true);
                    }
                }
            }
            Log("All song platforms found.");
        }

        public void DownloadMP3Clan(int num, int session)
        {
            PassArguments result = songArray[session][num];
           
            EditList("Loading...", result.PassedFileName, result.PassedNum, 0);

            int highestBitrateNum = 0;
            Mp3ClanTrack highestBitrateTrack = null;
            List<Mp3ClanTrack> tracks = null;

            const string searchBaseUrl = "You are not allowed to view links. Register or Login";
            var searchUrl = new Uri(string.Format(searchBaseUrl, Uri.EscapeDataString(result.PassedSong)));
            string pageSource = null;

            while (true)
            {
                try
                {
                    pageSource = new MyWebClient().DownloadString(searchUrl);
                    break;
                }
                catch (WebException e)
                {
                    if (e.Status == WebExceptionStatus.ProtocolError)
                    {
                        EditList("Queued", result.PassedFileName, result.PassedNum, 1); //In Que so when its less than 5 it will start
                        _youtubeNum++;
                        _totalQuedNum++;
                        var th = new Thread(unused => Search(num, session));
                        th.Start();
                        return;
                    }
                    Log("[Error: x3] " + e.Message + " | " + result.PassedFileName, true);
                }
            }
           

            IEnumerable<Mp3ClanTrack> trackResult;
            if (Mp3ClanTrack.TryParseFromSource(pageSource, out trackResult))
            {
                tracks = trackResult.ToList();
                foreach (var track in tracks)
                {
                    if (track.Artist.ToLower().Trim().Contains(result.PassedArtist.ToLower()) &&
                        track.Name.ToLower().Trim().Equals(result.PassedSong.ToLower()))
                    {
                        string bitrateString = null;
                        int attempts = 0;
                        while (true)
                        {
                            try
                            {
                                bitrateString = new MyWebClient().DownloadString("You are not allowed to view links. Register or Login" + track.Mp3ClanUrl.Replace("You are not allowed to view links. Register or Login", ""));
                                break;
                            }
                            catch (Exception ex)
                            {
                                attempts++;
                                if (attempts > 2)
                                {
                                    Log("[Infomation: x4] " + result.PassedFileName + " " + ex.Message);
                                    bitrateString = "0 kbps";
                                    break;
                                }
                            }
                        }

                        int bitrate = Int32.Parse(GetKbps(bitrateString));
                        if (bitrate >= 192)
                        {
                            if (bitrate > highestBitrateNum)
                            {
                                double persentage = (GetLength(bitrateString) / result.PassedLength) * 100;
//                                double durationMS = TimeSpan.FromMinutes(getLength(bitrateString)).TotalMilliseconds;
//                                double persentage = (durationMS/result.passedLengthMS)*100;
                                if (persentage >= 85 && persentage <= 115)
                                {
//                                    Log("Length acc: " + string.Format("{0:0.00}", persentage) + "%");
                                    highestBitrateNum = bitrate;
                                    highestBitrateTrack = track;
                                }
                            }
                        }
                    }
                }
            }
            //=======For testing================
//            EditList("Queued", result.passedFileName, result.passedNum, 1);
//            youtubeNum++;
//            totalQuedNum++;
//            var th = new Thread(unused => Search(num));
//            th.Start();
            //==================================

            if (highestBitrateTrack == null)
            {//Youtube
                EditList("Queued", result.PassedFileName, result.PassedNum, 1);
                _youtubeNum++;
                _totalQuedNum++;
                var th = new Thread(unused => Search(num, session));
                th.Start();
            }
            else
            {//MP3Clan
                songArray[session][num].PassedTrack = highestBitrateTrack;
                EditList("Queued", result.PassedFileName, result.PassedNum, 1);
                _mp3ClanNum++;
                _totalQuedNum++;

                var th = new Thread(unused => StartDownloadMp3Clan(num, session));
                th.Start();
            }
        }

        public async void Search(int num, int session, int retry = 0, string videoID = null)
        {
            PassArguments result = songArray[session][num];

            while (_running >= maxRunning)
            {
                Thread.Sleep(500);
            }
            _totalQuedNum--;
            _running++;
            EditList("Loading...", result.PassedFileName, result.PassedNum, 0);

            string url = "";
            var youtubeService = new YouTubeService(new BaseClientService.Initializer()
            {
                ApiKey = ApiKey,
                ApplicationName = this.GetType().ToString()
            });

            var searchListRequest = youtubeService.Search.List("snippet");

            if (retry == 0)
            {
                searchListRequest.Q = result.PassedFileName;
            }
            else if (retry == 1)
            {
                string newName = null;
                try
                {
                    newName = result.PassedSong.Substring(0, result.PassedSong.IndexOf("-")) + " - " + result.PassedArtist;
                }
                catch (Exception ex)
                {
                    newName = result.PassedFileName;
                }
                searchListRequest.Q = newName;
            }
            else if (retry == 2)
            {
                string newName = null;
                try
                {
                    newName = result.PassedSong.Substring(0, result.PassedSong.IndexOf("(")) + " - " + result.PassedArtist;
                }
                catch (Exception ex)
                {
                    newName = result.PassedFileName;
                }
                searchListRequest.Q = newName;
            }
            else if (retry == 3)
            {
                string newName = null;
                try
                {
                    newName = result.PassedSong.Substring(0, result.PassedSong.IndexOf("/")) + " - " + result.PassedArtist;
                }
                catch (Exception ex)
                {
                    newName = result.PassedFileName;
                }
                searchListRequest.Q = newName;
            }
            else if (retry == 4)
            {
                searchListRequest.Q = result.PassedFileName;
            }
            searchListRequest.MaxResults = 5;
            searchListRequest.Order = SearchResource.ListRequest.OrderEnum.Relevance;

            var searchListResponse = await searchListRequest.ExecuteAsync();
            List<string> videos = new List<string>();

            string[] excludeStrings = { "live", "cover" };
            string[] includeStrings = { "hd", "official" };
            string[] includeChannelStrings = { "vevo" };
            double highpersentage = 99999999.0;

            for (int i = 0; i < excludeStrings.Length; i++)
            {
                if (result.PassedFileName.ToLower().Contains(excludeStrings))
                {
                    excludeStrings = excludeStrings.Where(w => w != excludeStrings).ToArray();
                }
            }

            foreach (var word in excludeStrings)
            {
                if ((result.PassedFileName).ToLower().Contains(word))
                {
                    excludeStrings = excludeStrings.Where(str => str != word).ToArray();
                }
            }

            bool keepgoing = true;
            foreach (var searchResult in searchListResponse.Items)
            {
                if (keepgoing)
                {
                    if (searchResult.Id.VideoId != null && "You are not allowed to view links. Register or Login" + searchResult.Id.VideoId != videoID)
                    {
                        if (retry == 4)
                        {
                            Log("[Infomation x13] Downloaded song may be incorrect for " + result.PassedFileName);
                            videos.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.VideoId));
                            url = "You are not allowed to view links. Register or Login" + searchResult.Id.VideoId;
                            keepgoing = false;
                            break;
                        }
                        if (excludeStrings.Any(searchResult.Snippet.Title.ToLower().Contains) ||
                            excludeStrings.Any(searchResult.Snippet.Description.ToLower().Contains))
                        {
//                        MessageBox.Show("ERROR IT CONTAINS BAD STUFF");
                        }
                        else
                        {
                            var searchListRequest2 = youtubeService.Videos.List("contentDetails");
                            searchListRequest2.Id = searchResult.Id.VideoId;
                            var searchListResponse2 = await searchListRequest2.ExecuteAsync();

                            foreach (var searchResult2 in searchListResponse2.Items)
                            {
                                string durationTimeSpan = searchResult2.ContentDetails.Duration;
                                TimeSpan youTubeDuration = XmlConvert.ToTimeSpan(durationTimeSpan);
                                double durationMs = (youTubeDuration).TotalMilliseconds;
                                double persentage = (durationMs/result.PassedLengthMs)*100;

                                if (persentage >= 90 && persentage <= 110)
                                {
                                    double number = Math.Abs(durationMs - result.PassedLengthMs);
                                    if (number < highpersentage)
                                    {
//                                        Log(string.Format("{0:0.00}", persentage) + "% from the original and number is " + number + " | " + searchResult.Id.VideoId);
                                        videos.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.VideoId));
                                        url = "You are not allowed to view links. Register or Login" + searchResult.Id.VideoId;
                                        highpersentage = number;
                                    }

                                    if (includeChannelStrings.Any(searchResult.Snippet.ChannelTitle.ToLower().Contains) || searchResult.Snippet.ChannelTitle.ToLower().Contains(result.PassedArtist.Replace(" ","").ToLower()))
                                    {
//                                        Log("using Official | " + searchResult.Id.VideoId);
                                        videos.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.VideoId));
                                        url = "You are not allowed to view links. Register or Login" + searchResult.Id.VideoId;
                                        keepgoing = false;
                                        break;
                                    }

                                    if (includeStrings.Any(searchResult.Snippet.Description.ToLower().Contains) || includeStrings.Any(searchResult.Snippet.Title.ToLower().Contains))
                                    {
//                                        Log("using Original " + string.Format("{0:0.00}", persentage) + "% from the original| " + searchResult.Id.VideoId);
                                        videos.Add(String.Format("{0} ({1})", searchResult.Snippet.Title, searchResult.Id.VideoId));
                                        url = "You are not allowed to view links. Register or Login" + searchResult.Id.VideoId;
                                        keepgoing = false;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            if (url != "")
            {
                songArray[session][num].PassedURL = url;
            }
            else
            {
                if (retry == 0)
                {
                    _running--;
                    _totalQuedNum++;
                    if (_running < 0)
                    {
                        _running = 0;
                    }
                    Search(num, session, 1);
                    return;
                }
                if (retry == 1)
                {
                    _running--;
                    _totalQuedNum++;
                    if (_running < 0)
                    {
                        _running = 0;
                    }
                    Search(num, session, 2);
                    return;
                }
                if (retry == 2)
                {
                    _running--;
                    _totalQuedNum++;
                    if (_running < 0)
                    {
                        _running = 0;
                    }
                    Search(num, session, 3);
                    return;
                }
                if (retry == 3)
                {
                    _running--;
                    _totalQuedNum++;
                    if (_running < 0)
                    {
                        _running = 0;
                    }
                    Search(num, session, 4);
                    return;
                }
                if (retry == 4)
                {
                    Done(result.PassedFileName, result.PassedNum, "NotFound", 5);//Youtube not found
                    Log("[Error x9] Video not found for: " + result.PassedFileName, true);
                    _running--;
                    if (_running < 0)
                    {
                        _running = 0;
                    }
                    return;
                }
                Log("[Error x10] " + result.PassedFileName, true);
                _running--;
                _totalQuedNum++;
                if (_running < 0)
                {
                    _running = 0;
                }
                return;
            }

            songArray[session][num].YouTubeVideoQuality = youTubeDownloader.GetYouTubeVideoUrls(result.PassedURL);
            if (songArray[session][num].YouTubeVideoQuality == null)
            {
//                Log("Cant download " + result.passedFileName + " because of age restriction on video");
                _running--;
                if (_running < 0)
                {
                    _running = 0;
                }
                _totalQuedNum++;
                Search(num, session, 0, url);
                return;
            }
            YouTubeDownload(num, session);
        }

        void YouTubeDownload(int num, int session)
        {
            PassArguments result = songArray[session][num];
//            while (true)
//            {
                try
                {
                    List<YouTubeVideoQuality> urls = result.YouTubeVideoQuality;
                    YouTubeVideoQuality highestQual = new YouTubeVideoQuality();

                    foreach (var url in urls)
                    {
                        if (url.Extention == "mp4")
                        {
                            highestQual = urls[0];
                            break;
                        }
                    }
                    string Url = "";
                    string saveTo = "";
                    try
      &
You are not allowed to view links. Register or Login

Tags:
Tags:

 


SMF 2.0.19 | SMF © 2016, Simple Machines
Paginas Afiliadas
Twitter - FaceBook - Daraxblog
Designed by Smf Personal