diff --git a/.gitignore b/.gitignore
old mode 100755
new mode 100644
index 7be2372..894a44c
--- a/.gitignore
+++ b/.gitignore
@@ -102,6 +102,3 @@ venv.bak/
# mypy
.mypy_cache/
-
-# keys folder
-keys/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100755
index 7005c2c..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-# https://hub.docker.com/r/library/python/tags/
-image: python:latest
-
-# Change pip's cache directory to be inside the project directory since we can
-# only cache local items.
-variables:
- PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
-
-# Pip's cache doesn't store the python packages
-# https://pip.pypa.io/en/stable/reference/pip_install/#caching
-#
-# If you want to also cache the installed packages, you have to install
-# them in a virtualenv and cache it as well.
-cache:
- paths:
- - .cache/pip
- - venv/
-
-before_script:
- - python -V # Print out python version for debugging
- - apt update && apt upgrade -y
- - apt install -y libchromaprint-tools ffmpeg --install-recommends
- - pip install -r YouTubeMDBot/requirements.txt
-
-test:pylint:
- script:
- - pip install pylint
- - pylint -j 0 --exit-zero --ignored-classes=_socketobject *.py YouTubeMDBot
-
-test:
- script:
- - python -m unittest $(pwd)/YouTubeMDBot/tests/*.py
diff --git a/Design/Database/YouTubeMDBot.drawio b/Design/Database/YouTubeMDBot.drawio
deleted file mode 100644
index 6e7c827..0000000
--- a/Design/Database/YouTubeMDBot.drawio
+++ /dev/null
@@ -1 +0,0 @@
-7V1Nl5s2FP01XjaHb5vlfGSaTdq0c3qarObIINt0AE1AZMb59RVGwraEOwyWkZgqmxjBCNC9enq670nM3Jvs5dcCPG0+oximM8eKX2bu7cxxbNddkP/qkm1TsrCcpmBdJDG9aF9wn/yEtNCipVUSw/LoQoxQipOn48II5TmM8FEZKAr0fHzZCqXHd30CaygU3EcgFUv/TmK8oW/hBPvyTzBZb9id7SBszixB9LguUJXT+80cd7X715zOAKuLvmi5ATF6PihyP87cmwIh3PzKXm5gWrcta7bm7+5OnG2fu4A57vMHle/e383/zLIAeD+j4J8s+eP3X2yXPt0PkFa0Rf4qYUEfGW9ZM+1eFNZV2TP3+nmTYHj/BKL67DMhBinb4Cylp9tX3R3gAj3CG5SiYleVa1muGwTkzCpJ04PyW++jfefX5SjHXdeDNFnnpCwib0we0b1ep6Cs4bfI7whlSUR/1xXc02evHyEFS5het3ixunOUw/YsKmJYcGd29TSMtT1yTJsKFhi+nETBbrElfQaiDOJiSy6hf+Ay3tPuYof0+HlPPptRZnNAvICWAcr3dVv1HnPyg8L+JgrYAgVmTpCS+15X5Me6/kFqoEXkHm2pwBJyO9Jv4esMOabTEV9OgfrfIA5j2SG4vVkngQU8CXpyYHE5DjgdHODBzeOr2uDuG/0A0L6t2dQKY8Eoc01H7oyqIoJ9bBcGxRri1ykuwnHQ4H5He7OyAqYAJz+OH7kLBHqHLyghL3MKbaErN29K/+jQgnP1BFw9NldP0w5CPTtGtC99DklcgSQ5yKCxAkOtQKCdGfAEhDFYG4BlDfbqAfYFgFOQG4SHIuwFvm4IBwLCq6Qo8QOIIkjwMEgPQzpwtOvL8/fts7ka+WyuI8lnm4/tsy1GIgl8SfDXut9/8OnRN2oF6t+3L4cHW3qgiFieIZYEYoXv2/r4hiTnk8QT1cV3RZLAkEQCSU7rj0umNH5KSozqxmhFyOVJEXK4VP1/8GP10589UXuMATay0mCE/ROGQd1UxROVQyHEQJoSPpg4wyU7uw5MEBVG/T0CNkK97hE0tkwTj8DnR/KBHoHPexYX9whEZWMDROmq2KBsWZU6WIF4Hi4tS7QCqxUMoqiDthfp7QtubF+I3X0xbm8fTX3IyfN+pdjtDnbqQ6NF1Id7AWJ3dAkFgrG2x7zB6sZRiZXwePgHWgnPHdtKTFGBeMNQMjckOZ8krDkOSEKgv2J47ZlCzC7mpoZHRp6lBB3Yd1okmPLaiCcRSK/oiSyJ4/SUs7ofmyw5Y9PbWH3WgNNKBUw6sMQBx+vgKO9PyBtwfFFLIC129ZuBW4Z/4WoHtygdTGAIcHsPATqlLU13CDgtPrQi4pcCriDBMoKl0RjPNhS+dQy564uGYmSR0Rd1BzOl7IEkn/WgfErpixlM+pv8/iElX2lw2vO4jusNtPleyLkK/thGX0yD0p8mzEj1oInS5Bg+F8od7BpwEwh3dN9AVBrN9FDWfMHTb74g6pEGbllw875C25nVwS0qhKCKE/SwQkUGsDggmLBiv1xYvmOrjyuymYMA9feKtADeGqwHYj3n53LqE58DUeQriS/3UKJ8/ZAm+aMBWxLYngZgi+oNKB9rC/5A7gBigIGBW5Id9y1POdxTzA9hfobu8zZO0Q0kLUQNxl6IGkxRA2KNZkgyEkmmqAAx18aQZCSSTHENXdA7gGhIIoUkXVlsXADxc+uLmujhuVklnvPheFV1wJatq4sfBqKUZDbJuTAPTmQhq5ucsGjF4fYYCU7NUhVJEM81gFjUlkCBk9KIxbJ2ufE6UgjGBtkRQU6XVWYwHoix6+iHsagbRuhHx86GBuO+W93oNx6LYiGZskBQNgsJDdDDgPb1G5VFwa+AESriJF8bqOXZ7flcPdSibBdXBcAJyg3MklwwHWAWhbeoKjHKTFRP8qQq0MB8iwLaFlW4WppxWuLegzoAPcUlmfPe63YbIVgTOT2UtN/PYuz9fhj9pkWS3tFbQxIpJOnaFEp7kgSGJKOSRNTxJkAS35BkVJJ0Lf/UniS9VwIZkkghyRTTDtm0ypBkJJJMMu2wP0lClSQRZIuhS0/5TUn8cGyaTDHxkMW9dbclLrdCIRhqTFwuyiFslndxmkwy9bC3NWli7Mpowuce8guLe9OEj3ry4tnFaTLWFnqKaKI0Q9WdSxp0PD5ZbfRB53R+YpuHepfUqWomP1V6fmrYN93lcvmpjG8mP3XExZLHJFAfVA07khfNImgJUPPmXQesHQHrsm4dA7Esq25b6rMYQ1G8NNuW9QAz4MFcBCKW425cFk5RYmRu5euOfKjTUjPbGTrf4z/Jao893wvHEhn3W6bvd0n/RusYa8v0sPdKRtWqk8tZk/kwegkJl/yE8+L0mqI4ySY3r9NkoVTDtrldD21r6JJXx+NrGnvRazhFeZI5xdoThZ9PDNWdbG4SuhhbdwqnKE+yebL2NHk/Xo0oT5qNF2VtvOhagiDZMdsZdevFdqtPA/hFAOe8SNUbqzpWV7omv8d+CrbpbiW1CUGcu3Ebv6BHeQDCsUQ90gQgxmVBwL6kqkyvdKwJJlu2xutVd5CS3LiDZ7qDjtWlhXLDxT0G9b4bSWS+yCLBVHA7faqPWDuWKHTiJINicMMMB8Mw1iB+5VgTlBtbZvYYD5R+reUdjQcnPvhrLSHMdw31vYIlJn1bMPsm8nlsBLi01oXqDzY51gQVwtZB6WECdPpM65RNgFEILycYBVxgSr0+yIg7LavQd6cAOqU0VuFcq2BPcBl4f5oozmrgP9MZ8jHmvjTxeaeDnzWeoAmBDWwPLnuqLyhPPzD/VcEFtU971jU1SubgJDWt3hzUK8TZhjbONVV9vzj5Zg7ySsp5HCSHBUL48HLibmw+oxjWV/wL
\ No newline at end of file
diff --git a/Design/Database/YouTubeMDBot.pdf b/Design/Database/YouTubeMDBot.pdf
deleted file mode 100644
index 3754129..0000000
Binary files a/Design/Database/YouTubeMDBot.pdf and /dev/null differ
diff --git a/Design/Database/YouTubeMDBot.png b/Design/Database/YouTubeMDBot.png
deleted file mode 100644
index c003094..0000000
Binary files a/Design/Database/YouTubeMDBot.png and /dev/null differ
diff --git a/Design/Database/YouTubeMDBot.svg b/Design/Database/YouTubeMDBot.svg
deleted file mode 100644
index 95d49cb..0000000
--- a/Design/Database/YouTubeMDBot.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/Design/Database/database_model.mwb b/Design/Database/database_model.mwb
new file mode 100644
index 0000000..03ffdc9
Binary files /dev/null and b/Design/Database/database_model.mwb differ
diff --git a/Design/Database/database_model.mwb.bak b/Design/Database/database_model.mwb.bak
new file mode 100644
index 0000000..ea968e3
Binary files /dev/null and b/Design/Database/database_model.mwb.bak differ
diff --git a/Design/Database/generated_sql_file.sql b/Design/Database/generated_sql_file.sql
new file mode 100644
index 0000000..e65336e
--- /dev/null
+++ b/Design/Database/generated_sql_file.sql
@@ -0,0 +1,251 @@
+-- MySQL Script generated by MySQL Workbench
+-- lun 22 jul 2019 14:28:48 CEST
+-- Model: New Model Version: 1.0
+-- MySQL Workbench Forward Engineering
+
+SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
+SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
+SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
+
+-- -----------------------------------------------------
+-- Schema youtubemd
+-- -----------------------------------------------------
+
+-- -----------------------------------------------------
+-- Schema youtubemd
+-- -----------------------------------------------------
+CREATE SCHEMA IF NOT EXISTS `youtubemd` DEFAULT CHARACTER SET utf8mb4 ;
+SHOW WARNINGS;
+USE `youtubemd` ;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`User`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`User` (
+ `id` INT(64) NOT NULL DEFAULT 0,
+ `name` VARCHAR(45) NULL DEFAULT 'User',
+ `surname` VARCHAR(45) NULL,
+ `username` VARCHAR(45) NULL,
+ `lastSeen` DATETIME NOT NULL,
+ `firstUsage` DATETIME NOT NULL,
+ PRIMARY KEY (`id`))
+ENGINE = InnoDB
+CHECKSUM = 1
+PACK_KEYS = 1;
+
+SHOW WARNINGS;
+CREATE UNIQUE INDEX `id_UNIQUE` ON `youtubemd`.`User` (`id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+CREATE UNIQUE INDEX `username_UNIQUE` ON `youtubemd`.`User` (`username` ASC) VISIBLE;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`Preferences`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`Preferences` (
+ `language` VARCHAR(3) NOT NULL DEFAULT 'en',
+ `audioQuality` ENUM('320k', '256k', '128k') NOT NULL DEFAULT '128k',
+ `audioSampling` ENUM('44000', '48000') NOT NULL DEFAULT '44000',
+ `sendSongLinks` TINYINT NOT NULL DEFAULT 0,
+ `User_id` INT(64) NOT NULL,
+ PRIMARY KEY (`User_id`),
+ CONSTRAINT `fk_Preferences_User`
+ FOREIGN KEY (`User_id`)
+ REFERENCES `youtubemd`.`User` (`id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`Metadata`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`Metadata` (
+ `idMetadata` INT NOT NULL AUTO_INCREMENT,
+ `title` VARCHAR(100) NOT NULL,
+ `artist` VARCHAR(60) NOT NULL,
+ `cover` BLOB NOT NULL,
+ `duration` INT NULL,
+ `customMetadata` TINYINT NOT NULL DEFAULT 0,
+ PRIMARY KEY (`idMetadata`))
+ENGINE = InnoDB
+AUTO_INCREMENT = 0
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`VideoInformation`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`VideoInformation` (
+ `id` VARCHAR(11) NOT NULL,
+ `title` VARCHAR(100) NOT NULL,
+ `channel` VARCHAR(60) NOT NULL,
+ PRIMARY KEY (`id`))
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+CREATE UNIQUE INDEX `id_UNIQUE` ON `youtubemd`.`VideoInformation` (`id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`DownloadInformation`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`DownloadInformation` (
+ `file_id` VARCHAR(50) NOT NULL,
+ `audioQuality` ENUM('320k', '256k', '128k') NOT NULL,
+ `audioSampling` ENUM('44000', '48000') NULL,
+ `Metadata_idMetadata` INT NOT NULL,
+ `VideoInformation_id` VARCHAR(11) NOT NULL,
+ PRIMARY KEY (`file_id`, `Metadata_idMetadata`, `VideoInformation_id`),
+ CONSTRAINT `fk_DownloadInformation_Metadata1`
+ FOREIGN KEY (`Metadata_idMetadata`)
+ REFERENCES `youtubemd`.`Metadata` (`idMetadata`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_DownloadInformation_VideoInformation1`
+ FOREIGN KEY (`VideoInformation_id`)
+ REFERENCES `youtubemd`.`VideoInformation` (`id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+CREATE UNIQUE INDEX `file_id_UNIQUE` ON `youtubemd`.`DownloadInformation` (`file_id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+CREATE INDEX `fk_DownloadInformation_Metadata1_idx` ON `youtubemd`.`DownloadInformation` (`Metadata_idMetadata` ASC) VISIBLE;
+
+SHOW WARNINGS;
+CREATE INDEX `fk_DownloadInformation_VideoInformation1_idx` ON `youtubemd`.`DownloadInformation` (`VideoInformation_id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`History`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`History` (
+ `User_id` INT(64) NOT NULL,
+ `DownloadInformation_file_id` VARCHAR(50) NOT NULL,
+ PRIMARY KEY (`User_id`, `DownloadInformation_file_id`),
+ CONSTRAINT `fk_History_User1`
+ FOREIGN KEY (`User_id`)
+ REFERENCES `youtubemd`.`User` (`id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_History_DownloadInformation1`
+ FOREIGN KEY (`DownloadInformation_file_id`)
+ REFERENCES `youtubemd`.`DownloadInformation` (`file_id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+CREATE INDEX `fk_History_DownloadInformation1_idx` ON `youtubemd`.`History` (`DownloadInformation_file_id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`VideoStatistics`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`VideoStatistics` (
+ `timesRequested` INT NOT NULL DEFAULT 0,
+ `VideoInformation_id` VARCHAR(11) NOT NULL,
+ PRIMARY KEY (`VideoInformation_id`),
+ CONSTRAINT `fk_VideoStatistics_VideoInformation1`
+ FOREIGN KEY (`VideoInformation_id`)
+ REFERENCES `youtubemd`.`VideoInformation` (`id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`DownloadStatistics`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`DownloadStatistics` (
+ `timesRequested` INT NOT NULL DEFAULT 0,
+ `DownloadInformation_file_id` VARCHAR(50) NOT NULL,
+ PRIMARY KEY (`DownloadInformation_file_id`),
+ CONSTRAINT `fk_DownloadStatistics_DownloadInformation1`
+ FOREIGN KEY (`DownloadInformation_file_id`)
+ REFERENCES `youtubemd`.`DownloadInformation` (`file_id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`Playlist`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`Playlist` (
+ `id` VARCHAR(60) NOT NULL,
+ PRIMARY KEY (`id`))
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+CREATE UNIQUE INDEX `id_UNIQUE` ON `youtubemd`.`Playlist` (`id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`Playlist_has_VideoInformation`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`Playlist_has_VideoInformation` (
+ `Playlist_id` VARCHAR(60) NOT NULL,
+ `VideoInformation_id` VARCHAR(11) NOT NULL,
+ PRIMARY KEY (`Playlist_id`, `VideoInformation_id`),
+ CONSTRAINT `fk_Playlist_has_VideoInformation_Playlist1`
+ FOREIGN KEY (`Playlist_id`)
+ REFERENCES `youtubemd`.`Playlist` (`id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Playlist_has_VideoInformation_VideoInformation1`
+ FOREIGN KEY (`VideoInformation_id`)
+ REFERENCES `youtubemd`.`VideoInformation` (`id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+CREATE INDEX `fk_Playlist_has_VideoInformation_VideoInformation1_idx` ON `youtubemd`.`Playlist_has_VideoInformation` (`VideoInformation_id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+CREATE INDEX `fk_Playlist_has_VideoInformation_Playlist1_idx` ON `youtubemd`.`Playlist_has_VideoInformation` (`Playlist_id` ASC) VISIBLE;
+
+SHOW WARNINGS;
+
+-- -----------------------------------------------------
+-- Table `youtubemd`.`PlaylistStatistics`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `youtubemd`.`PlaylistStatistics` (
+ `timesRequested` INT NOT NULL,
+ `Playlist_id` VARCHAR(60) NOT NULL,
+ PRIMARY KEY (`Playlist_id`),
+ CONSTRAINT `fk_PlaylistStatistics_Playlist1`
+ FOREIGN KEY (`Playlist_id`)
+ REFERENCES `youtubemd`.`Playlist` (`id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB
+CHECKSUM = 1;
+
+SHOW WARNINGS;
+
+SET SQL_MODE=@OLD_SQL_MODE;
+SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
+SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
diff --git a/Design/Database/img_database_model.png b/Design/Database/img_database_model.png
new file mode 100644
index 0000000..6046766
Binary files /dev/null and b/Design/Database/img_database_model.png differ
diff --git a/Design/Database/new_database_model.mwb b/Design/Database/new_database_model.mwb
deleted file mode 100644
index 8a3992e..0000000
Binary files a/Design/Database/new_database_model.mwb and /dev/null differ
diff --git a/Design/Database/pdf_database_model.pdf b/Design/Database/pdf_database_model.pdf
new file mode 100644
index 0000000..4cdb445
Binary files /dev/null and b/Design/Database/pdf_database_model.pdf differ
diff --git a/Design/Database/psql_model.sql b/Design/Database/psql_model.sql
deleted file mode 100644
index 7fffb89..0000000
--- a/Design/Database/psql_model.sql
+++ /dev/null
@@ -1,266 +0,0 @@
--- PostgreSQL model for YouTubeMDBot application
--- Created by Javinator9889 - thu, 24 October, 2019
--- Last modification: mon, 4 November, 2019
--- Version 1.1
-
--- DROP schema - only for testing
--- DROP SCHEMA IF EXISTS youtubemd CASCADE;
--- DROP TYPE IF EXISTS AFORMAT;
--- DROP TYPE IF EXISTS aquality;
--- DROP TYPE IF EXISTS behaviour;
-
--- Custom "enum" types
-CREATE TYPE AFORMAT AS ENUM ('mp3', 'm4a', 'ogg');
-CREATE TYPE AQUALITY AS ENUM ('128k', '96k');
-CREATE TYPE BEHAVIOUR AS ENUM ('always', 'not_found', 'ask', 'never');
-
--- Create DB schema
-CREATE SCHEMA IF NOT EXISTS youtubemd;
-
--- ---------------------------------------
--- Table User --
--- ---------------------------------------
-CREATE TABLE IF NOT EXISTS youtubemd.User
-(
- "id" INT PRIMARY KEY NOT NULL,
- "name" VARCHAR(45),
- "tag" VARCHAR(45),
- "lang" VARCHAR(2),
- "first_access" date
-);
-
--- ---------------------------------------------
--- Table Preferences --
--- ---------------------------------------------
-CREATE TABLE IF NOT EXISTS youtubemd.Preferences
-(
- "audio_format" AFORMAT NOT NULL DEFAULT 'm4a',
- "audio_quality" AQUALITY NOT NULL DEFAULT '128k',
- "song_behaviour" BEHAVIOUR NOT NULL DEFAULT 'not_found',
- "send_song_link" BOOLEAN DEFAULT False,
- "user_id" INT NOT NULL,
- PRIMARY KEY ("user_id"),
- CONSTRAINT "fk_user_id"
- FOREIGN KEY ("user_id")
- REFERENCES youtubemd.User ("id")
- ON DELETE CASCADE
- ON UPDATE CASCADE
-);
-
--- ------------------------------------------
--- Table YouTube --
--- ------------------------------------------
-CREATE TABLE IF NOT EXISTS youtubemd.YouTube
-(
- "id" VARCHAR(11) UNIQUE NOT NULL,
- "times_requested" INT NOT NULL DEFAULT 0,
- PRIMARY KEY ("id")
-);
-
--- ------------------------------------------
--- Table Metadata --
--- ------------------------------------------
-CREATE TABLE IF NOT EXISTS youtubemd.Metadata
-(
- "id" BIGSERIAL NOT NULL,
- "artist" VARCHAR(45) NOT NULL,
- "album" VARCHAR(45) NOT NULL,
- "cover" BYTEA NOT NULL,
- "release_id" VARCHAR(36),
- "recording_id" VARCHAR(36),
- "duration" INT,
- "title" VARCHAR(45),
- "custom_metadata" BOOLEAN,
- PRIMARY KEY ("id")
-);
-
--- ----------------------------------------------------
--- Relation between YouTube and Metadata --
--- ----------------------------------------------------
-CREATE TABLE IF NOT EXISTS youtubemd.Video_Has_Metadata
-(
- "id" VARCHAR(11) NOT NULL,
- "metadata_id" INT NOT NULL,
- PRIMARY KEY ("id", "metadata_id"),
- CONSTRAINT "fk_video_id"
- FOREIGN KEY ("id")
- REFERENCES youtubemd.YouTube ("id"),
- CONSTRAINT "fk_metadata_id"
- FOREIGN KEY ("metadata_id")
- REFERENCES youtubemd.Metadata ("id")
-);
-
--- --------------------------------------
--- Table File --
--- --------------------------------------
-CREATE TABLE IF NOT EXISTS youtubemd.File
-(
- "id" VARCHAR(50) UNIQUE NOT NULL,
- "metadata_id" INT UNIQUE NOT NULL,
- "audio_quality" AQUALITY NOT NULL,
- "size" INT,
- PRIMARY KEY ("id", "metadata_id"),
- CONSTRAINT "fk_metadata_id"
- FOREIGN KEY ("metadata_id")
- REFERENCES youtubemd.Metadata ("id")
-);
-
--- -----------------------------------------
--- Table History --
--- -----------------------------------------
-CREATE TABLE IF NOT EXISTS youtubemd.History
-(
- "id" BIGSERIAL NOT NULL,
- "file_id" VARCHAR(50) NOT NULL,
- "user_id" INT NOT NULL,
- "metadata_id" INT NOT NULL,
- "date" date,
- PRIMARY KEY ("id", "file_id", "user_id", "metadata_id"),
- CONSTRAINT "fk_user_id"
- FOREIGN KEY ("user_id")
- REFERENCES youtubemd.User ("id")
- ON DELETE CASCADE
- ON UPDATE CASCADE,
- CONSTRAINT "fk_file_id"
- FOREIGN KEY ("file_id")
- REFERENCES youtubemd.File ("id"),
- CONSTRAINT "fk_metadata_id"
- FOREIGN KEY ("metadata_id")
- REFERENCES youtubemd.Metadata ("id")
-);
-
--- ------------------------------------------
--- Table Playlist --
--- ------------------------------------------
-CREATE TABLE IF NOT EXISTS youtubemd.Playlist
-(
- "id" VARCHAR(22) NOT NULL UNIQUE,
- PRIMARY KEY ("id")
-);
-
--- ----------------------------------------------
--- Table YouTube stats --
--- ----------------------------------------------
-CREATE TABLE IF NOT EXISTS youtubemd.YouTubeStats
-(
- "id" VARCHAR(11) NOT NULL UNIQUE,
- "daily_requests" INT NOT NULL DEFAULT 1,
- "weekly_requests" INT NOT NULL DEFAULT 1,
- "monthly_requests" INT NOT NULL DEFAULT 1,
- PRIMARY KEY ("id"),
- CONSTRAINT "fk_youtube_id"
- FOREIGN KEY ("id")
- REFERENCES youtubemd.YouTube ("id")
-);
-
--- Additional indexes
-CREATE INDEX youtubemd.user_preferences_ix ON youtubemd.Preferences ("user_id");
-CREATE INDEX youtubemd.video_metadata_ix ON youtubemd.Video_Has_Metadata ("id", "metadata_id");
-CREATE INDEX youtubemd.history_ix ON youtubemd.History ("id", "file_id", "user_id", "metadata_id");
-
--- Trigger that updated the different stats
-CREATE FUNCTION youtubemd.process_stats() RETURNS trigger AS
-$$
-DECLARE
- daily_value INT;
- weekly_value INT;
- monthly_value INT;
-BEGIN
- IF (SELECT EXISTS(SELECT 1 FROM youtubemd.YouTubeStats WHERE youtubemd.YouTubeStats.id = NEW.id)) THEN
- SELECT INTO daily_value, weekly_value, monthly_value youtubemd.YouTubeStats.daily_requests,
- youtubemd.YouTubeStats.weekly_requests,
- youtubemd.YouTubeStats.monthly_requests
- FROM youtubemd.YouTubeStats
- WHERE youtubemd.YouTubeStats.id = NEW.id;
- daily_value = daily_value + 1;
- weekly_value = weekly_value + 1;
- monthly_value = monthly_value + 1;
-
- UPDATE youtubemd.YouTubeStats
- SET daily_requests = daily_value,
- weekly_requests = weekly_value,
- monthly_requests = monthly_value
- WHERE id = NEW.id;
- ELSE
- INSERT INTO youtubemd.YouTubeStats (id) VALUES (NEW.id);
- END IF;
- RETURN NEW;
-END;
-$$ LANGUAGE plpgsql;
-
--- Complementary functions with useful operations
-CREATE FUNCTION youtubemd.top_10_daily()
- RETURNS TABLE
- (
- id VARCHAR(11),
- daily_requests INT
- )
-AS
-$$
-BEGIN
- RETURN QUERY SELECT DISTINCT youtubemd.YouTubeStats.id, youtubemd.YouTubeStats.daily_requests
- FROM youtubemd.youtubestats
- ORDER BY daily_requests DESC
- FETCH FIRST 10 ROWS ONLY;
-END;
-$$ LANGUAGE plpgsql;
-
-CREATE FUNCTION youtubemd.top_10_weekly()
- RETURNS TABLE
- (
- id VARCHAR(11),
- weekly_requests INT
- )
-AS
-$$
-BEGIN
- RETURN QUERY SELECT DISTINCT youtubemd.YouTubeStats.id, youtubemd.YouTubeStats.weekly_requests
- FROM youtubemd.youtubestats
- ORDER BY weekly_requests DESC
- FETCH FIRST 10 ROWS ONLY;
-END;
-$$ LANGUAGE plpgsql;
-
-CREATE FUNCTION youtubemd.top_10_monthly()
- RETURNS TABLE
- (
- id VARCHAR(11),
- monthly_requests INT
- )
-AS
-$$
-BEGIN
- RETURN QUERY SELECT DISTINCT youtubemd.YouTubeStats.id, youtubemd.YouTubeStats.monthly_requests
- FROM youtubemd.youtubestats
- ORDER BY monthly_requests DESC
- FETCH FIRST 10 ROWS ONLY;
-END;
-$$ LANGUAGE plpgsql;
-
-CREATE FUNCTION youtubemd.clear_daily_stats() AS
-$$
-BEGIN
- UPDATE youtubemd.YouTubeStats SET daily_requests = 0;
-END;
-$$ LANGUAGE plpgsql;
-
-CREATE FUNCTION youtubemd.clear_weekly_stats() AS
-$$
-BEGIN
- UPDATE youtubemd.YouTubeStats SET weekly_requests = 0;
-END;
-$$ LANGUAGE plpgsql;
-
-CREATE FUNCTION youtubemd.clear_monthly_stats() AS
-$$
-BEGIN
- UPDATE youtubemd.YouTubeStats SET monthly_requests = 0;
-END;
-$$ LANGUAGE plpgsql;
-
--- Init the trigger
-CREATE TRIGGER stats_update
- AFTER INSERT OR UPDATE
- ON youtubemd.YouTube
- FOR EACH ROW
-EXECUTE PROCEDURE youtubemd.process_stats();
diff --git a/Design/Database/vect_database_model.svg b/Design/Database/vect_database_model.svg
new file mode 100644
index 0000000..fe40b7a
--- /dev/null
+++ b/Design/Database/vect_database_model.svg
@@ -0,0 +1,3111 @@
+
+
diff --git a/Design/SRS/Software Requirement Specification.docx b/Design/SRS/Software Requirement Specification.docx
old mode 100755
new mode 100644
diff --git a/Design/SRS/Software Requirement Specification.pdf b/Design/SRS/Software Requirement Specification.pdf
old mode 100755
new mode 100644
diff --git a/Design/logo/logo.png b/Design/logo/logo.png
old mode 100755
new mode 100644
diff --git a/Design/logo/logo.psd b/Design/logo/logo.psd
old mode 100755
new mode 100644
diff --git a/LICENSE b/LICENSE
old mode 100755
new mode 100644
diff --git a/README.md b/README.md
old mode 100755
new mode 100644
diff --git a/YouTubeMDBot/.idea/.gitignore b/YouTubeMDBot/.idea/.gitignore
deleted file mode 100755
index 0e40fe8..0000000
--- a/YouTubeMDBot/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-
-# Default ignored files
-/workspace.xml
\ No newline at end of file
diff --git a/YouTubeMDBot/.idea/YouTubeMDBot.iml b/YouTubeMDBot/.idea/YouTubeMDBot.iml
deleted file mode 100755
index 6711606..0000000
--- a/YouTubeMDBot/.idea/YouTubeMDBot.iml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/YouTubeMDBot/.idea/codeStyles/codeStyleConfig.xml b/YouTubeMDBot/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100755
index a55e7a1..0000000
--- a/YouTubeMDBot/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/YouTubeMDBot/.idea/dictionaries/javinator9889.xml b/YouTubeMDBot/.idea/dictionaries/javinator9889.xml
deleted file mode 100755
index 787b6ce..0000000
--- a/YouTubeMDBot/.idea/dictionaries/javinator9889.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
- acoustid
- ffmpeg
- fpcalc
- javinator
-
-
-
\ No newline at end of file
diff --git a/YouTubeMDBot/.idea/git_toolbox_prj.xml b/YouTubeMDBot/.idea/git_toolbox_prj.xml
deleted file mode 100644
index c7846c0..0000000
--- a/YouTubeMDBot/.idea/git_toolbox_prj.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/YouTubeMDBot/.idea/inspectionProfiles/profiles_settings.xml b/YouTubeMDBot/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100755
index 105ce2d..0000000
--- a/YouTubeMDBot/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/YouTubeMDBot/.idea/misc.xml b/YouTubeMDBot/.idea/misc.xml
deleted file mode 100755
index 8656114..0000000
--- a/YouTubeMDBot/.idea/misc.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/YouTubeMDBot/.idea/modules.xml b/YouTubeMDBot/.idea/modules.xml
deleted file mode 100755
index 48cc268..0000000
--- a/YouTubeMDBot/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/YouTubeMDBot/.idea/vcs.xml b/YouTubeMDBot/.idea/vcs.xml
deleted file mode 100755
index 6c0b863..0000000
--- a/YouTubeMDBot/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/YouTubeMDBot/__init__.py b/YouTubeMDBot/__init__.py
deleted file mode 100755
index 986bc7c..0000000
--- a/YouTubeMDBot/__init__.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from .api import YouTubeAPI
-from .api import YouTubeVideoData
-
-from .audio import FPCalc
-from .audio import FFmpegOGG
-from .audio import FFmpegMP3
-from .audio import FFmpegOpener
-from .audio import ffmpeg_available
-
-from .commands import StartHandler
-
-from .constants import *
-
-from .decorators import restricted
-from .decorators import send_action
-
-from .downloader import YouTubeDownloader
-
-from .errors import EmptyBodyError
-
-from .logging_utils import LoggingHandler
-from .logging_utils import setup_logging
-
-from .metadata import AudioMetadata
-from .metadata import MetadataIdentifier
-from .metadata import YouTubeMetadataIdentifier
-
-from .utils import get_yt_video_id
diff --git a/YouTubeMDBot/__main__.py b/YouTubeMDBot/__main__.py
deleted file mode 100755
index 8a858f1..0000000
--- a/YouTubeMDBot/__main__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
diff --git a/YouTubeMDBot/api/__init__.py b/YouTubeMDBot/api/__init__.py
deleted file mode 100755
index 4ba8496..0000000
--- a/YouTubeMDBot/api/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from ..api.youtube_api import YouTubeAPI
-from ..api.youtube_api import YouTubeVideoData
diff --git a/YouTubeMDBot/api/youtube_api.py b/YouTubeMDBot/api/youtube_api.py
deleted file mode 100755
index d8ff82f..0000000
--- a/YouTubeMDBot/api/youtube_api.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from isodate import parse_duration
-
-from ..constants import YOUTUBE
-from ..errors import EmptyBodyError
-
-
-class YouTubeVideoData:
- """
- Obtains YouTube video data and wraps it inside this class. All fields are direct
- access available, so it is possible to access them directly:
- - title
- - id
- - thumbnail
- - artist
- - duration
- - views
- - likes
- - dislikes
- """
-
- def __init__(self, data: dict, ignore_errors: bool = False):
- """
- By passing a dict with the YouTube data (YouTube API v3), generate and obtain
- the information available from the result.
- :param data: a dictionary with the information obtained from YouTube API.
- :param ignore_errors: whether to ignore or not errors (do not raise exceptions).
- :raises EmptyBodyError when there is no information available and ignored
- errors is False.
- """
- if not data.get("items"):
- raise EmptyBodyError("The data object has no items")
- self.id: str = ""
- self.title: str = ""
- self.thumbnail: str = ""
- self.artist: str = ""
- self.duration: float = 0.0
- self.views: int = 0
- self.likes: int = 0
- self.dislikes: int = 0
- if len(data.get("items")) >= 1:
- content = data.get("items")[0]
- snippet = content.get("snippet")
- details = content.get("contentDetails")
- statistics = content.get("statistics")
- if not snippet and not ignore_errors:
- raise EmptyBodyError("No information available to requested video")
- elif not snippet and ignore_errors:
- snippet_available = False
- else:
- snippet_available = True
- if not details and not ignore_errors:
- raise EmptyBodyError("No video details available")
- elif not details and ignore_errors:
- details_available = False
- else:
- details_available = True
- if not statistics and not ignore_errors:
- raise EmptyBodyError("No statistics available")
- elif not statistics and ignore_errors:
- statistics_available = False
- else:
- statistics_available = True
- c_id = content.get("id", "")
- self.id = c_id.get("videoId", "") if isinstance(c_id, dict) else c_id
- if snippet_available:
- self.title = snippet["title"]
- try:
- self.thumbnail = snippet["thumbnails"]["maxres"]["url"]
- except KeyError:
- try:
- self.thumbnail = snippet["thumbnails"]["high"]["url"]
- except KeyError:
- try:
- self.thumbnail = snippet["thumbnails"]["medium"]["url"]
- except KeyError:
- self.thumbnail = snippet["thumbnails"]["default"]["url"]
- self.artist = snippet["channelTitle"]
- if details_available:
- self.duration = parse_duration(details["duration"]).total_seconds()
- if statistics_available:
- self.views = int(statistics["viewCount"])
- self.likes = int(statistics["likeCount"])
- self.dislikes = int(statistics["dislikeCount"])
-
-
-class YouTubeAPI:
- """
- Wrapper for the YouTube API data. Allows the developer searching for videos and,
- with a given video ID, obtain its data.
- """
-
- def __init__(self):
- from googleapiclient.discovery import build
-
- self.__youtube = build(serviceName=YOUTUBE["api"]["name"],
- version=YOUTUBE["api"]["version"],
- developerKey=YOUTUBE["key"])
-
- def search(self, term: str) -> dict:
- """
- Searchs for a video with the specified term.
- :param term: the search term.
- :return: dict with YouTube data - can be wrapped inside "YouTubeVideoData" class.
- """
- return self.__youtube.search().list(
- q=term,
- type="video",
- part="id,snippet",
- maxResults=1
- ).execute()
-
- @staticmethod
- def video_details(video_id: str) -> YouTubeVideoData:
- """
- Generates a "YouTubeVideoData" object wrapper with the video ID information.
- :param video_id: YouTube video ID.
- :return: YouTubeVideoData object with the available metadata.
- """
- try:
- import ujson as json
- except ImportError:
- import json
- from urllib.request import urlopen
-
- youtube_information = YOUTUBE.copy()
- api_url = youtube_information["endpoint"].format(video_id, YOUTUBE["key"])
- data = urlopen(url=api_url)
- return YouTubeVideoData(data=json.loads(data.read()), ignore_errors=True)
diff --git a/YouTubeMDBot/audio/__init__.py b/YouTubeMDBot/audio/__init__.py
deleted file mode 100755
index 49eab31..0000000
--- a/YouTubeMDBot/audio/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from ..audio.ffmpeg import FFmpegOpener
-from ..audio.ffmpeg import FFmpegOGG
-from ..audio.ffmpeg import FFmpegMP3
-from ..audio.ffmpeg import ffmpeg_available
-from ..audio.fpcalc import FPCalc
diff --git a/YouTubeMDBot/audio/ffmpeg.py b/YouTubeMDBot/audio/ffmpeg.py
deleted file mode 100755
index f94c5ae..0000000
--- a/YouTubeMDBot/audio/ffmpeg.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from abc import ABC
-from abc import abstractmethod
-from typing import List
-from subprocess import PIPE
-from subprocess import Popen
-
-from ..constants import FFMPEG_OPENER
-from ..constants import FFMPEG_CONVERTER
-
-
-def ffmpeg_available() -> bool:
- """
- Checks if "ffmpeg" is installed or not.
- :return: True if installed, else False
- """
- try:
- proc = Popen(["ffmpeg", "-version"],
- stdout=PIPE,
- stderr=PIPE)
- except OSError:
- return False
- else:
- proc.wait()
- return proc.returncode == 0
-
-
-class FFmpeg(ABC):
- """
- Base abstract class for the FFmpeg operators. All classes that works with FFmpeg
- must inherit from this class in order to maintain readability and code optimization.
-
- Allows execution of the ffmpeg command by using the subprocess module. Everything
- is working with PIPEs, so there is no directly discs operations (everything is
- loaded and working with RAM).
- """
-
- def __init__(self, data: bytes, command: List[str] = None):
- """
- Creates the class by passing the data which will be processed and the command (
- by default, None).
- :param data: audio data that will be processed.
- :param command: the ffmpeg command.
- """
- self._data = data
- self.__command = command
- self.__out = None
- self.__err = None
-
- def process(self) -> int:
- """
- Runs the ffmpeg command in a separate process and pipes both stdout and stderr.
- :return: the return code of the operation ('0' if everything is OK, > 0 if not).
- """
- proc = Popen(self.__command, stdout=PIPE, stderr=PIPE, stdin=PIPE)
- self.__out, self.__err = proc.communicate(self._data)
- return proc.returncode
-
- def get_command(self) -> List[str]:
- """
- Get the command for editing.
- :return: List[str] with the command - as this is a pointer, all editions done
- to the list are directly changing the self object.
- """
- return self.__command
-
- def set_command(self, command: List[str]):
- """
- Sets the new list, overriding every old implementation.
- :param command: the new command.
- """
- self.__command = command
-
- def get_output(self) -> bytes:
- """
- Gets the stdout of the process.
- :return: bytes with the command output.
- """
- return self.__out
-
- def get_extra(self) -> bytes:
- """
- Gets the stderr of the process.
- :return: bytes with extra information.
- """
- return self.__err
-
-
-class FFmpegOpener(FFmpeg):
- """
- Opens and produces and audio in PWM mode.
- """
-
- def __init__(self, data: bytes):
- super().__init__(data=data, command=FFMPEG_OPENER.copy())
-
-
-class FFmpegExporter(FFmpeg):
- """
- Base class for the exporter options available in ffmpeg.
- All classes that are developed for converting audio files must inherit from this
- class and implement the "convert" method.
- """
-
- def __init__(self, data: bytes, bitrate: str = None):
- """
- Generates a new instance of the class.
- :param data: the audio data.
- :param bitrate: the new bitrate of the audio data, or None for keeping its
- default.
- """
- super().__init__(data=data, command=FFMPEG_CONVERTER.copy())
- self._bitrate = bitrate
-
- @abstractmethod
- def convert(self) -> int:
- """
- Converts the audio to the desired format.
- :return: the operation result code.
- :raises NotImplementedError when trying to access this method directly on super
- class.
- """
- raise NotImplementedError
-
-
-class FFmpegMP3(FFmpegExporter):
- """
- Exports audio data to MP3 format.
- """
- def convert(self) -> int:
- command = super().get_command()
- if self._bitrate:
- command.append("-b:a")
- command.append(self._bitrate)
- command.append("-acodec")
- command.append("libmp3lame")
- command.append("-f")
- command.append("mp3")
- command.append("-")
- return self.process()
-
-
-class FFmpegOGG(FFmpegExporter):
- """
- Exports audio data to OGG format.
- """
- def convert(self) -> int:
- command = super().get_command()
- if self._bitrate:
- command.append("-b:a")
- command.append(self._bitrate)
- command.append("-c:a")
- command.append("libvorbis")
- command.append("-f")
- command.append("ogg")
- command.append("-")
- return self.process()
diff --git a/YouTubeMDBot/audio/fpcalc.py b/YouTubeMDBot/audio/fpcalc.py
deleted file mode 100755
index 5629b12..0000000
--- a/YouTubeMDBot/audio/fpcalc.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-import re
-from subprocess import PIPE
-from subprocess import Popen
-
-from ..constants import FPCALC
-
-
-def is_fpcalc_available() -> bool:
- """
- Checks if ffmpeg is installed in the system.
- :return: True if available, else False.
- """
- try:
- proc = Popen(["fpcalc", "-v"], stdout=PIPE, stderr=PIPE)
- except OSError:
- return False
- else:
- proc.wait()
-
-
-class FPCalc:
- """
- Calculates audio fingerprint by passing the audio bytes.
- It operates with pipes so no file is created.
- """
- def __init__(self, audio: bytes):
- """
- Creates the FPCalc object.
- :param audio: the audio bytes.
- """
- fpcalc = Popen(FPCALC, stdout=PIPE, stdin=PIPE)
- out, _ = fpcalc.communicate(audio)
- res = out.decode("utf-8")
-
- duration_pattern = "[^=]\\d+\\n"
- fingerprint_pattern = "[^=]*$"
- duration = re.search(duration_pattern, res)
- fingerprint = re.search(fingerprint_pattern, res)
-
- self.__duration: int = int(duration.group(0))
- self.__fp: str = str(fingerprint.group(0))
-
- def duration(self) -> int:
- """
- Obtains the audio duration in seconds.
- :return: duration in seconds.
- """
- return self.__duration
-
- def fingerprint(self) -> str:
- """
- Obtains the audio fingerprint.
- :return: fingerprint in seconds.
- """
- return self.__fp
diff --git a/YouTubeMDBot/bot.py b/YouTubeMDBot/bot.py
deleted file mode 100755
index 10b68d9..0000000
--- a/YouTubeMDBot/bot.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from telegram.ext import Updater
-from telegram.ext import CommandHandler
-
-PROGRAM_ARGS = None
-
-
-def main(args: dict):
- global PROGRAM_ARGS
- PROGRAM_ARGS = args
- updater = Updater(args["token"], workers=args["workers"])
-
- dispatcher = updater.dispatcher
- dispatcher.add_handler(CommandHandler("hello", main))
-
- if args["use_webhook"]:
- updater.start_webhook(listen=args["ip"],
- port=args["port"],
- url_path=args["token"],
- webhook_url=args["public_url"] + '/' + args["token"])
- else:
- updater.start_polling(args["poll_interval"])
- updater.idle()
diff --git a/YouTubeMDBot/commands/StartHandler.py b/YouTubeMDBot/commands/StartHandler.py
deleted file mode 100755
index 8d18c8e..0000000
--- a/YouTubeMDBot/commands/StartHandler.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-class StartHandler(object):
- def __init__(self):
- self._user_data = {}
-
- def start(self, bot, update):
- pass
- # TODO
diff --git a/YouTubeMDBot/commands/__init__.py b/YouTubeMDBot/commands/__init__.py
deleted file mode 100755
index 8a858f1..0000000
--- a/YouTubeMDBot/commands/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
diff --git a/YouTubeMDBot/constants/__init__.py b/YouTubeMDBot/constants/__init__.py
deleted file mode 100755
index 12af4b1..0000000
--- a/YouTubeMDBot/constants/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from ..constants.app_constants import ACOUSTID_KEY
-from ..constants.app_constants import FPCALC
-from ..constants.app_constants import YDL_CLI_OPTIONS
-from ..constants.app_constants import YOUTUBE
-from ..constants.app_constants import PROGRAM_ARGS
-from ..constants.app_constants import FFMPEG_OPENER
-from ..constants.app_constants import FFMPEG_CONVERTER
diff --git a/YouTubeMDBot/constants/app_constants.py b/YouTubeMDBot/constants/app_constants.py
deleted file mode 100755
index 1ef1e70..0000000
--- a/YouTubeMDBot/constants/app_constants.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-import os
-import sys
-
-PROGRAM_ARGS = sys.argv
-# YouTube DL options
-YDL_CLI_OPTIONS = ["youtube-dl", "--format", "bestaudio[ext=m4a]", "--quiet", "--output",
- "-"]
-
-# FPCalc command
-FPCALC = ["fpcalc", "-"]
-
-# API keys
-ACOUSTID_KEY = os.environ["ACOUSTID_KEY"]
-YOUTUBE = {
- "key": os.environ["YOUTUBE_KEY"],
- "api": {
- "name": "youtube",
- "version": "v3"
- },
- "endpoint":
- "https://www.googleapis.com/youtube/v3/videos?"
- "part=id,snippet,contentDetails,statistics&id={0}&key={1}"
-}
-
-# FFmpeg commands
-FFMPEG_OPENER = "ffmpeg -i - -f s16le -".split(" ")
-FFMPEG_CONVERTER = ["ffmpeg", "-i", "-", "-vn", "-map_metadata", "0",
- "-movflags", "use_metadata_tags"]
diff --git a/YouTubeMDBot/decorators/__init__.py b/YouTubeMDBot/decorators/__init__.py
deleted file mode 100755
index 9a87cd2..0000000
--- a/YouTubeMDBot/decorators/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from ..decorators.decorators import restricted
-from ..decorators.decorators import send_action
diff --git a/YouTubeMDBot/decorators/decorators.py b/YouTubeMDBot/decorators/decorators.py
deleted file mode 100755
index b3463c4..0000000
--- a/YouTubeMDBot/decorators/decorators.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from functools import wraps
-
-from ..constants import PROGRAM_ARGS
-
-
-def send_action(action):
- """
- Sends an action while processing a command.
- :param action: the action to be performed.
- :return: itself.
- """
-
- def decorator(func):
- @wraps(func)
- def command_func(update, context, *args, **kwargs):
- context.bot.send_chat_action(chat_id=update.effective_message.chat_id, action=action)
- return func(update, context, *args, **kwargs)
- return command_func
-
- return decorator
-
-
-def restricted(func):
- """
- Restricts a specific function to be accessed from non-authorized users.
- :param func: function to be wrapped.
- :return: itself.
- """
- @wraps(func)
- def wrapped(update, context, *args, **kwargs):
- user_id = update.effective_user.id
- if user_id not in PROGRAM_ARGS["admin"]:
- return
- return func(update, context, *args, **kwargs)
- return wrapped
diff --git a/YouTubeMDBot/downloader/__init__.py b/YouTubeMDBot/downloader/__init__.py
deleted file mode 100755
index 89c3cee..0000000
--- a/YouTubeMDBot/downloader/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from ..downloader.youtube_downloader import YouTubeDownloader
diff --git a/YouTubeMDBot/downloader/youtube_downloader.py b/YouTubeMDBot/downloader/youtube_downloader.py
deleted file mode 100755
index 69c8edb..0000000
--- a/YouTubeMDBot/downloader/youtube_downloader.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from io import BytesIO
-from typing import Tuple
-
-from ..constants.app_constants import YDL_CLI_OPTIONS
-
-
-class YouTubeDownloader:
- """
- Download a YouTube video directly into memory.
- """
- def __init__(self, url: str):
- """
- Creates the YouTubeDownloader object. Call "download" for obtaining the video.
- :param url: the video URL.
- """
- self.__url: str = url
- self.__options: list = YDL_CLI_OPTIONS.copy()
- self.__options.append(self.__url)
-
- def download(self) -> Tuple[BytesIO, bytes]:
- """
- Downloads the YouTube video directly into memory by using pipes.
- :return: a tuple with "BytesIO" and "bytes".
- """
- import subprocess
-
- proc = subprocess.Popen(self.__options,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- stdout, stderr = proc.communicate()
- retcode = proc.returncode
- if retcode == 0:
- return BytesIO(stdout), stdout
- else:
- raise RuntimeError("youtube-dl downloader exception - more info: " +
- str(stderr.decode("utf-8")))
-
- def get_url(self) -> str:
- """
- Obtains the video URL.
- :return: str with the URL.
- """
- return self.__url
diff --git a/YouTubeMDBot/errors/EmptyBodyError.py b/YouTubeMDBot/errors/EmptyBodyError.py
deleted file mode 100755
index a2e8f81..0000000
--- a/YouTubeMDBot/errors/EmptyBodyError.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-class EmptyBodyError(Exception):
- """
- Raises an exception when the body of the json data is empty (e.g.: there is no
- video information)
- """
- pass
diff --git a/YouTubeMDBot/errors/__init__.py b/YouTubeMDBot/errors/__init__.py
deleted file mode 100755
index 98ce0a2..0000000
--- a/YouTubeMDBot/errors/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from ..errors.EmptyBodyError import EmptyBodyError
diff --git a/YouTubeMDBot/logging_utils/__init__.py b/YouTubeMDBot/logging_utils/__init__.py
deleted file mode 100755
index 45c2549..0000000
--- a/YouTubeMDBot/logging_utils/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from ..logging_utils.utils import LoggingHandler
-from ..logging_utils.utils import setup_logging
diff --git a/YouTubeMDBot/logging_utils/utils.py b/YouTubeMDBot/logging_utils/utils.py
deleted file mode 100755
index 3b6ac76..0000000
--- a/YouTubeMDBot/logging_utils/utils.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-import logging
-
-
-def cleanup_old_logs(log_file: str):
- """
- Cleans-up the old log files.
- :param log_file: log filename that must be cleaned.
- """
- import tarfile
- import os
-
- tar_log_filename = log_file + ".tar.gz"
-
- if os.path.exists(log_file):
- if os.path.exists(tar_log_filename):
- os.remove(tar_log_filename)
- with tarfile.open(tar_log_filename, "w:gz") as tar:
- tar.add(log_file, arcname=os.path.basename(log_file))
- tar.close()
- os.remove(log_file)
-
-
-def setup_logging(logger_name: str, log_file: str, level=logging.DEBUG,
- formatter: str = "%(process)d - %(asctime)s | [%(levelname)s]: %(message)s"):
- """
- Creates a new logging which can log to stdout or file.
- :param logger_name: the logger name.
- :param log_file: log filename.
- :param level: logging level.
- :param formatter: the logging formatter.
- :return: the logging file handler.
- """
- from os import path
- from os import makedirs
-
- log_dir = path.dirname(path.abspath(log_file))
- if not path.exists(log_dir):
- makedirs(log_dir)
-
- cleanup_old_logs(log_file)
- new_logging = logging.getLogger(logger_name)
- logging_formatter = logging.Formatter(formatter)
- logging_file_handler = logging.FileHandler(log_file, mode="w")
-
- logging_file_handler.setFormatter(logging_formatter)
-
- new_logging.setLevel(level)
- new_logging.addHandler(logging_file_handler)
-
- return logging_file_handler
-
-
-class LoggingHandler:
- """
- LoggingHandler singleton class that outputs to multiple logging instances.
- """
-
- class __LoggingHandler:
- def __init__(self, logs: list):
- self.__logs = logs
-
- def debug(self, msg):
- for log in self.__logs:
- log.debug(msg)
-
- def info(self, msg):
- for log in self.__logs:
- log.info(msg)
-
- def error(self, msg):
- for log in self.__logs:
- log.error(msg)
-
- def warning(self, msg):
- for log in self.__logs:
- log.warning(msg)
-
- def critical(self, msg):
- for log in self.__logs:
- log.critical(msg)
-
- def get_loggers(self) -> list:
- return self.__logs
-
- __instance = None
-
- def __new__(cls, *args, **kwargs):
- """
- Generates a new instance.
- :param args: not used.
- :param kwargs: "logs" is a list instance that must be provided the first time
- this class is created.
- :return: the LoggingHandler instance.
- """
- if not LoggingHandler.__instance:
- logs = kwargs.get("logs")
- if not logs or len(logs) == 0:
- raise AttributeError(
- "At least kwarg \"log\" (a list of the loggers) must be provided")
- LoggingHandler.__instance = LoggingHandler.__LoggingHandler(logs)
- return LoggingHandler.__instance
-
- def __getattr__(self, item):
- return getattr(self.__instance, item)
-
- def __setattr__(self, key, value):
- return setattr(self.__instance, key, value)
-
- def debug(self, msg):
- """
- Debugs to loggers
- :param msg: message to debug
- """
- self.__instance.debug(msg)
-
- def info(self, msg):
- """
- Info to loggers
- :param msg: message to info
- """
- self.__instance.info(msg)
-
- def error(self, msg):
- """
- Error to loggers
- :param msg: message to error
- """
- self.__instance.error(msg)
-
- def warning(self, msg):
- """
- Warns to loggers
- :param msg: message to warn
- """
- self.__instance.warning(msg)
-
- def critical(self, msg):
- """
- Critical to loggers
- :param msg: message to critical
- """
- self.__instance.critical(msg)
-
- def get_loggers(self) -> list:
- """
- Obtains the list of loggers.
- :return: the list of loggers.
- """
- return self.__instance.get_loggers()
diff --git a/YouTubeMDBot/metadata/AudioMetadata.py b/YouTubeMDBot/metadata/AudioMetadata.py
deleted file mode 100755
index 77754e1..0000000
--- a/YouTubeMDBot/metadata/AudioMetadata.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from mutagen.mp4 import MP4
-from mutagen.mp4 import MP4Cover
-from io import BytesIO
-
-
-class AudioMetadata:
- """
- Wrapper class for setting the audio metadata to the downloaded YouTube video
- object. By using this class, it is possible to set the required information by
- using mutagen without having to remember the metadata keys.
- """
- def __init__(self, audio: BytesIO):
- """
- Generates a new instance.
- :param audio: the audio metadata, in BytesIO, in MP4 format.
- """
- self._audio = MP4(audio)
- self._data = audio
-
- def set_title(self, title: str):
- """
- Sets the audio title.
- :param title: the audio title.
- """
- self._audio[u"\xa9nam"] = title
-
- def set_artist(self, artist: str):
- """
- Sets the audio artist.
- :param artist: the audio artist.
- """
- self._audio[u"\xa9ART"] = artist
-
- def set_album(self, album: str):
- """
- Sets the audio album.
- :param album: the audio album
- """
- self._audio[u"\xa9alb"] = album
-
- def set_extras(self, extras: list):
- """
- Sets the audio extras.
- :param extras: a list of extras that will be added to the audio information.
- """
- self._audio[u"\xa9cmt"] = '; '.join(map(str, extras))
-
- def set_cover(self, cover: bytes):
- """
- Sets the audio cover.
- :param cover: the audio cover.
- """
- mp4_cover = MP4Cover(cover, MP4Cover.FORMAT_JPEG)
- self._audio[u"covr"] = [mp4_cover]
-
- def save(self) -> BytesIO:
- """
- Saves the new metadata into the audio file object.
- :return: the audio file object with the new metadata.
- """
- self._data.seek(0)
- self._audio.save(self._data)
- return self._data
diff --git a/YouTubeMDBot/metadata/MetadataIdentifier.py b/YouTubeMDBot/metadata/MetadataIdentifier.py
deleted file mode 100755
index d3396aa..0000000
--- a/YouTubeMDBot/metadata/MetadataIdentifier.py
+++ /dev/null
@@ -1,175 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-try:
- import ujson as json
-except ImportError:
- import json
-import acoustid
-import musicbrainzngs
-
-from ..audio import FPCalc
-from ..api import YouTubeAPI
-from ..utils import youtube_utils
-from ..constants import ACOUSTID_KEY
-from ..downloader import YouTubeDownloader
-
-
-class MetadataIdentifier:
- """
- Base identifier class. By using the audio data, calculates and generates a
- fingerprint for searching across the MusicBrainz database.
-
- Once the audio has been identified, the available params and information are:
- - audio (bytes)
- - result (json)
- - artist (str)
- - title (str)
- - release_id (str)
- - recording_id (str)
- - score (float)
- - cover (bytes)
- - album (str)
- - duration (int)
- - youtube_data (bool)
- - youtube_id (str)
- """
- def __init__(self, audio: bytes):
- """
- Generates a new instance of the MetadataIdentifier class.
- :param audio: the audio data, in bytes.
- """
- self.audio = audio
- self.result: json = None
- self.artist: str = ""
- self.title: str = ""
- self.release_id: str = ""
- self.recording_id: str = ""
- self.score: float = 0.0
- self.cover: bytes = bytes(0)
- self.album: str = ""
- self.duration: int = 0
- self.youtube_data: bool = False
- self.youtube_id: str = ""
-
- @staticmethod
- def _is_valid_result(data: json) -> bool:
- """
- Checks whether the obtained result, in json, is valid or not, by checking for
- certain keys that must exist.
- :param data: the result in json.
- :return: 'True' if the result is valid, else 'False'.
- """
- if "results" not in data:
- return False
- elif data["status"] != "ok":
- return False
- elif len(data["results"]) == 0:
- return False
- else:
- if "recordings" not in data["results"][0]:
- return False
- else:
- return True
-
- def identify_audio(self) -> bool:
- """
- Tries to identify the audio by using the audio fingerprint. If the audio has
- been successfully identified, then obtains all the data related to it.
- :return: 'True' if the result is valid (the audio was correctly identified),
- else 'False'.
- """
- fingerprint = FPCalc(self.audio)
- data: json = acoustid.lookup(apikey=ACOUSTID_KEY,
- fingerprint=fingerprint.fingerprint(),
- duration=fingerprint.duration(),
- meta="recordings releaseids releasegroups")
- self.result = data
- is_valid = self._is_valid_result(data)
- if is_valid:
- for result in data["results"]:
- if "recordings" not in result:
- break
- self.score = result["score"]
- for recording in result["recordings"]:
- if recording.get("artists"):
- names = [artist["name"] for artist in recording["artists"]]
- self.artist = "; ".join(names)
- else:
- self.artist = "Unknown"
- self.title = recording["title"]
- if recording.get("releasegroups"):
- self.release_id = \
- recording["releasegroups"][0]["releases"][0]["id"]
- self.album = recording["releasegroups"][0]["title"]
- self.cover = musicbrainzngs.get_image_front(self.release_id)
- self.recording_id = recording["id"]
- self.duration = recording["duration"]
- is_valid = True
- break
- break
- return is_valid
-
-
-class YouTubeMetadataIdentifier(MetadataIdentifier):
- """
- Identifies YouTube metadata by using MusicBrainz database and YouTube metadata. If
- the first identification (MusicBrainz) fails, then fallback to YouTube
- identification if the "downloader" was provided.
-
- Once the audio has been identified, the available params and information are:
- - audio (bytes)
- - result (json)
- - artist (str)
- - title (str)
- - release_id (str)
- - recording_id (str)
- - score (float)
- - cover (bytes)
- - album (str)
- - duration (int)
- - youtube_data (bool)
- - youtube_id (str)
-
- If "youtube_data" is True, then only audio, title, artist, duration, cover and
- youtube_id are available.
- """
- def __init__(self, audio: bytes, downloader: YouTubeDownloader = None):
- """
- Generates a new instance of the MetadataIdentifier class.
- :param audio: the audio data, in bytes.
- :param downloader: a downloader object, for obtaining the video information if
- MusicBrainz fails.
- """
- super().__init__(audio)
- self._downloader = downloader
-
- def identify_audio(self) -> bool:
- valid = super().identify_audio()
- if not valid:
- if self._downloader:
- from urllib.request import urlopen
-
- video_id = youtube_utils.get_yt_video_id(self._downloader.get_url())
- video_data = YouTubeAPI.video_details(video_id)
- self.title = video_data.title
- self.artist = video_data.artist
- self.duration = video_data.duration
- self.cover = urlopen(video_data.thumbnail).read()
- self.youtube_id = video_data.id
- self.youtube_data = True
-
- valid = True
- return valid
diff --git a/YouTubeMDBot/metadata/__init__.py b/YouTubeMDBot/metadata/__init__.py
deleted file mode 100755
index 4abadb9..0000000
--- a/YouTubeMDBot/metadata/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from ..metadata.MetadataIdentifier import MetadataIdentifier
-from ..metadata.MetadataIdentifier import YouTubeMetadataIdentifier
-
-from ..metadata.AudioMetadata import AudioMetadata
diff --git a/YouTubeMDBot/requirements.txt b/YouTubeMDBot/requirements.txt
deleted file mode 100755
index e341c34..0000000
--- a/YouTubeMDBot/requirements.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-mutagen
-isodate
-google-api-python-client
-musicbrainzngs
-ujson
-youtube_dl
-pyacoustid
-python-telegram-bot
diff --git a/YouTubeMDBot/tests/converter.py b/YouTubeMDBot/tests/converter.py
deleted file mode 100644
index 17aac27..0000000
--- a/YouTubeMDBot/tests/converter.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import unittest
-import mutagen
-
-from typing import Tuple
-from io import BytesIO
-
-from YouTubeMDBot.tests.tagger import TaggerTest
-from YouTubeMDBot.downloader import YouTubeDownloader
-from YouTubeMDBot.audio import FFmpegMP3
-from YouTubeMDBot.audio import FFmpegOGG
-
-
-class MyTestCase(TaggerTest):
- def find_metadata(self, downloader: YouTubeDownloader) -> Tuple[BytesIO, bytes]:
- io, data = super().find_metadata(downloader)
- io.seek(0)
- mp3 = FFmpegMP3(data=data, bitrate="96k") # downrate
- ogg = FFmpegOGG(data=data, bitrate="256k") # uprate
-
- mp3.convert()
- ogg.convert()
-
- mp3_container = BytesIO(mp3.get_output())
- ogg_container = BytesIO(ogg.get_output())
-
- print(mp3.get_extra().decode("utf-8"))
- print(ogg.get_extra().decode("utf-8"))
-
- print(mutagen.File(mp3_container).pprint())
- print(mutagen.File(ogg_container).pprint())
-
- return io, data
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/YouTubeMDBot/tests/downloader.py b/YouTubeMDBot/tests/downloader.py
deleted file mode 100755
index cb53f74..0000000
--- a/YouTubeMDBot/tests/downloader.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import threading
-import unittest
-from time import sleep
-
-from YouTubeMDBot.downloader import YouTubeDownloader
-
-
-class DownloadTest(unittest.TestCase):
- _elements = 0
- _max = 0
- _lock = threading.Lock()
-
- def test_multithread_download(self):
- yt1 = YouTubeDownloader(url="https://www.youtube.com/watch?v=Inm-N5rLUSI")
- yt2 = YouTubeDownloader(url="https://www.youtube.com/watch?v=-_ZwpOdXXcA")
- yt3 = YouTubeDownloader(url="https://www.youtube.com/watch?v=WOGWZD5iT10")
- yt4 = YouTubeDownloader(url="https://www.youtube.com/watch?v=9HfoNUjw5u8")
- t1 = threading.Thread(target=self.write_to_file, args=(yt1, "v1.m4a",))
- t2 = threading.Thread(target=self.write_to_file, args=(yt2, "v2.m4a",))
- t3 = threading.Thread(target=self.write_to_file, args=(yt3, "v3.m4a",))
- t4 = threading.Thread(target=self.write_to_file, args=(yt4, "v4.m4a",))
-
- self._max = 4
-
- t1.start()
- t2.start()
- t3.start()
- t4.start()
-
- while self._elements < self._max:
- sleep(1)
-
- def barrier(self):
- with self._lock:
- self._elements += 1
-
- def write_to_file(self, yt: YouTubeDownloader, name: str):
- _, data = yt.download()
- print(name + " downloaded")
- with open(name, "wb") as f:
- f.write(data)
- self.barrier()
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/YouTubeMDBot/tests/identifier.py b/YouTubeMDBot/tests/identifier.py
deleted file mode 100755
index 747f4a0..0000000
--- a/YouTubeMDBot/tests/identifier.py
+++ /dev/null
@@ -1,106 +0,0 @@
-import threading
-import unittest
-from pprint import pprint
-from time import sleep
-from time import time
-from typing import Tuple
-from io import BytesIO
-
-from YouTubeMDBot.downloader import YouTubeDownloader
-from YouTubeMDBot.metadata import YouTubeMetadataIdentifier
-
-
-class IdentifierTest(unittest.TestCase):
- lock = threading.Lock()
- threads = 0
- max = 0
- song_info = {}
-
- def test_identification(self):
- url = "https://www.youtube.com/watch?v=YQHsXMglC9A"
- downloader = YouTubeDownloader(url=url)
- audio, data = downloader.download()
- with open("hello.m4a", "wb") as song:
- song.write(data)
- identifier = YouTubeMetadataIdentifier(audio=data, downloader=downloader)
-
- valid = identifier.identify_audio()
- assert valid
- print("{0} by {1} - score: {2} / 1\n"
- "\thttps://musicbrainz.org/recording/{3}\n"
- "\thttps://musicbrainz.org/release/{4}\n\n"
- .format(identifier.title, identifier.artist,
- identifier.score,
- identifier.recording_id, identifier.release_id))
- with open("cover.jpg", "wb") as cover:
- cover.write(identifier.cover)
-
- def test_multiple_download_identification(self):
- yt1 = YouTubeDownloader(url="https://www.youtube.com/watch?v=Inm-N5rLUSI")
- yt2 = YouTubeDownloader(url="https://www.youtube.com/watch?v=-_ZwpOdXXcA")
- yt3 = YouTubeDownloader(url="https://www.youtube.com/watch?v=WOGWZD5iT10")
- yt4 = YouTubeDownloader(url="https://www.youtube.com/watch?v=GfKV9KaNJXc")
- yt5 = YouTubeDownloader(url="https://www.youtube.com/watch?v=DiItGE3eAyQ")
- yt6 = YouTubeDownloader(url="https://www.youtube.com/watch?v=GuZzuQvv7uc")
-
- t1 = threading.Thread(target=self.find_metadata, args=(yt1,))
- t2 = threading.Thread(target=self.find_metadata, args=(yt2,))
- t3 = threading.Thread(target=self.find_metadata, args=(yt3,))
- t4 = threading.Thread(target=self.find_metadata, args=(yt4,))
- t5 = threading.Thread(target=self.find_metadata, args=(yt5,))
- t6 = threading.Thread(target=self.find_metadata, args=(yt6,))
-
- self.max = 6
-
- t1.start()
- t2.start()
- t3.start()
- t4.start()
- t5.start()
- t6.start()
-
- while self.threads < self.max:
- sleep(1)
-
- # pprint(self.song_info)
- pprint("Finished")
-
- def barrier(self):
- with self.lock:
- self.threads += 1
-
- def getThreads(self):
- with self.lock:
- return self.threads
-
- def find_metadata(self, downloader: YouTubeDownloader) -> Tuple[BytesIO, bytes]:
- st_dl_t = time()
- io, data = downloader.download()
- f_dl_t = time()
- print("Downloaded {} - elapsed time: {:.1f}s".format(downloader.get_url(),
- f_dl_t - st_dl_t))
- identifier = YouTubeMetadataIdentifier(audio=data, downloader=downloader)
- valid = identifier.identify_audio()
- assert valid
- self.song_info[downloader.get_url()] = {
- "title": identifier.title,
- "artist": identifier.artist,
- "cover": identifier.cover
- }
- if not identifier.youtube_data:
- self.song_info[downloader.get_url()]["score"] = identifier.score
- self.song_info[downloader.get_url()]["record_id"] = \
- "https://musicbrainz.org/recording/{0}".format(identifier.recording_id)
- self.song_info[downloader.get_url()]["release_id"] = \
- "https://musicbrainz.org/release/{0}".format(identifier.release_id)
- self.song_info[downloader.get_url()]["album"] = identifier.album
- else:
- self.song_info[downloader.get_url()]["duration"] = identifier.duration
- self.song_info[downloader.get_url()]["id"] = identifier.youtube_id
- self.song_info[downloader.get_url()]["youtube_data"] = True
- self.barrier()
- return io, data
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/YouTubeMDBot/tests/song_search.py b/YouTubeMDBot/tests/song_search.py
deleted file mode 100755
index c4afadb..0000000
--- a/YouTubeMDBot/tests/song_search.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import unittest
-
-from YouTubeMDBot.api import YouTubeAPI
-from YouTubeMDBot.api import YouTubeVideoData
-
-
-class TestSearch(unittest.TestCase):
- def test_search(self):
- s = YouTubeAPI()
- search: dict = s.search(term="test")
- data = YouTubeVideoData(data=search, ignore_errors=True)
- print("Title: {0}\n"
- "Artist: {1}\n"
- "Thumbnail: {2}\n"
- "Duration: {3}\n"
- "Views: {4}\n"
- "Likes: {5}\n"
- "Dislikes: {6}\n"
- "Id: {7}".format(data.title, data.artist, data.thumbnail,
- data.duration, data.views, data.likes,
- data.dislikes, data.id))
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/YouTubeMDBot/tests/tagger.py b/YouTubeMDBot/tests/tagger.py
deleted file mode 100755
index 7d8ed02..0000000
--- a/YouTubeMDBot/tests/tagger.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import unittest
-import mutagen
-
-from typing import Tuple
-from io import BytesIO
-
-from YouTubeMDBot.tests.identifier import IdentifierTest
-from YouTubeMDBot.downloader import YouTubeDownloader
-from YouTubeMDBot.metadata import AudioMetadata
-from YouTubeMDBot.utils import youtube_utils
-
-
-class TaggerTest(IdentifierTest):
- def find_metadata(self, downloader: YouTubeDownloader) -> Tuple[BytesIO, bytes]:
- io, data = super().find_metadata(downloader)
- tagger = AudioMetadata(io)
- url = downloader.get_url()
-
- tagger.set_title(super().song_info[url]["title"])
- tagger.set_artist(super().song_info[url]["artist"])
- tagger.set_cover(super().song_info[url]["cover"])
- extra = ["YouTube URL: " + url]
- if not super().song_info[url].get("youtube_data"):
- tagger.set_album(super().song_info[url]["album"])
- extra.append("MusicBrainz Record ID: " + super().song_info[url][
- "record_id"])
- extra.append("MusicBrainz Release ID: " + super().song_info[url][
- "release_id"])
- tagger.set_extras(extra)
- else:
- tagger.set_extras(["YouTube ID: {}".format(super().song_info[url]["id"])])
- yid = youtube_utils.get_yt_video_id(url)
- rs = tagger.save()
- rs.seek(0)
- print(mutagen.File(rs).pprint())
- rs.seek(0)
- with open(yid + ".m4a", "wb") as f:
- f.write(rs.read())
- rs.seek(0)
- return rs, rs.read()
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/YouTubeMDBot/utils/__init__.py b/YouTubeMDBot/utils/__init__.py
deleted file mode 100755
index 9fe1f3e..0000000
--- a/YouTubeMDBot/utils/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-from ..utils.youtube_utils import get_yt_video_id
diff --git a/YouTubeMDBot/utils/youtube_utils.py b/YouTubeMDBot/utils/youtube_utils.py
deleted file mode 100755
index 7d66274..0000000
--- a/YouTubeMDBot/utils/youtube_utils.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# YouTubeMDBot
-# Copyright (C) 2019 - Javinator9889
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-def get_yt_video_id(url: str) -> str:
- # initial version: http://stackoverflow.com/a/7936523/617185 \
- # by Mikhail Kashkin(http://stackoverflow.com/users/85739/mikhail-kashkin)
- """Returns Video_ID extracting from the given url of Youtube
-
- Examples of URLs:
- Valid:
- 'http://youtu.be/_lOT2p_FCvA',
- 'www.youtube.com/watch?v=_lOT2p_FCvA&feature=feedu',
- 'http://www.youtube.com/embed/_lOT2p_FCvA',
- 'http://www.youtube.com/v/_lOT2p_FCvA?version=3&hl=en_US',
- 'https://www.youtube.com/watch?v=rTHlyTphWP0&index=6&list=PLjeDyYvG6-40qawYNR4juzvSOg-ezZ2a6',
- 'youtube.com/watch?v=_lOT2p_FCvA',
-
- Invalid:
- 'youtu.be/watch?v=_lOT2p_FCvA',
- """
-
- from urllib.parse import urlparse
- from urllib.parse import parse_qs
-
- if url.startswith(('youtu', 'www')):
- url = 'http://' + url
-
- query = urlparse(url)
-
- if 'youtube' in query.hostname:
- if query.path == '/watch':
- return parse_qs(query.query)['v'][0]
- elif query.path.startswith(('/embed/', '/v/')):
- return query.path.split('/')[2]
- elif 'youtu.be' in query.hostname:
- return query.path[1:]
- else:
- raise ValueError