Skip to content

Commit

Permalink
Merge branch 'putevents'
Browse files Browse the repository at this point in the history
  • Loading branch information
faridrasidov committed Oct 11, 2024
2 parents adbb766 + dfeb2b8 commit d97b972
Show file tree
Hide file tree
Showing 5 changed files with 449 additions and 45 deletions.
51 changes: 37 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<div align="center">
<h1>SoundCld</h1>
<p>
Python Api Handler For The Internal V2 SoundCloud API. Does Not Require An API Key.
Python API handler for the SoundCloud Internal V2 API,
allowing interaction without an API key.
</p>

<!-- Badges -->
Expand All @@ -13,21 +14,31 @@
<a href="https://github.com/faridrasidov/soundcld"><img src="https://img.shields.io/github/forks/faridrasidov/soundcld?style=social" alt="forks - soundcld"></a>
</div>

****
**Installation:**
## Table of Contents
- [Installation](#installation)
- [Usage](#usage)
- [Specifications](#specifications)
- [Authentication](#authentication)
- [License](#license)


<a name="installation"></a>
## Installation:
**Global Installation**
```shell
# For Global
$ git clone https://github.com/faridrasidov/soundcld
$ cd soundcld
$ pip install .
```
**Virtual Environment**
```shell
# For Venv
$ git clone https://github.com/faridrasidov/soundcld
$ cd soundcld
$ path/to/your/venv/pip install .
```
**Example Of Usage:**

<a name="usage"></a>
## Usage
```python
from soundcld import SoundCloud

Expand All @@ -38,17 +49,24 @@ for item in search:
print(item.permalink, item.kind)
```

**Specifications:**
<a name="specifications"></a>
## Specifications

- **You Can Change Your Profile Info**
- **45 Get Api Requests Has Been Handled.(Some Of Them Require Auth)**
- **7 Put Api Requests hs Been Handled. (All Of Them Require Auth)**
- **1 Post Api Requests hs Been Handled. (All Of Them Require Auth)**
- **2 Delete Api Requests hs Been Handled. (All Of Them Require Auth)**
- **Last Valid Generated ID's Automatically Added To 'data.json' File To improve Api Speed.**
- **46 Get Api Requests Has Been Handled.(Some Of Them Require Auth)**
- **You Can Change Your Profile Info Too**
****


<a name="authentication"></a>
## Authentication
**Notes about `auth`:**

**Some methods require authentication. If you want to use them, you should get the values
written at the bottom from your cookies and put them in a package folder ("soundcloud")
named cookies.json. You will also need to change your "client_id" in data.json in that folder.**
written at the bottom from your cookies and put them in file which is in package folder ("soundcloud")
named cookies.json. You will also need to change your "client_id" in data.json file in that folder.**

**Save Them Into:**

Expand All @@ -59,7 +77,8 @@ named cookies.json. You will also need to change your "client_id" in data.json i
{
"moe_uuid": "<moe_uuid>",
"oauth_token": "<oauth_token>",
"sc_anonymous_id": "<sc_anonymous_id>"
"sc_anonymous_id": "<sc_anonymous_id>",
"datadome": "<datadome>"
}
```

Expand All @@ -70,4 +89,8 @@ named cookies.json. You will also need to change your "client_id" in data.json i
"client_id": "<client_id>",
"app_version": "<app_version>"
}
```
```

<a name="license"></a>
## License
`Soundcld` source code is licensed under the terms of the Boost Software License. See [LICENSE](https://github.com/faridrasidov/soundcld/blob/master/LICENCE.txt) for more information.
175 changes: 173 additions & 2 deletions soundcld/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""
SoundCld Is Soundcloud-v2 api handler
"""
from typing import List
from datetime import datetime
from typing import List, Union

import soundcld.resource
from .api_handler import BaseSound
Expand Down Expand Up @@ -671,4 +672,174 @@ def change_my_profile_info(
for item, value in payload.items():
if not value:
payload[item] = last_info[item]
return self._put_payload(link, payload)
return self._put_payload(link, **payload)

def like_track(self, track_id: int):
"""
Likes The Track by Me {Logged-In User}.
"""
link = f'/users/{self.my_account_id}/track_likes/{track_id}'
return self._put_payload(link)

def like_playlist(self, playlist_id: int):
"""
Likes The Playlist or Album by Me {Logged-In User}.
"""
link = f'/users/{self.my_account_id}/playlist_likes/{playlist_id}'
return self._put_payload(link)

def dislike_track(self, track_id: int):
"""
Dislikes The Track by Me {Logged-In User}.
"""
link = f'/users/{self.my_account_id}/track_likes/{track_id}'
return self._delete_payload(link)

def dislike_playlist(self, playlist_id: int):
"""
Dislikes The Playlist or Album by Me {Logged-In User}.
"""
link = f'/users/{self.my_account_id}/playlist_likes/{playlist_id}'
return self._delete_payload(link)

def create_playlist(
self,
playlist_name: str,
trak_id: Union[int, List[int]],
is_public: bool = True
):
"""
Creates The Playlist by Me {Logged-In User}.
"""
link = f'/playlists'
payload = {
'playlist': {
'title': playlist_name,
'tracks': [trak_id],
'_resource_id': 'f-',
'_resource_type': 'playlist'
}
}
if is_public:
payload['sharing'] = 'public'
else:
payload['sharing'] = 'private'
return self._post_payload(link, **payload)

def delete_playlist(self, playlist_id: int):
"""
Removes The Playlist by Me {Logged-In User}.
"""
link = f'/playlists/{playlist_id}'
return self._delete_payload(link)

def add_track_to_playlist(
self,
playlist_id: int,
track_id: Union[int, List[int]]
):
"""
Adds Track Or List Of Tracks To The Playlist by Me {Logged-In User}.
"""
link = f'/playlists/{playlist_id}'
temp_playlist = self.get_playlist(playlist_id)
temp_tracks = []
for item in temp_playlist.tracks:
temp_tracks.append(item.id)
if isinstance(track_id, int):
temp_tracks.append(track_id)
else:
temp_tracks.extend(track_id)
payload = {
'playlist': {
'tracks': temp_tracks
}
}
return self._put_payload(link, **payload)

def remove_track_from_playlist(
self,
playlist_id: int,
track_id: Union[int, List[int]]
):
"""
Removes Track Or List Of Tracks To The Playlist by Me {Logged-In User}.
"""
link = f'/playlists/{playlist_id}'
temp_playlist = self.get_playlist(playlist_id)
temp_tracks = []
for item in temp_playlist.tracks:
temp_tracks.append(item.id)
if isinstance(track_id, int):
temp_tracks.remove(track_id)
else:
for item in track_id:
temp_tracks.remove(item)
payload = {
'playlist': {
'tracks': temp_tracks
}
}
return self._put_payload(link, **payload)

def edit_playlist_info(
self,
playlist_id: int,
title: str = None,
description: str = None,
playlist_type: str = None,
release_date: str = None,
genre: str = None,
tag: str = None,
permalink: str = None
):
"""
Changes The Playlist Info by Me {Logged-In User}.
:param playlist_id: The ID Of Playlist
:param title: Title Of Playlist
:param description: Description Of Playlist
:param playlist_type: Must Be One Of These [playlist, album, compilation, single, ep]
:param release_date: Must Be In "year-month-day" Format
:param genre: Genre Of Playlist
:param tag: Tag or Tags Of Playlist
:param permalink: The Link Name Of Playlist
"""
link = f'/playlists/{playlist_id}'
playlist_temp_data = self.get_playlist(playlist_id)
payload = {
'title': title,
'description': description,
'kind': playlist_type,
'release_date': release_date,
'genre': genre,
'tag_list': tag,
'permalink': permalink
}
temp_dict = {}
for item, value in playlist_temp_data.items():
temp_dict[item] = value
for item, value in payload.items():
if value:
temp_dict[item] = value
if item == 'permalink':
temp_link = playlist_temp_data['permalink_url'].split('/')
temp_link[-1] = temp_dict[item]
temp_dict['permalink_url'] = '/'.join(temp_link)
elif item == 'kind':
if not (temp_dict['release_date'] or payload['release_date']):
print('release_date not added')
return
if value != 'playlist':
temp_dict['set_type'] = value
else:
temp_dict['set_type'] = None
some_arr = []
for item in playlist_temp_data.tracks:
some_arr.append(item.id)
now = datetime.utcnow()
temp_dict['last_modified'] = f'{now.isoformat().split(".")[0]}Z'
temp_dict['tracks'] = some_arr
temp_dict['_resource_id'] = playlist_id
temp_dict['_resource_type'] = playlist_temp_data.kind
return self._put_payload(link, **temp_dict)
55 changes: 46 additions & 9 deletions soundcld/api_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import re
from dataclasses import dataclass
from datetime import datetime
from functools import wraps
from typing import List, Union, Iterator

import requests
Expand All @@ -15,7 +16,9 @@
GetReq,
ListGetReq,
CollectionGetReq,
PutReq
PutReq,
DeleteReq,
PostReq
)
from soundcld.resource import (
SearchItem, Like, RepostItem, StreamItem,
Expand All @@ -32,7 +35,16 @@
confDirectory = scriptDirectory + '/data.json'
cookieDirectory = scriptDirectory + '/cookies.json'
headerDirectory = scriptDirectory + '/headers.json'
infoDirectory = scriptDirectory + '/run_data.json'


def update_cookies_after(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
result = func(self, *args, **kwargs)
self._update_cookies()
return result

return wrapper


@dataclass
Expand Down Expand Up @@ -88,6 +100,16 @@ def __set_conf_last(self) -> None:
with open(confDirectory, 'w', encoding='utf-8') as file:
json.dump(config, file, indent=4)

def _update_cookies(self):
cookie = {
'moe_uuid': self.cookies['moe_uuid'],
'oauth_token': self.cookies['oauth_token'],
'sc_anonymous_id': self.cookies['sc_anonymous_id'],
'datadome': self.cookies['datadome']
}
with open(cookieDirectory, 'w', encoding='utf-8') as file:
json.dump(cookie, file, indent=4)

def __get_cookies(self) -> None:
if os.path.exists(cookieDirectory):
with open(cookieDirectory, 'r', encoding='utf-8') as file:
Expand Down Expand Up @@ -182,9 +204,22 @@ def _get_conversation_messages(self, req: str, **param) -> Union[Iterator[Messag
def _get_web_profile_list(self, req: str) -> List[WebProfile]:
return ListGetReq[WebProfile](self, req, WebProfile)()

def _put_payload(self, req: str, payload: dict) -> bool:
@update_cookies_after
def _post_payload(self, req: str, **payload: dict) -> bool:
if self.is_logged_in():
return PostReq(self, req)(**payload)
return False

@update_cookies_after
def _put_payload(self, req: str, **payload: dict) -> bool:
if self.is_logged_in():
return PutReq(self, req)(payload)
return PutReq(self, req)(**payload)
return False

@update_cookies_after
def _delete_payload(self, req: str, **payload: dict) -> bool:
if self.is_logged_in():
return DeleteReq(self, req)(**payload)
return False

def generate_client_id(self) -> None:
Expand Down Expand Up @@ -244,7 +279,7 @@ def is_logged_in(self) -> bool:
"""
if self.cookies:
if all(self.cookies.values()):
if os.path.exists(infoDirectory):
if os.path.exists(confDirectory):
time_diff = self.__valid_time_diff()
if 0 < time_diff < 60:
self.__save_validate_time()
Expand All @@ -264,17 +299,19 @@ def is_logged_in(self) -> bool:
return True
return False

@staticmethod
def __save_validate_time():
def __save_validate_time(self):
json_dict = {
'user_id': self.data['user_id'],
'client_id': self.data['client_id'],
'app_version': self.data['app_version'],
'last_validate': datetime.now().isoformat()
}
with open(infoDirectory, 'w', encoding='utf-8') as file:
with open(confDirectory, 'w', encoding='utf-8') as file:
json.dump(json_dict, file, indent=4)

@staticmethod
def __valid_time_diff() -> float:
with open(infoDirectory, 'r', encoding='utf-8') as file:
with open(confDirectory, 'r', encoding='utf-8') as file:
loaded_json = json.load(file)
if 'last_validate' in loaded_json.keys():
try:
Expand Down
Loading

0 comments on commit d97b972

Please sign in to comment.