diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/AnimeSnap.iml b/.idea/AnimeSnap.iml
new file mode 100644
index 0000000..dee4210
--- /dev/null
+++ b/.idea/AnimeSnap.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..fd3e62a
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..44a3ee0
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..b1f9072
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AnimeSearch.py b/AnimeSearch.py
new file mode 100644
index 0000000..998ecfd
--- /dev/null
+++ b/AnimeSearch.py
@@ -0,0 +1,171 @@
+import sys
+import os
+import json_operations
+import search
+from qfluentwidgets import *
+from qfluentwidgets import FluentIcon as FIF
+from qframelesswindow import *
+
+import qdarktheme
+from PyQt6.QtCore import *
+from PyQt6.QtGui import *
+from PyQt6.QtWidgets import *
+
+
+class App(QWidget):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self.setObjectName("AnimeSnap")
+ self.setGeometry(100, 100, 450, 150)
+
+ self.mainApp = parent
+
+ # Create a stacked widget to manage different screens
+ self.stacked_widget = QStackedWidget(self)
+
+ # Create the main menu widget
+ self.main_menu_widget = QWidget(self)
+ self.stacked_widget.addWidget(self.main_menu_widget)
+
+ self.img_path = ""
+ self.img_url = ""
+ self.anilist_id = ""
+ self.ctheme = "dark"
+
+ layout = QVBoxLayout(self)
+ layout.addStretch()
+ self.setLayout(layout)
+
+ self.buttons_frame = QWidget(self)
+ layout.addWidget(self.buttons_frame)
+
+ # Create a horizontal layout
+ top_layout = QHBoxLayout()
+ button_layout = QHBoxLayout()
+ checkbox_layout = QHBoxLayout()
+
+ layout.addLayout(top_layout)
+
+ layout.addWidget(QLabel(""))
+
+ self.img_url_entry = LineEdit(self)
+ self.img_url_entry.setPlaceholderText("Enter image URL or Upload an image")
+
+ self.file_path_label = CaptionLabel()
+
+ button_layout.addWidget(self.img_url_entry)
+
+ open_image_icon = QIcon("icons/folder.png")
+ self.open_image_button = QPushButton(self)
+ self.open_image_button.setStyleSheet("""
+ QPushButton {
+ border: none;
+ }
+ """)
+ self.open_image_button.clicked.connect(self.open_image)
+ self.open_image_button.setIcon(open_image_icon)
+ self.open_image_button.setIconSize(QSize(23, 23))
+ self.open_image_button.setFixedSize(28, 28)
+ button_layout.addWidget(self.open_image_button)
+
+ layout.addLayout(button_layout) # Add the button layout to the main layout
+ layout.addWidget(self.file_path_label)
+
+ layout.addWidget(QLabel(""))
+
+ layout.addLayout(checkbox_layout) # Add the checkbox layout to the main layout
+
+ self.checkbox_rbb = CheckBox()
+ self.checkbox_rbb.setText("Remove Black Borders")
+ checkbox_layout.addWidget(self.checkbox_rbb)
+
+ self.checkbox_iad = CheckBox()
+ self.checkbox_iad.setText("Include All Details")
+ checkbox_layout.addWidget(self.checkbox_iad)
+
+ layout.addWidget(QLabel(""))
+
+ anilist_label = CaptionLabel()
+ anilist_label.setText("Anilist Anime ID [OPTIONAL] [https://anilist.co/]")
+ layout.addWidget(anilist_label)
+
+ self.anilist_entry = LineEdit(self)
+ self.anilist_entry.setPlaceholderText(
+ "Use this if you know what anime it is and you just need the scene details"
+ )
+ layout.addWidget(self.anilist_entry)
+
+ search_button = PrimaryPushButton()
+ search_button.setText("Search")
+ search_button.clicked.connect(self.onClickSearch)
+ layout.addWidget(search_button)
+
+ def open_image(self):
+ # options = QFileDialog.Options()
+ file_path, _ = QFileDialog.getOpenFileName(
+ self,
+ "Open Image to Search",
+ "",
+ "Image Files (*.jpg *.png *.bmp);;All Files (*)",
+ )
+
+ if file_path:
+ self.img_path = file_path
+ self.file_path_label.setText(file_path)
+
+ def returnToMenu(self):
+ self.stacked_widget.setCurrentWidget(self.main_menu_widget)
+ self.img_path = ""
+ self.file_path_label.setText("") # Use self.file_path_label instead of self.main_menu_widget.file_path_label
+
+ def onClickSearch(self):
+ self.img_url = self.img_url_entry.text()
+ anilist_id = self.anilist_entry.text()
+ iad_status = self.checkbox_iad.isChecked()
+ rbb_status = self.checkbox_rbb.isChecked()
+ anilist_status = anilist_id if anilist_id else None
+
+ if self.img_url or self.img_path != "":
+ while self.layout().count():
+ item = self.layout().takeAt(0)
+ widget = item.widget()
+ if widget is not None:
+ widget.deleteLater()
+
+ if self.checkbox_iad.isChecked():
+ iad_status = True
+ else:
+ iad_status = False
+
+ if self.checkbox_rbb.isChecked():
+ rbb_status = True
+ else:
+ rbb_status = False
+
+ if anilist_id == "":
+ anilist_status = None
+ else:
+ anilist_status = anilist_id
+
+ if self.img_url == "":
+ search.search_img(self, anilist_info=anilist_status, rbb=rbb_status)
+ else:
+ search.search_url(self)
+
+ if iad_status is True:
+ json_operations.json_to_tabular(self, include_all_details=True)
+ else:
+ json_operations.json_to_tabular(self, include_all_details=False)
+
+ else:
+ dlg = QMessageBox(self)
+ dlg.setWindowTitle("Error")
+ dlg.setText("Select any Image or Enter any URL to search")
+ dlg.setStandardButtons(QMessageBox.StandardButton.Ok)
+ button = dlg.exec()
+
+ if button == QMessageBox.StandardButton.Ok:
+ pass
+ else:
+ pass
diff --git a/FUNDING.yml b/FUNDING.yml
new file mode 100644
index 0000000..438aa70
--- /dev/null
+++ b/FUNDING.yml
@@ -0,0 +1,13 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: 'https://ko-fi.com/rohankishore'
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+custom:
diff --git a/README.md b/README.md
index a4c9240..04bef31 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,9 @@
+
+
+![icon](https://github.com/user-attachments/assets/e7244321-0452-4a9d-b24d-1b70eedb16e7)
+
+
+
âŠī¸ AnimeSnap đĨ
@@ -30,7 +36,7 @@ And much more if you check `Include All Details`. It will also show multiple res
### Using the App
-![image](https://github.com/rohankishore/AnimeSnap/assets/109947257/17b36284-4231-4747-b047-185e892b9252)
+![image](https://github.com/user-attachments/assets/fc462b43-856f-410c-b998-f6841e2fd7df)
You can either use an image link or upload an image from your PC. With v2.0, there are 2 new checkboxes. Removing black bars will help to increase the accuracy of the overall results if there are any black borders in your image, though the processing time may slightly increase. By checking `Include All Details`, you can get all the info that you get by uploading to JSON.
diff --git a/icons/icon.png b/icons/icon.png
new file mode 100644
index 0000000..a013eb8
Binary files /dev/null and b/icons/icon.png differ
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..5697f5d
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,10 @@
+certifi==2023.7.22
+charset-normalizer==3.3.0
+darkdetect==0.7.1
+idna==3.4
+PyQt6==6.5.3
+PyQt6-Qt6==6.5.3
+PyQt6-sip==13.6.0
+pyqtdarktheme==2.1.0
+requests==2.31.0
+urllib3==2.0.6
diff --git a/src/json_operations.py b/src/json_operations.py
new file mode 100644
index 0000000..40b93aa
--- /dev/null
+++ b/src/json_operations.py
@@ -0,0 +1,58 @@
+import json
+from PyQt6.QtWidgets import QFileDialog, QMessageBox
+
+
+def json_to_tabular(self, include_all_details=False):
+ # Accessing information in the JSON data
+ result = self.a["result"]
+ formatted_data = ""
+ for entry in result:
+ anilist = entry["anilist"]
+ filename = entry["filename"]
+ episode = entry["episode"]
+ from_time = entry["from"]
+ to_time = entry["to"]
+ similarity = entry["similarity"] * 100
+ video_url = entry["video"]
+ image_url = entry["image"]
+
+ # Format the data with labels and insert into the QTextEdit widget
+
+ formatted_data += (
+ f"Filename: {filename}\n"
+ f"Episode: {episode}\n"
+ f"From: {from_time}\n"
+ f"To: {to_time}\n"
+ f"Similarity: {similarity:.2f}%\n\n"
+ )
+ if include_all_details:
+ formatted_data += (
+ f"Anilist: {anilist}\n"
+ f"Video URL: {video_url}\n"
+ f"Image URL: {image_url}\n"
+ )
+
+ self.textbox.setPlainText(formatted_data)
+
+
+def write_to_json(self):
+ file_path, _ = QFileDialog.getSaveFileName(
+ self,
+ "Save JSON File",
+ "",
+ "JSON Files (*.json);;All Files (*)",
+ )
+
+ if file_path:
+ try:
+ # Open the selected file in write mode and write the JSON data
+ with open(file_path, "w") as file:
+ json.dump(self.a, file)
+
+ # Inform the user that the file has been saved
+ QMessageBox.information(
+ self, "File Saved", "The file has been saved successfully."
+ )
+ except FileNotFoundError or PermissionError as e:
+ # Handle any exceptions that may occur
+ QMessageBox.critical(self, "Error", f"An error occurred: {str(e)}")
diff --git a/src/main.py b/src/main.py
new file mode 100644
index 0000000..d3dc488
--- /dev/null
+++ b/src/main.py
@@ -0,0 +1,77 @@
+from PyQt6.QtCore import *
+from PyQt6.QtGui import *
+from PyQt6.QtWidgets import *
+from qfluentwidgets import *
+from qfluentwidgets import FluentIcon as FIF
+from qframelesswindow import *
+
+import AnimeSearch
+
+
+class Window(MSFluentWindow):
+ """ Main window class. Uses MSFLuentWindow to imitate the Windows 11 FLuent Design windows. """
+
+ def __init__(self):
+ # self.isMicaEnabled = False
+ super().__init__()
+ #self.setTitleBar(CustomTitleBar(self))
+ #self.tabBar = self.titleBar.tabBar # type: TabBar
+
+ setTheme(Theme.DARK)
+
+
+ # create sub interface
+ self.homeInterface = AnimeSearch.App(self)
+ # self.settingInterface = Settings()
+ # self.settingInterface.setObjectName("markdownInterface")
+
+
+ self.initNavigation()
+ self.initWindow()
+
+ def initNavigation(self):
+ self.addSubInterface(self.homeInterface, FIF.SEARCH, "Find Anime", FIF.SEARCH, NavigationItemPosition.TOP)
+ # self.addSubInterface(self.settingInterface, FIF.SETTING, 'Settings', FIF.SETTING, NavigationItemPosition.BOTTOM)
+ self.navigationInterface.addItem(
+ routeKey='Help',
+ icon=FIF.INFO,
+ text='About',
+ onClick=self.showMessageBox,
+ selectable=False,
+ position=NavigationItemPosition.BOTTOM)
+
+ self.navigationInterface.setCurrentItem(
+ self.homeInterface.objectName())
+
+ def initWindow(self):
+ self.resize(500, 160)
+ self.setWindowIcon(QIcon('icons/icon.png'))
+ self.setWindowTitle('AnimeSnap')
+
+ w, h = 1200, 800
+ self.move(w // 2 - self.width() // 2, h // 2 - self.height() // 2)
+
+ def showMessageBox(self):
+ w = MessageBox(
+ 'AnimeSnap đ¯',
+ (
+ "Version : 3.0"
+ + "\n" + "\n" + "\n" + "đ I hope you'll enjoy using AnimeSnap as much as I did while coding it đ" + "\n" + "\n" + "\n" +
+ "Made with đ By Rohan Kishore"
+ ),
+ self
+ )
+ w.yesButton.setText('GitHub')
+ w.cancelButton.setText('Return')
+
+ if w.exec():
+ QDesktopServices.openUrl(QUrl("https://github.com/rohankishore/AnimeSnap"))
+
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ #qdarktheme.setup_theme("dark")
+ window = Window()
+ window.show()
+ sys.exit(app.exec())
diff --git a/src/search.py b/src/search.py
new file mode 100644
index 0000000..972182e
--- /dev/null
+++ b/src/search.py
@@ -0,0 +1,84 @@
+import urllib
+
+import requests
+from PyQt6.QtWidgets import QVBoxLayout, QTextEdit, QPushButton, QWidget
+
+import json_operations
+
+
+def search_img(self, anilist_info=None, rbb=False):
+ self.setGeometry(100, 100, 750, 800)
+ self.a = ""
+
+ if anilist_info is None:
+ self.a = requests.post(
+ "https://api.trace.moe/search",
+ files={"image": open(f"{self.img_path}", "rb")},
+ ).json()
+ else:
+ if rbb is False:
+ url = f"https://api.trace.moe/search?anilistID={anilist_info}&url=" + "{}"
+ self.a = requests.get(
+ url.format(urllib.parse.quote_plus(f"{self.img_path}"))
+ ).json()
+ else:
+ url = f"https://api.trace.moe/search?anilistID={anilist_info}&url=" + "{}"
+ self.a = requests.get(
+ url.format(urllib.parse.quote_plus(f"{self.img_path}"))
+ ).json()
+
+ self.textbox = QTextEdit()
+ self.textbox.setPlainText(str(self.a) + "\n" + "\n")
+ self.textbox.setReadOnly(True)
+
+ layout = QVBoxLayout()
+ layout.addWidget(self.textbox)
+
+ json_button = QPushButton("Write to JSON")
+ json_button.clicked.connect(lambda: json_operations.write_to_json(self))
+ layout.addWidget(json_button)
+
+ another_image_button = QPushButton("Search Another Image")
+ another_image_button.clicked.connect(self.returnToMenu)
+ layout.addWidget(another_image_button)
+
+ central_widget = QWidget()
+ central_widget.setLayout(layout)
+ self.setCentralWidget(central_widget)
+
+
+def search_url(self, anilist_info=None, rbb=False):
+ self.setGeometry(100, 100, 750, 800)
+ self.a = ""
+ if anilist_info is None:
+ self.a = requests.post(
+ "https://api.trace.moe/search",
+ files={"image": open(f"{self.img_url}", "rb")},
+ ).json()
+ else:
+ if rbb is False:
+ url = f"https://api.trace.moe/search?anilistID={anilist_info}&url=" + "{}"
+ self.a = requests.get(
+ url.format(urllib.parse.quote_plus(f"{self.img_url}"))
+ ).json()
+ else:
+ url = (
+ "https://api.trace.moe/search?anilistID"
+ + str(anilist_info)
+ + "&url="
+ + "{}"
+ )
+ self.a = requests.get(
+ url.format(urllib.parse.quote_plus(f"{self.img_url}"))
+ ).json()
+
+ self.textbox = QTextEdit()
+ self.textbox.setPlainText(str(self.a) + "\n" + "\n")
+ self.textbox.setReadOnly(True)
+
+ layout = QVBoxLayout()
+ layout.addWidget(self.textbox)
+
+ central_widget = QWidget()
+ central_widget.setLayout(layout)
+ self.setCentralWidget(central_widget)