mirror of
https://github.com/chibicitiberiu/ytsm.git
synced 2024-02-24 05:43:31 +00:00
Integrated pytaw library for youtube API.
This commit is contained in:
parent
6dd63b078f
commit
0fb09b00da
12
.idea/dataSources.local.xml
generated
12
.idea/dataSources.local.xml
generated
@ -2,7 +2,17 @@
|
||||
<project version="4">
|
||||
<component name="dataSourceStorageLocal">
|
||||
<data-source name="Django default" uuid="2dac2136-d902-4d27-8789-9371934602fd">
|
||||
<database-info product="SQLite" version="3.20.1" jdbc-version="2.1" driver-name="SQLite JDBC" driver-version="3.20.1.1" family="SQLITE" exact-version="3.20.1" />
|
||||
<database-info product="SQLite" version="3.25.1" jdbc-version="2.1" driver-name="SQLite JDBC" driver-version="3.25.1" family="SQLITE" exact-version="3.25.1">
|
||||
<identifier-quote-string>"</identifier-quote-string>
|
||||
</database-info>
|
||||
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
|
||||
<auth-required>false</auth-required>
|
||||
<introspection-schemas>*:@</introspection-schemas>
|
||||
</data-source>
|
||||
<data-source name="db" uuid="77df9da5-0b97-445e-a895-744ef8257a74">
|
||||
<database-info product="SQLite" version="3.25.1" jdbc-version="2.1" driver-name="SQLite JDBC" driver-version="3.25.1" family="SQLITE" exact-version="3.25.1">
|
||||
<identifier-quote-string>"</identifier-quote-string>
|
||||
</database-info>
|
||||
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
|
||||
<auth-required>false</auth-required>
|
||||
<introspection-schemas>*:@</introspection-schemas>
|
||||
|
17
.idea/dataSources.xml
generated
17
.idea/dataSources.xml
generated
@ -12,5 +12,22 @@
|
||||
<property name="enable_load_extension" value="true" />
|
||||
</driver-properties>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="db" uuid="77df9da5-0b97-445e-a895-744ef8257a74">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/db.sqlite3</jdbc-url>
|
||||
<driver-properties>
|
||||
<property name="enable_load_extension" value="true" />
|
||||
</driver-properties>
|
||||
<libraries>
|
||||
<library>
|
||||
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.25.1/sqlite-jdbc-3.25.1.jar</url>
|
||||
</library>
|
||||
<library>
|
||||
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.25.1/license.txt</url>
|
||||
</library>
|
||||
</libraries>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<dataSource name="Django default">
|
||||
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.10">
|
||||
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.11">
|
||||
<root id="1">
|
||||
<ServerVersion>3.20.1</ServerVersion>
|
||||
<ServerVersion>3.25.1</ServerVersion>
|
||||
</root>
|
||||
<schema id="2" parent="1" name="main">
|
||||
<Current>1</Current>
|
||||
@ -11,868 +11,28 @@
|
||||
<collation id="3" parent="1" name="BINARY"/>
|
||||
<collation id="4" parent="1" name="NOCASE"/>
|
||||
<collation id="5" parent="1" name="RTRIM"/>
|
||||
<table id="6" parent="2" name="YtManagerApp_channel"/>
|
||||
<table id="7" parent="2" name="YtManagerApp_subscription"/>
|
||||
<table id="8" parent="2" name="YtManagerApp_subscriptionfolder"/>
|
||||
<table id="9" parent="2" name="YtManagerApp_usersettings"/>
|
||||
<table id="10" parent="2" name="YtManagerApp_video"/>
|
||||
<table id="11" parent="2" name="auth_group"/>
|
||||
<table id="12" parent="2" name="auth_group_permissions"/>
|
||||
<table id="13" parent="2" name="auth_permission"/>
|
||||
<table id="14" parent="2" name="auth_user"/>
|
||||
<table id="15" parent="2" name="auth_user_groups"/>
|
||||
<table id="16" parent="2" name="auth_user_user_permissions"/>
|
||||
<table id="17" parent="2" name="django_admin_log"/>
|
||||
<table id="18" parent="2" name="django_content_type"/>
|
||||
<table id="19" parent="2" name="django_migrations"/>
|
||||
<table id="20" parent="2" name="django_session"/>
|
||||
<table id="21" parent="2" name="sqlite_master">
|
||||
<table id="6" parent="2" name="sqlite_master">
|
||||
<System>1</System>
|
||||
</table>
|
||||
<table id="22" parent="2" name="sqlite_sequence">
|
||||
<System>1</System>
|
||||
</table>
|
||||
<column id="23" parent="6" name="id">
|
||||
<column id="7" parent="6" name="type">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="24" parent="6" name="channel_id">
|
||||
<column id="8" parent="6" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="25" parent="6" name="username">
|
||||
<column id="9" parent="6" name="tbl_name">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="26" parent="6" name="custom_url">
|
||||
<column id="10" parent="6" name="rootpage">
|
||||
<Position>4</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<DataType>int|0s</DataType>
|
||||
</column>
|
||||
<column id="27" parent="6" name="name">
|
||||
<column id="11" parent="6" name="sql">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="28" parent="6" name="description">
|
||||
<Position>6</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="29" parent="6" name="icon_default">
|
||||
<Position>7</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="30" parent="6" name="icon_best">
|
||||
<Position>8</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="31" parent="6" name="upload_playlist_id">
|
||||
<Position>9</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="32" parent="6" name="sqlite_autoindex_YtManagerApp_channel_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>channel_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="33" parent="6" name="sqlite_autoindex_YtManagerApp_channel_2">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>username</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="34" parent="6" name="sqlite_autoindex_YtManagerApp_channel_3">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>custom_url</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="35" parent="6">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="36" parent="6">
|
||||
<ColNames>channel_id</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<key id="37" parent="6">
|
||||
<ColNames>username</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_2</UnderlyingIndexName>
|
||||
</key>
|
||||
<key id="38" parent="6">
|
||||
<ColNames>custom_url</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_3</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="39" parent="7" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="40" parent="7" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="41" parent="7" name="playlist_id">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="42" parent="7" name="description">
|
||||
<Position>4</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="43" parent="7" name="icon_default">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="44" parent="7" name="icon_best">
|
||||
<Position>6</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="45" parent="7" name="auto_download">
|
||||
<Position>7</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="46" parent="7" name="download_limit">
|
||||
<Position>8</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="47" parent="7" name="download_order">
|
||||
<Position>9</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="48" parent="7" name="manager_delete_after_watched">
|
||||
<Position>10</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="49" parent="7" name="channel_id">
|
||||
<Position>11</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="50" parent="7" name="parent_folder_id">
|
||||
<Position>12</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="51" parent="7" name="user_id">
|
||||
<Position>13</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="52" parent="7" name="sqlite_autoindex_YtManagerApp_subscription_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>playlist_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="53" parent="7" name="YtManagerApp_subscription_channel_id_b83c6f21">
|
||||
<ColNames>channel_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="54" parent="7" name="YtManagerApp_subscription_parent_folder_id_c4c64c21">
|
||||
<ColNames>parent_folder_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="55" parent="7" name="YtManagerApp_subscription_user_id_9d38617d">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="56" parent="7">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="57" parent="7">
|
||||
<ColNames>playlist_id</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_subscription_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<foreign-key id="58" parent="7">
|
||||
<ColNames>channel_id</ColNames>
|
||||
<RefTableName>YtManagerApp_channel</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="59" parent="7">
|
||||
<ColNames>parent_folder_id</ColNames>
|
||||
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="60" parent="7">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="61" parent="8" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="62" parent="8" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="63" parent="8" name="user_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="64" parent="8" name="parent_id">
|
||||
<Position>4</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<index id="65" parent="8" name="YtManagerApp_subscriptionfolder_user_id_6fb12da0">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="66" parent="8" name="YtManagerApp_subscriptionfolder_parent_id_bd5f4bc1">
|
||||
<ColNames>parent_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="67" parent="8">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="68" parent="8">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="69" parent="8">
|
||||
<ColNames>parent_id</ColNames>
|
||||
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="70" parent="9" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="71" parent="9" name="mark_deleted_as_watched">
|
||||
<Position>2</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="72" parent="9" name="delete_watched">
|
||||
<Position>3</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="73" parent="9" name="auto_download">
|
||||
<Position>4</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="74" parent="9" name="download_global_limit">
|
||||
<Position>5</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="75" parent="9" name="download_subscription_limit">
|
||||
<Position>6</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="76" parent="9" name="download_order">
|
||||
<Position>7</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="77" parent="9" name="download_path">
|
||||
<Position>8</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="78" parent="9" name="download_file_pattern">
|
||||
<Position>9</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="79" parent="9" name="download_format">
|
||||
<Position>10</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="80" parent="9" name="download_subtitles">
|
||||
<Position>11</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="81" parent="9" name="download_autogenerated_subtitles">
|
||||
<Position>12</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="82" parent="9" name="download_subtitles_all">
|
||||
<Position>13</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="83" parent="9" name="download_subtitles_langs">
|
||||
<Position>14</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="84" parent="9" name="download_subtitles_format">
|
||||
<Position>15</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="85" parent="9" name="user_id">
|
||||
<Position>16</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="86" parent="9" name="sqlite_autoindex_YtManagerApp_usersettings_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="87" parent="9">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="88" parent="9">
|
||||
<ColNames>user_id</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_usersettings_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<foreign-key id="89" parent="9">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="90" parent="10" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="91" parent="10" name="video_id">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="92" parent="10" name="name">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="93" parent="10" name="description">
|
||||
<Position>4</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="94" parent="10" name="watched">
|
||||
<Position>5</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="95" parent="10" name="downloaded_path">
|
||||
<Position>6</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="96" parent="10" name="playlist_index">
|
||||
<Position>7</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="97" parent="10" name="publish_date">
|
||||
<Position>8</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="98" parent="10" name="icon_default">
|
||||
<Position>9</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="99" parent="10" name="icon_best">
|
||||
<Position>10</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="100" parent="10" name="subscription_id">
|
||||
<Position>11</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="101" parent="10" name="YtManagerApp_video_subscription_id_720d4227">
|
||||
<ColNames>subscription_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="102" parent="10">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="103" parent="10">
|
||||
<ColNames>subscription_id</ColNames>
|
||||
<RefTableName>YtManagerApp_subscription</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="104" parent="11" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="105" parent="11" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(80)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="106" parent="11" name="sqlite_autoindex_auth_group_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>name</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="107" parent="11">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="108" parent="11">
|
||||
<ColNames>name</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_auth_group_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="109" parent="12" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="110" parent="12" name="group_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="111" parent="12" name="permission_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="112" parent="12" name="auth_group_permissions_group_id_permission_id_0cd325b0_uniq">
|
||||
<ColNames>group_id
|
||||
permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="113" parent="12" name="auth_group_permissions_group_id_b120cbf9">
|
||||
<ColNames>group_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="114" parent="12" name="auth_group_permissions_permission_id_84c5c92e">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="115" parent="12">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="116" parent="12">
|
||||
<ColNames>group_id</ColNames>
|
||||
<RefTableName>auth_group</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="117" parent="12">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<RefTableName>auth_permission</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="118" parent="13" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="119" parent="13" name="content_type_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="120" parent="13" name="codename">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="121" parent="13" name="name">
|
||||
<Position>4</Position>
|
||||
<DataType>varchar(255)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="122" parent="13" name="auth_permission_content_type_id_codename_01ab375a_uniq">
|
||||
<ColNames>content_type_id
|
||||
codename</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="123" parent="13" name="auth_permission_content_type_id_2f476e4b">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="124" parent="13">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="125" parent="13">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<RefTableName>django_content_type</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="126" parent="14" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="127" parent="14" name="password">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(128)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="128" parent="14" name="last_login">
|
||||
<Position>3</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
</column>
|
||||
<column id="129" parent="14" name="is_superuser">
|
||||
<Position>4</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="130" parent="14" name="username">
|
||||
<Position>5</Position>
|
||||
<DataType>varchar(150)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="131" parent="14" name="first_name">
|
||||
<Position>6</Position>
|
||||
<DataType>varchar(30)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="132" parent="14" name="email">
|
||||
<Position>7</Position>
|
||||
<DataType>varchar(254)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="133" parent="14" name="is_staff">
|
||||
<Position>8</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="134" parent="14" name="is_active">
|
||||
<Position>9</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="135" parent="14" name="date_joined">
|
||||
<Position>10</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="136" parent="14" name="last_name">
|
||||
<Position>11</Position>
|
||||
<DataType>varchar(150)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="137" parent="14" name="sqlite_autoindex_auth_user_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>username</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="138" parent="14">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="139" parent="14">
|
||||
<ColNames>username</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_auth_user_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="140" parent="15" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="141" parent="15" name="user_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="142" parent="15" name="group_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="143" parent="15" name="auth_user_groups_user_id_group_id_94350c0c_uniq">
|
||||
<ColNames>user_id
|
||||
group_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="144" parent="15" name="auth_user_groups_user_id_6a12ed8b">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="145" parent="15" name="auth_user_groups_group_id_97559544">
|
||||
<ColNames>group_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="146" parent="15">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="147" parent="15">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="148" parent="15">
|
||||
<ColNames>group_id</ColNames>
|
||||
<RefTableName>auth_group</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="149" parent="16" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="150" parent="16" name="user_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="151" parent="16" name="permission_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="152" parent="16" name="auth_user_user_permissions_user_id_permission_id_14a6b632_uniq">
|
||||
<ColNames>user_id
|
||||
permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="153" parent="16" name="auth_user_user_permissions_user_id_a95ead1b">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="154" parent="16" name="auth_user_user_permissions_permission_id_1fbb5f2c">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="155" parent="16">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="156" parent="16">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="157" parent="16">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<RefTableName>auth_permission</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="158" parent="17" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="159" parent="17" name="action_time">
|
||||
<Position>2</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="160" parent="17" name="object_id">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="161" parent="17" name="object_repr">
|
||||
<Position>4</Position>
|
||||
<DataType>varchar(200)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="162" parent="17" name="change_message">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="163" parent="17" name="content_type_id">
|
||||
<Position>6</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="164" parent="17" name="user_id">
|
||||
<Position>7</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="165" parent="17" name="action_flag">
|
||||
<Position>8</Position>
|
||||
<DataType>smallint unsigned|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="166" parent="17" name="django_admin_log_content_type_id_c4bce8eb">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="167" parent="17" name="django_admin_log_user_id_c564eba6">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="168" parent="17">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="169" parent="17">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<RefTableName>django_content_type</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="170" parent="17">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="171" parent="18" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="172" parent="18" name="app_label">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="173" parent="18" name="model">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="174" parent="18" name="django_content_type_app_label_model_76bd3d3b_uniq">
|
||||
<ColNames>app_label
|
||||
model</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="175" parent="18">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<column id="176" parent="19" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="177" parent="19" name="app">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(255)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="178" parent="19" name="name">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(255)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="179" parent="19" name="applied">
|
||||
<Position>4</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<key id="180" parent="19">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<column id="181" parent="20" name="session_key">
|
||||
<Position>1</Position>
|
||||
<DataType>varchar(40)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="182" parent="20" name="session_data">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="183" parent="20" name="expire_date">
|
||||
<Position>3</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="184" parent="20" name="sqlite_autoindex_django_session_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>session_key</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="185" parent="20" name="django_session_expire_date_a5c62663">
|
||||
<ColNames>expire_date</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="186" parent="20">
|
||||
<ColNames>session_key</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_django_session_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="187" parent="21" name="type">
|
||||
<Position>1</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="188" parent="21" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="189" parent="21" name="tbl_name">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="190" parent="21" name="rootpage">
|
||||
<Position>4</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="191" parent="21" name="sql">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="192" parent="22" name="name">
|
||||
<Position>1</Position>
|
||||
</column>
|
||||
<column id="193" parent="22" name="seq">
|
||||
<Position>2</Position>
|
||||
</column>
|
||||
</database-model>
|
||||
</dataSource>
|
883
.idea/dataSources/77df9da5-0b97-445e-a895-744ef8257a74.xml
generated
Normal file
883
.idea/dataSources/77df9da5-0b97-445e-a895-744ef8257a74.xml
generated
Normal file
@ -0,0 +1,883 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<dataSource name="db">
|
||||
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.11">
|
||||
<root id="1">
|
||||
<ServerVersion>3.25.1</ServerVersion>
|
||||
</root>
|
||||
<schema id="2" parent="1" name="main">
|
||||
<Current>1</Current>
|
||||
<Visible>1</Visible>
|
||||
</schema>
|
||||
<collation id="3" parent="1" name="BINARY"/>
|
||||
<collation id="4" parent="1" name="NOCASE"/>
|
||||
<collation id="5" parent="1" name="RTRIM"/>
|
||||
<table id="6" parent="2" name="YtManagerApp_channel"/>
|
||||
<table id="7" parent="2" name="YtManagerApp_subscription"/>
|
||||
<table id="8" parent="2" name="YtManagerApp_subscriptionfolder"/>
|
||||
<table id="9" parent="2" name="YtManagerApp_usersettings"/>
|
||||
<table id="10" parent="2" name="YtManagerApp_video"/>
|
||||
<table id="11" parent="2" name="auth_group"/>
|
||||
<table id="12" parent="2" name="auth_group_permissions"/>
|
||||
<table id="13" parent="2" name="auth_permission"/>
|
||||
<table id="14" parent="2" name="auth_user"/>
|
||||
<table id="15" parent="2" name="auth_user_groups"/>
|
||||
<table id="16" parent="2" name="auth_user_user_permissions"/>
|
||||
<table id="17" parent="2" name="django_admin_log"/>
|
||||
<table id="18" parent="2" name="django_content_type"/>
|
||||
<table id="19" parent="2" name="django_migrations"/>
|
||||
<table id="20" parent="2" name="django_session"/>
|
||||
<table id="21" parent="2" name="sqlite_master">
|
||||
<System>1</System>
|
||||
</table>
|
||||
<table id="22" parent="2" name="sqlite_sequence">
|
||||
<System>1</System>
|
||||
</table>
|
||||
<column id="23" parent="6" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="24" parent="6" name="channel_id">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="25" parent="6" name="username">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="26" parent="6" name="custom_url">
|
||||
<Position>4</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="27" parent="6" name="name">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="28" parent="6" name="description">
|
||||
<Position>6</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="29" parent="6" name="icon_default">
|
||||
<Position>7</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="30" parent="6" name="icon_best">
|
||||
<Position>8</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="31" parent="6" name="upload_playlist_id">
|
||||
<Position>9</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="32" parent="6" name="sqlite_autoindex_YtManagerApp_channel_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>channel_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="33" parent="6" name="sqlite_autoindex_YtManagerApp_channel_2">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>username</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="34" parent="6" name="sqlite_autoindex_YtManagerApp_channel_3">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>custom_url</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="35" parent="6">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="36" parent="6">
|
||||
<ColNames>channel_id</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<key id="37" parent="6">
|
||||
<ColNames>username</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_2</UnderlyingIndexName>
|
||||
</key>
|
||||
<key id="38" parent="6">
|
||||
<ColNames>custom_url</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_channel_3</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="39" parent="7" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="40" parent="7" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(1024)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="41" parent="7" name="playlist_id">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(128)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="42" parent="7" name="description">
|
||||
<Position>4</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="43" parent="7" name="icon_default">
|
||||
<Position>5</Position>
|
||||
<DataType>varchar(1024)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="44" parent="7" name="icon_best">
|
||||
<Position>6</Position>
|
||||
<DataType>varchar(1024)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="45" parent="7" name="auto_download">
|
||||
<Position>7</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="46" parent="7" name="download_limit">
|
||||
<Position>8</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="47" parent="7" name="download_order">
|
||||
<Position>9</Position>
|
||||
<DataType>varchar(128)|0s</DataType>
|
||||
</column>
|
||||
<column id="48" parent="7" name="channel_id">
|
||||
<Position>10</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="49" parent="7" name="parent_folder_id">
|
||||
<Position>11</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="50" parent="7" name="user_id">
|
||||
<Position>12</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="51" parent="7" name="delete_after_watched">
|
||||
<Position>13</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<index id="52" parent="7" name="YtManagerApp_subscription_channel_id_b83c6f21">
|
||||
<ColNames>channel_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="53" parent="7" name="YtManagerApp_subscription_parent_folder_id_c4c64c21">
|
||||
<ColNames>parent_folder_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="54" parent="7" name="YtManagerApp_subscription_user_id_9d38617d">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="55" parent="7">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="56" parent="7">
|
||||
<ColNames>channel_id</ColNames>
|
||||
<RefTableName>YtManagerApp_channel</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="57" parent="7">
|
||||
<ColNames>parent_folder_id</ColNames>
|
||||
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="58" parent="7">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="59" parent="8" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="60" parent="8" name="user_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="61" parent="8" name="name">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(250)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="62" parent="8" name="parent_id">
|
||||
<Position>4</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<index id="63" parent="8" name="YtManagerApp_subscriptionfolder_user_id_6fb12da0">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="64" parent="8" name="YtManagerApp_subscriptionfolder_parent_id_bd5f4bc1">
|
||||
<ColNames>parent_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="65" parent="8">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="66" parent="8">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="67" parent="8">
|
||||
<ColNames>parent_id</ColNames>
|
||||
<RefTableName>YtManagerApp_subscriptionfolder</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="68" parent="9" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="69" parent="9" name="delete_watched">
|
||||
<Position>2</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="70" parent="9" name="auto_download">
|
||||
<Position>3</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="71" parent="9" name="download_global_limit">
|
||||
<Position>4</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="72" parent="9" name="download_subscription_limit">
|
||||
<Position>5</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="73" parent="9" name="download_order">
|
||||
<Position>6</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
</column>
|
||||
<column id="74" parent="9" name="download_path">
|
||||
<Position>7</Position>
|
||||
<DataType>varchar(1024)|0s</DataType>
|
||||
</column>
|
||||
<column id="75" parent="9" name="download_file_pattern">
|
||||
<Position>8</Position>
|
||||
<DataType>varchar(1024)|0s</DataType>
|
||||
</column>
|
||||
<column id="76" parent="9" name="download_format">
|
||||
<Position>9</Position>
|
||||
<DataType>varchar(256)|0s</DataType>
|
||||
</column>
|
||||
<column id="77" parent="9" name="download_subtitles">
|
||||
<Position>10</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="78" parent="9" name="download_autogenerated_subtitles">
|
||||
<Position>11</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="79" parent="9" name="download_subtitles_all">
|
||||
<Position>12</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<column id="80" parent="9" name="download_subtitles_langs">
|
||||
<Position>13</Position>
|
||||
<DataType>varchar(250)|0s</DataType>
|
||||
</column>
|
||||
<column id="81" parent="9" name="download_subtitles_format">
|
||||
<Position>14</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
</column>
|
||||
<column id="82" parent="9" name="user_id">
|
||||
<Position>15</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="83" parent="9" name="mark_deleted_as_watched">
|
||||
<Position>16</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
</column>
|
||||
<index id="84" parent="9" name="sqlite_autoindex_YtManagerApp_usersettings_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="85" parent="9">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="86" parent="9">
|
||||
<ColNames>user_id</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_YtManagerApp_usersettings_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<foreign-key id="87" parent="9">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="88" parent="10" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="89" parent="10" name="video_id">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="90" parent="10" name="name">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="91" parent="10" name="description">
|
||||
<Position>4</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="92" parent="10" name="watched">
|
||||
<Position>5</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="93" parent="10" name="downloaded_path">
|
||||
<Position>6</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="94" parent="10" name="playlist_index">
|
||||
<Position>7</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="95" parent="10" name="publish_date">
|
||||
<Position>8</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="96" parent="10" name="icon_default">
|
||||
<Position>9</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="97" parent="10" name="icon_best">
|
||||
<Position>10</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="98" parent="10" name="subscription_id">
|
||||
<Position>11</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="99" parent="10" name="rating">
|
||||
<Position>12</Position>
|
||||
<DataType>real|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="100" parent="10" name="uploader_name">
|
||||
<Position>13</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="101" parent="10" name="views">
|
||||
<Position>14</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="102" parent="10" name="YtManagerApp_video_subscription_id_720d4227">
|
||||
<ColNames>subscription_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="103" parent="10">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="104" parent="10">
|
||||
<ColNames>subscription_id</ColNames>
|
||||
<RefTableName>YtManagerApp_subscription</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="105" parent="11" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="106" parent="11" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(80)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="107" parent="11" name="sqlite_autoindex_auth_group_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>name</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="108" parent="11">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="109" parent="11">
|
||||
<ColNames>name</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_auth_group_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="110" parent="12" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="111" parent="12" name="group_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="112" parent="12" name="permission_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="113" parent="12" name="auth_group_permissions_group_id_permission_id_0cd325b0_uniq">
|
||||
<ColNames>group_id
|
||||
permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="114" parent="12" name="auth_group_permissions_group_id_b120cbf9">
|
||||
<ColNames>group_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="115" parent="12" name="auth_group_permissions_permission_id_84c5c92e">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="116" parent="12">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="117" parent="12">
|
||||
<ColNames>group_id</ColNames>
|
||||
<RefTableName>auth_group</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="118" parent="12">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<RefTableName>auth_permission</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="119" parent="13" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="120" parent="13" name="content_type_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="121" parent="13" name="codename">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="122" parent="13" name="name">
|
||||
<Position>4</Position>
|
||||
<DataType>varchar(255)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="123" parent="13" name="auth_permission_content_type_id_codename_01ab375a_uniq">
|
||||
<ColNames>content_type_id
|
||||
codename</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="124" parent="13" name="auth_permission_content_type_id_2f476e4b">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="125" parent="13">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="126" parent="13">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<RefTableName>django_content_type</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="127" parent="14" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="128" parent="14" name="password">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(128)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="129" parent="14" name="last_login">
|
||||
<Position>3</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
</column>
|
||||
<column id="130" parent="14" name="is_superuser">
|
||||
<Position>4</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="131" parent="14" name="username">
|
||||
<Position>5</Position>
|
||||
<DataType>varchar(150)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="132" parent="14" name="first_name">
|
||||
<Position>6</Position>
|
||||
<DataType>varchar(30)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="133" parent="14" name="email">
|
||||
<Position>7</Position>
|
||||
<DataType>varchar(254)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="134" parent="14" name="is_staff">
|
||||
<Position>8</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="135" parent="14" name="is_active">
|
||||
<Position>9</Position>
|
||||
<DataType>bool|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="136" parent="14" name="date_joined">
|
||||
<Position>10</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="137" parent="14" name="last_name">
|
||||
<Position>11</Position>
|
||||
<DataType>varchar(150)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="138" parent="14" name="sqlite_autoindex_auth_user_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>username</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="139" parent="14">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<key id="140" parent="14">
|
||||
<ColNames>username</ColNames>
|
||||
<UnderlyingIndexName>sqlite_autoindex_auth_user_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="141" parent="15" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="142" parent="15" name="user_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="143" parent="15" name="group_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="144" parent="15" name="auth_user_groups_user_id_group_id_94350c0c_uniq">
|
||||
<ColNames>user_id
|
||||
group_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="145" parent="15" name="auth_user_groups_user_id_6a12ed8b">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="146" parent="15" name="auth_user_groups_group_id_97559544">
|
||||
<ColNames>group_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="147" parent="15">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="148" parent="15">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="149" parent="15">
|
||||
<ColNames>group_id</ColNames>
|
||||
<RefTableName>auth_group</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="150" parent="16" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="151" parent="16" name="user_id">
|
||||
<Position>2</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="152" parent="16" name="permission_id">
|
||||
<Position>3</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="153" parent="16" name="auth_user_user_permissions_user_id_permission_id_14a6b632_uniq">
|
||||
<ColNames>user_id
|
||||
permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="154" parent="16" name="auth_user_user_permissions_user_id_a95ead1b">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="155" parent="16" name="auth_user_user_permissions_permission_id_1fbb5f2c">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="156" parent="16">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="157" parent="16">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="158" parent="16">
|
||||
<ColNames>permission_id</ColNames>
|
||||
<RefTableName>auth_permission</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="159" parent="17" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="160" parent="17" name="action_time">
|
||||
<Position>2</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="161" parent="17" name="object_id">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="162" parent="17" name="object_repr">
|
||||
<Position>4</Position>
|
||||
<DataType>varchar(200)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="163" parent="17" name="change_message">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="164" parent="17" name="content_type_id">
|
||||
<Position>6</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="165" parent="17" name="user_id">
|
||||
<Position>7</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="166" parent="17" name="action_flag">
|
||||
<Position>8</Position>
|
||||
<DataType>smallint unsigned|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="167" parent="17" name="django_admin_log_content_type_id_c4bce8eb">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="168" parent="17" name="django_admin_log_user_id_c564eba6">
|
||||
<ColNames>user_id</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="169" parent="17">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<foreign-key id="170" parent="17">
|
||||
<ColNames>content_type_id</ColNames>
|
||||
<RefTableName>django_content_type</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<foreign-key id="171" parent="17">
|
||||
<ColNames>user_id</ColNames>
|
||||
<RefTableName>auth_user</RefTableName>
|
||||
<RefColNames>id</RefColNames>
|
||||
<Deferrable>1</Deferrable>
|
||||
<InitiallyDeferred>1</InitiallyDeferred>
|
||||
</foreign-key>
|
||||
<column id="172" parent="18" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="173" parent="18" name="app_label">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="174" parent="18" name="model">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(100)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="175" parent="18" name="django_content_type_app_label_model_76bd3d3b_uniq">
|
||||
<ColNames>app_label
|
||||
model</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="176" parent="18">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<column id="177" parent="19" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="178" parent="19" name="app">
|
||||
<Position>2</Position>
|
||||
<DataType>varchar(255)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="179" parent="19" name="name">
|
||||
<Position>3</Position>
|
||||
<DataType>varchar(255)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="180" parent="19" name="applied">
|
||||
<Position>4</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<key id="181" parent="19">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
<column id="182" parent="20" name="session_key">
|
||||
<Position>1</Position>
|
||||
<DataType>varchar(40)|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="183" parent="20" name="session_data">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="184" parent="20" name="expire_date">
|
||||
<Position>3</Position>
|
||||
<DataType>datetime|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="185" parent="20" name="sqlite_autoindex_django_session_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>session_key</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="186" parent="20" name="django_session_expire_date_a5c62663">
|
||||
<ColNames>expire_date</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="187" parent="20">
|
||||
<ColNames>session_key</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_django_session_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="188" parent="21" name="type">
|
||||
<Position>1</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="189" parent="21" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="190" parent="21" name="tbl_name">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="191" parent="21" name="rootpage">
|
||||
<Position>4</Position>
|
||||
<DataType>int|0s</DataType>
|
||||
</column>
|
||||
<column id="192" parent="21" name="sql">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="193" parent="22" name="name">
|
||||
<Position>1</Position>
|
||||
</column>
|
||||
<column id="194" parent="22" name="seq">
|
||||
<Position>2</Position>
|
||||
</column>
|
||||
</database-model>
|
||||
</dataSource>
|
2
.idea/dataSources/77df9da5-0b97-445e-a895-744ef8257a74/storage_v2/_src_/schema/main.uQUzAA.meta
generated
Normal file
2
.idea/dataSources/77df9da5-0b97-445e-a895-744ef8257a74/storage_v2/_src_/schema/main.uQUzAA.meta
generated
Normal file
@ -0,0 +1,2 @@
|
||||
#n:main
|
||||
!<md> [0, 0, null, null, -2147483648, -2147483648]
|
565
.idea/workspace.xml
generated
565
.idea/workspace.xml
generated
@ -2,10 +2,39 @@
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="245751b6-c863-4572-8723-8499964fe105" name="Default Changelist" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/readme.md" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/YtManagerApp/migrations/0007_auto_20181029_1638.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/.gitignore" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/.pytaw.conf" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/README.md" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/docs/Makefile" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/docs/conf.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/docs/index.rst" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/docs/make.bat" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/main_test.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/pytaw/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/pytaw/utils.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/pytaw/youtube.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/setup.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/tests/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/external/pytaw/tests/test_pytaw.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/dataSources.local.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.local.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/dataSources.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/dataSources/2dac2136-d902-4d27-8789-9371934602fd.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources/2dac2136-d902-4d27-8789-9371934602fd.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/youtube-channel-manager.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/youtube-channel-manager.iml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/management.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/management/videos.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/management/videos.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/models.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/scheduler.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/scheduler.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/utils/iterutils.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/utils/youtube.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/utils/youtube.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/YtManagerApp/views/index.py" beforeDir="false" afterPath="$PROJECT_DIR$/YtManagerApp/views/index.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/config/config.ini" beforeDir="false" afterPath="$PROJECT_DIR$/config/config.ini" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/readme.md" beforeDir="false" afterPath="$PROJECT_DIR$/readme.md" afterDir="false" />
|
||||
</list>
|
||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
@ -32,19 +61,23 @@
|
||||
<expand>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="db" type="feb32156:DbDataSourceImpl" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="db" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="db" type="feb32156:DbDataSourceImpl" />
|
||||
<item name="schemas" type="d4e8921:DatabaseStructure$FamilyGroup" />
|
||||
<item name="main: schema" type="90513b60:SqliteImplModel$Schema" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="Database" type="3277223f:DatabaseStructure$DbRootGroup" />
|
||||
<item name="Django default" type="feb32156:DbDataSourceImpl" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</component>
|
||||
@ -137,51 +170,60 @@
|
||||
<usages-collector id="statistics.lifecycle.project">
|
||||
<counts>
|
||||
<entry key="project.open.time.10" value="1" />
|
||||
<entry key="project.opened" value="1" />
|
||||
<entry key="project.open.time.13" value="1" />
|
||||
<entry key="project.opened" value="2" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.extensions.open">
|
||||
<counts>
|
||||
<entry key="auth_group" value="1" />
|
||||
<entry key="gitignore" value="1" />
|
||||
<entry key="html" value="1" />
|
||||
<entry key="md" value="2" />
|
||||
<entry key="py" value="3" />
|
||||
<entry key="ini" value="2" />
|
||||
<entry key="md" value="5" />
|
||||
<entry key="py" value="35" />
|
||||
<entry key="ytmanagerapp_channel" value="1" />
|
||||
<entry key="ytmanagerapp_subscription" value="2" />
|
||||
<entry key="ytmanagerapp_video" value="3" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.types.open">
|
||||
<counts>
|
||||
<entry key="Database Element" value="7" />
|
||||
<entry key="HTML" value="1" />
|
||||
<entry key="Markdown" value="2" />
|
||||
<entry key="Ini" value="2" />
|
||||
<entry key="Markdown" value="5" />
|
||||
<entry key="PLAIN_TEXT" value="1" />
|
||||
<entry key="Python" value="3" />
|
||||
<entry key="Python" value="35" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.extensions.edit">
|
||||
<counts>
|
||||
<entry key="md" value="464" />
|
||||
<entry key="py" value="1" />
|
||||
<entry key="py@youtube-channel-manager" value="12" />
|
||||
<entry key="Django Console" value="155" />
|
||||
<entry key="ini" value="2" />
|
||||
<entry key="md" value="509" />
|
||||
<entry key="py" value="3244" />
|
||||
<entry key="py@youtube-channel-manager" value="41" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.types.edit">
|
||||
<counts>
|
||||
<entry key="CommandLine" value="12" />
|
||||
<entry key="Markdown" value="464" />
|
||||
<entry key="Python" value="1" />
|
||||
<entry key="CommandLine" value="23" />
|
||||
<entry key="Ini" value="2" />
|
||||
<entry key="Markdown" value="509" />
|
||||
<entry key="PLAIN_TEXT" value="18" />
|
||||
<entry key="Python" value="3399" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
</session>
|
||||
</component>
|
||||
<component name="FileEditorManager">
|
||||
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
||||
<leaf>
|
||||
<file pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/readme.md">
|
||||
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
|
||||
<state split_layout="SPLIT">
|
||||
<first_editor relative-caret-position="180">
|
||||
<caret line="12" column="31" lean-forward="true" selection-start-line="12" selection-start-column="31" selection-end-line="12" selection-end-column="31" />
|
||||
</first_editor>
|
||||
<second_editor />
|
||||
<entry file="file://$PROJECT_DIR$/config/config.ini">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="373">
|
||||
<caret line="44" lean-forward="true" selection-start-line="44" selection-end-line="44" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
@ -194,22 +236,13 @@
|
||||
<option value="SCSS File" />
|
||||
<option value="Setup Script" />
|
||||
<option value="JavaScript File" />
|
||||
<option value="Python Script" />
|
||||
<option value="HTML File" />
|
||||
<option value="Python Script" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="FindInProjectRecents">
|
||||
<findStrings>
|
||||
<find>ConfigParserWithEnv</find>
|
||||
<find>logger</find>
|
||||
<find>app_config</find>
|
||||
<find>dialog_</find>
|
||||
<find>.find</find>
|
||||
<find>.show</find>
|
||||
<find>videos_wrapper</find>
|
||||
<find>videos_loading</find>
|
||||
<find>submit</find>
|
||||
<find>modal_edit_folder</find>
|
||||
<find>modal_update_folder</find>
|
||||
<find>modal_delete_folder</find>
|
||||
@ -231,6 +264,15 @@
|
||||
<find>settings</find>
|
||||
<find>settings.</find>
|
||||
<find>manager_delete_after_watched</find>
|
||||
<find>dist</find>
|
||||
<find>relatedPlaylists</find>
|
||||
<find>thumbnails</find>
|
||||
<find>'thumbnails'</find>
|
||||
<find>thumbn</find>
|
||||
<find>thumbnail</find>
|
||||
<find>get_or_create</find>
|
||||
<find>video</find>
|
||||
<find>class thum</find>
|
||||
</findStrings>
|
||||
<replaceStrings>
|
||||
<replace>loading</replace>
|
||||
@ -254,8 +296,6 @@
|
||||
<component name="IdeDocumentHistory">
|
||||
<option name="CHANGED_PATHS">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_create_modal.html" />
|
||||
<option value="$PROJECT_DIR$/../YoutubeApi-tests/multiinheritance.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_edit_modal.html" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/controls/folder_update_modal.html" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/folders.py" />
|
||||
@ -277,11 +317,7 @@
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/apps.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/index.js" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/js/common.js" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/scheduler.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/videos.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/utils/iterutils.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/utils/youtube.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templatetags/common.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templatetags/ratings.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index_videos.html" />
|
||||
@ -297,14 +333,20 @@
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/utils/customconfigparser.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/utils/extended_interpolation_with_env.py" />
|
||||
<option value="$PROJECT_DIR$/config/defaults.ini" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/models.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/appconfig.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/downloader.py" />
|
||||
<option value="$PROJECT_DIR$/config/config.ini" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/views/index.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/scheduler.py" />
|
||||
<option value="$PROJECT_DIR$/readme.md" />
|
||||
<option value="$PROJECT_DIR$/external/pytaw/README.md" />
|
||||
<option value="$PROJECT_DIR$/external/pytaw/pytaw/youtube.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/utils/youtube.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/videos.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/models.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/views/index.py" />
|
||||
<option value="$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py" />
|
||||
<option value="$PROJECT_DIR$/config/config.ini" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
@ -320,8 +362,8 @@
|
||||
</packageJsonPaths>
|
||||
</component>
|
||||
<component name="ProjectFrameBounds" extendedState="6">
|
||||
<option name="x" value="-8" />
|
||||
<option name="y" value="-8" />
|
||||
<option name="x" value="803" />
|
||||
<option name="y" value="615" />
|
||||
<option name="width" value="643" />
|
||||
<option name="height" value="321" />
|
||||
</component>
|
||||
@ -378,6 +420,7 @@
|
||||
<foldersAlwaysOnTop value="true" />
|
||||
</navigator>
|
||||
<panes>
|
||||
<pane id="Scope" />
|
||||
<pane id="ProjectPane">
|
||||
<subPane>
|
||||
<expand>
|
||||
@ -385,6 +428,11 @@
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="config" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
@ -394,50 +442,25 @@
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="management" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="management" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="jobs" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="import" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="static" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="import" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="bootstrap-4.1.3" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="templates" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="youtube-channel-manager" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="youtube-channel-manager" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="templates" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="YtManagerApp" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="utils" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</subPane>
|
||||
</pane>
|
||||
<pane id="Scope" />
|
||||
</panes>
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
@ -445,26 +468,26 @@
|
||||
<property name="SearchEverywhereHistoryKey" value="	FILE	file://D:/Dev/youtube-channel-manager/YtManager/settings.py" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="database.console.LAST_STATE" value="false" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/../YoutubeApi-tests/get_playlist_info.py" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/../pytaw" />
|
||||
<property name="list.type.of.created.stylesheet" value="SCSS" />
|
||||
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
|
||||
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
|
||||
<property name="settings.editor.selected.configurable" value="reference.settings.ide.settings.web.browsers" />
|
||||
</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\management\jobs" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp\controls" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\registration" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\config" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp\js" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\views\controls" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\views" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\static\YtManagerApp" />
|
||||
</key>
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/YtManagerApp/import" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\management\jobs" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\YtManagerApp\controls" />
|
||||
<recent name="D:\Dev\youtube-channel-manager\YtManagerApp\templates\registration" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunDashboard">
|
||||
@ -610,29 +633,29 @@
|
||||
<frame x="0" y="0" width="1920" height="1048" extended-state="6" />
|
||||
<editor active="true" />
|
||||
<layout>
|
||||
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.18603411" />
|
||||
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.18923241" />
|
||||
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
|
||||
<window_info id="Favorites" order="2" side_tool="true" />
|
||||
<window_info anchor="bottom" id="Message" order="0" />
|
||||
<window_info anchor="bottom" id="Find" order="1" sideWeight="0.49733475" weight="0.329718" />
|
||||
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.5245203" visible="true" weight="0.33913043" />
|
||||
<window_info anchor="bottom" id="Find" order="1" sideWeight="0.49520257" weight="0.38152173" />
|
||||
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.5234541" visible="true" weight="0.2228261" />
|
||||
<window_info anchor="bottom" id="Cvs" order="3" weight="0.25" />
|
||||
<window_info anchor="bottom" id="Inspection" order="4" weight="0.4" />
|
||||
<window_info anchor="bottom" id="Debug" order="5" sideWeight="0.49520257" weight="0.37608695" />
|
||||
<window_info anchor="bottom" id="Debug" order="5" sideWeight="0.4936034" weight="0.37608695" />
|
||||
<window_info anchor="bottom" id="TODO" order="6" weight="0.329718" />
|
||||
<window_info anchor="bottom" id="manage.py@youtube-channel-manager" order="7" sideWeight="0.49626866" weight="0.49347827" />
|
||||
<window_info anchor="bottom" id="manage.py@youtube-channel-manager" order="7" sideWeight="0.49573562" weight="0.49347827" />
|
||||
<window_info anchor="bottom" id="Docker" order="8" show_stripe_button="false" />
|
||||
<window_info anchor="bottom" id="Database Changes" order="9" weight="0.3285968" />
|
||||
<window_info anchor="bottom" id="Event Log" order="10" sideWeight="0.47547975" side_tool="true" visible="true" weight="0.33913043" />
|
||||
<window_info anchor="bottom" id="Event Log" order="10" sideWeight="0.47654584" side_tool="true" visible="true" weight="0.2228261" />
|
||||
<window_info anchor="bottom" id="Version Control" order="11" sideWeight="0.49946696" weight="0.329718" />
|
||||
<window_info anchor="bottom" id="Terminal" order="12" sideWeight="0.49626866" weight="0.3576087" />
|
||||
<window_info anchor="bottom" id="Python Console" order="13" sideWeight="0.49733475" weight="0.3347826" />
|
||||
<window_info anchor="bottom" id="Terminal" order="12" sideWeight="0.49573562" weight="0.3576087" />
|
||||
<window_info anchor="bottom" id="Python Console" order="13" sideWeight="0.4968017" weight="0.33804348" />
|
||||
<window_info anchor="bottom" id="Database Console" order="14" sideWeight="0.49838188" weight="0.21908894" />
|
||||
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
|
||||
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
|
||||
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
|
||||
<window_info anchor="right" id="SciView" order="3" weight="0.32995737" />
|
||||
<window_info anchor="right" id="Database" order="4" weight="0.2675906" />
|
||||
<window_info anchor="right" id="Database" order="4" visible="true" weight="0.20469083" />
|
||||
</layout>
|
||||
<layout-to-restore>
|
||||
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.15138593" />
|
||||
@ -667,75 +690,6 @@
|
||||
<option name="myLimit" value="2678400000" />
|
||||
</component>
|
||||
<component name="editorHistoryManager">
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/utils/youtube.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="187">
|
||||
<caret line="11" lean-forward="true" selection-start-line="11" selection-end-line="11" />
|
||||
<folding>
|
||||
<element signature="e#0#43#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/videos.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="357">
|
||||
<caret line="24" column="9" lean-forward="true" selection-start-line="24" selection-start-column="9" selection-end-line="24" selection-end-column="9" />
|
||||
<folding>
|
||||
<element signature="e#0#71#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="68">
|
||||
<caret line="4" selection-start-line="4" selection-end-line="4" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templatetags/common.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="459">
|
||||
<caret line="30" lean-forward="true" selection-start-line="30" selection-end-line="30" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templatetags/ratings.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="187">
|
||||
<caret line="30" column="9" lean-forward="true" selection-start-line="30" selection-start-column="9" selection-end-line="30" selection-end-column="9" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/views/old_views.py" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state>
|
||||
<caret selection-end-column="48" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://C:/Python36/Lib/site-packages/django/template/base.py" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/index_videos.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="143">
|
||||
<caret line="23" column="56" selection-start-line="23" selection-start-column="56" selection-end-line="23" selection-end-column="56" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://C:/Python36/Lib/site-packages/django/core/handlers/exception.py" />
|
||||
<entry file="file://C:/Python36/Lib/site-packages/django/views/generic/base.py" />
|
||||
<entry file="file://C:/Python36/Lib/site-packages/django/views/generic/edit.py" />
|
||||
<entry file="file://C:/Python36/Lib/site-packages/django/db/models/query.py" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/server_settings.html" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/templates/YtManagerApp/settings.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="187">
|
||||
<caret line="11" column="18" lean-forward="true" selection-start-line="11" selection-start-column="18" selection-end-line="11" selection-end-column="18" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/css/style.scss">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="391">
|
||||
@ -751,20 +705,6 @@
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_video">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_subscription">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://C:/Python36/Lib/site-packages/django/db/models/fields/__init__.py" />
|
||||
<entry file="file://C:/Python36/Lib/site-packages/django/forms/models.py" />
|
||||
<entry file="file://C:/Python36/Lib/site-packages/django/contrib/auth/decorators.py" />
|
||||
@ -772,9 +712,6 @@
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="136">
|
||||
<caret line="8" lean-forward="true" selection-start-line="8" selection-end-line="8" />
|
||||
<folding>
|
||||
<element signature="e#0#42#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
@ -797,48 +734,15 @@
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="459">
|
||||
<caret line="92" lean-forward="true" selection-start-line="92" selection-end-line="92" />
|
||||
<folding>
|
||||
<element signature="e#0#9#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://C:/Python36/Lib/collections/__init__.py" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="510">
|
||||
<caret line="102" column="12" lean-forward="true" selection-start-line="102" selection-start-column="12" selection-end-line="102" selection-end-column="12" />
|
||||
<folding>
|
||||
<element signature="e#0#37#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/scheduler.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="136">
|
||||
<caret line="8" column="32" lean-forward="true" selection-start-line="8" selection-start-column="32" selection-end-line="8" selection-end-column="32" />
|
||||
<folding>
|
||||
<element signature="e#0#14#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$USER_HOME$/.PyCharm2018.2/system/python_stubs/-1184660488/builtins.py" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/urls.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="408">
|
||||
<caret line="33" selection-start-line="33" selection-end-line="33" />
|
||||
<folding>
|
||||
<element signature="e#643#675#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManager/settings.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="746">
|
||||
<caret line="132" selection-start-line="132" selection-end-line="132" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
@ -856,29 +760,13 @@
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="187">
|
||||
<caret line="11" selection-start-line="11" selection-end-line="11" />
|
||||
<folding>
|
||||
<element signature="e#0#42#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://C:/Python36/Lib/configparser.py" />
|
||||
<entry file="file://C:/Python36/Lib/site-packages/apscheduler/executors/base.py" />
|
||||
<entry file="file://C:/Python36/Lib/site-packages/django/db/models/fields/related_descriptors.py" />
|
||||
<entry file="file:///usr/lib/python3/dist-packages/django/db/models/base.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="193">
|
||||
<caret line="1652" selection-start-line="1652" selection-end-line="1652" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/views/index.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state>
|
||||
<caret column="49" selection-start-column="49" selection-end-column="49" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3/dist-packages/django/db/models/base.py" />
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/appmain.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="165">
|
||||
@ -916,30 +804,6 @@
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="311">
|
||||
<caret line="123" selection-start-line="123" selection-end-line="123" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/config/config.ini">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="491">
|
||||
<caret line="59" selection-start-line="59" selection-end-line="59" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/models.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="345">
|
||||
<caret line="23" column="30" lean-forward="true" selection-start-line="23" selection-start-column="30" selection-end-line="23" selection-end-column="30" />
|
||||
<folding>
|
||||
<element signature="e#0#14#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/static/YtManagerApp/import/bootstrap-4.1.3/.gitignore">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="45">
|
||||
@ -954,15 +818,204 @@
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/delete_video.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="485">
|
||||
<caret line="37" column="65" selection-start-line="37" selection-start-column="65" selection-end-line="38" selection-end-column="76" />
|
||||
<folding>
|
||||
<element signature="e#0#14#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/local/lib/python3.6/dist-packages/apscheduler/schedulers/base.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="421">
|
||||
<caret line="440" column="16" selection-start-line="440" selection-start-column="16" selection-end-line="440" selection-end-column="16" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/readme.md">
|
||||
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
|
||||
<state split_layout="SPLIT">
|
||||
<first_editor relative-caret-position="180">
|
||||
<caret line="12" column="31" lean-forward="true" selection-start-line="12" selection-start-column="31" selection-end-line="12" selection-end-column="31" />
|
||||
<first_editor relative-caret-position="60">
|
||||
<caret line="4" selection-start-line="4" selection-end-line="4" />
|
||||
</first_editor>
|
||||
<second_editor />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManager/settings.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="1046">
|
||||
<caret line="132" selection-start-line="132" selection-end-line="132" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_subscription">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/auth_group">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://2dac2136-d902-4d27-8789-9371934602fd/schema/main/table/ytmanagerapp_video">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/external/pytaw/docs/conf.py">
|
||||
<provider selected="true" editor-type-id="text-editor" />
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/scheduler.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="300">
|
||||
<caret line="20" column="5" selection-start-line="20" selection-start-column="5" selection-end-line="20" selection-end-column="5" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/utils/iterutils.py" />
|
||||
<entry file="das://77df9da5-0b97-445e-a895-744ef8257a74/schema/main/table/ytmanagerapp_channel">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://77df9da5-0b97-445e-a895-744ef8257a74/schema/main/table/ytmanagerapp_subscription">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="das://77df9da5-0b97-445e-a895-744ef8257a74/schema/main/table/ytmanagerapp_video">
|
||||
<provider selected="true" editor-type-id="com.intellij.database.editor.DatabaseTableFileEditorProvider">
|
||||
<state>
|
||||
<filtering enabled="true" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/external/pytaw/README.md">
|
||||
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
|
||||
<state split_layout="SPLIT">
|
||||
<first_editor relative-caret-position="75">
|
||||
<caret line="5" lean-forward="true" selection-start-line="5" selection-end-line="5" />
|
||||
</first_editor>
|
||||
<second_editor />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/management.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="1500">
|
||||
<caret line="100" column="20" lean-forward="true" selection-start-line="100" selection-start-column="20" selection-end-line="100" selection-end-column="20" />
|
||||
<folding>
|
||||
<element signature="e#0#65#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/videos.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="-308">
|
||||
<caret line="8" lean-forward="true" selection-start-line="8" selection-end-line="8" />
|
||||
<folding>
|
||||
<element signature="e#0#9#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/external/pytaw/pytaw/youtube.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="187">
|
||||
<caret line="174" column="11" lean-forward="true" selection-start-line="174" selection-start-column="11" selection-end-line="174" selection-end-column="11" />
|
||||
<folding>
|
||||
<element signature="e#0#18#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/local/lib/python3.6/dist-packages/django/forms/utils.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="75">
|
||||
<caret line="5" selection-start-line="5" selection-end-line="5" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/local/lib/python3.6/dist-packages/django/db/backends/sqlite3/base.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="138">
|
||||
<caret line="314" column="31" selection-start-line="314" selection-start-column="31" selection-end-line="314" selection-end-column="31" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/models.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="251">
|
||||
<caret line="280" column="32" selection-start-line="280" selection-start-column="22" selection-end-line="280" selection-end-column="32" />
|
||||
<folding>
|
||||
<element signature="e#0#14#0" expanded="true" />
|
||||
<marker date="1540831325280" expanded="true" signature="7713:7719" ph="..." />
|
||||
<marker date="1540831325280" expanded="true" signature="13276:13861" ph="..." />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/utils/youtube.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="120">
|
||||
<caret line="8" column="27" selection-start-line="8" selection-start-column="27" selection-end-line="8" selection-end-column="27" />
|
||||
<folding>
|
||||
<element signature="e#0#32#0" expanded="true" />
|
||||
<marker date="1540827548686" expanded="true" signature="184:185" ph="..." />
|
||||
<marker date="1540827548686" expanded="true" signature="761:766" ph="..." />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/synchronize.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="270">
|
||||
<caret line="18" column="71" selection-start-line="18" selection-start-column="71" selection-end-line="18" selection-end-column="71" />
|
||||
<folding>
|
||||
<element signature="e#0#12#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/views/index.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="521">
|
||||
<caret line="299" selection-start-line="299" selection-end-line="299" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/YtManagerApp/management/jobs/download_video.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="408">
|
||||
<caret line="99" column="49" selection-start-line="99" selection-start-column="49" selection-end-line="99" selection-end-column="49" />
|
||||
<folding>
|
||||
<element signature="e#0#37#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/config/config.ini">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="373">
|
||||
<caret line="44" lean-forward="true" selection-start-line="44" selection-end-line="44" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</component>
|
||||
</project>
|
@ -35,4 +35,5 @@ def schedule_delete_video(video: Video):
|
||||
:param video:
|
||||
:return:
|
||||
"""
|
||||
scheduler.instance.add_job(delete_video, args=[video])
|
||||
job = scheduler.scheduler.add_job(delete_video, args=[video])
|
||||
log.info('Scheduled delete video job video=(%s), job=%s', video, job.id)
|
||||
|
@ -25,8 +25,8 @@ def __get_valid_path(path):
|
||||
def __build_youtube_dl_params(video: Video):
|
||||
# resolve path
|
||||
pattern_dict = {
|
||||
'channel': video.subscription.channel.name,
|
||||
'channel_id': video.subscription.channel.channel_id,
|
||||
'channel': video.subscription.channel_name,
|
||||
'channel_id': video.subscription.channel_id,
|
||||
'playlist': video.subscription.name,
|
||||
'playlist_id': video.subscription.playlist_id,
|
||||
'playlist_index': "{:03d}".format(1 + video.playlist_index),
|
||||
@ -88,7 +88,7 @@ def download_video(video: Video, attempt: int = 1):
|
||||
|
||||
elif attempt <= max_attempts:
|
||||
log.warning('Re-enqueueing video (attempt %d/%d)', attempt, max_attempts)
|
||||
scheduler.instance.add_job(download_video, args=[video, attempt + 1])
|
||||
__schedule_download_video(video, attempt + 1)
|
||||
|
||||
else:
|
||||
log.error('Multiple attempts to download video %d [%s %s] failed!', video.id, video.video_id, video.name)
|
||||
@ -96,10 +96,15 @@ def download_video(video: Video, attempt: int = 1):
|
||||
video.save()
|
||||
|
||||
|
||||
def __schedule_download_video(video: Video, attempt=1):
|
||||
job = scheduler.scheduler.add_job(download_video, args=[video, attempt])
|
||||
log.info('Scheduled download video job video=(%s), attempt=%d, job=%s', video, attempt, job.id)
|
||||
|
||||
|
||||
def schedule_download_video(video: Video):
|
||||
"""
|
||||
Schedules a download video job to run immediately.
|
||||
:param video:
|
||||
:return:
|
||||
"""
|
||||
scheduler.instance.add_job(download_video, args=[video, 1])
|
||||
__schedule_download_video(video)
|
||||
|
@ -7,9 +7,8 @@ from apscheduler.triggers.cron import CronTrigger
|
||||
from YtManagerApp import scheduler
|
||||
from YtManagerApp.appconfig import settings
|
||||
from YtManagerApp.management.downloader import fetch_thumbnail, downloader_process_all, downloader_process_subscription
|
||||
from YtManagerApp.management.videos import create_video
|
||||
from YtManagerApp.models import *
|
||||
from YtManagerApp.utils.youtube import YoutubeAPI
|
||||
from YtManagerApp.utils import youtube
|
||||
|
||||
log = logging.getLogger('sync')
|
||||
__lock = Lock()
|
||||
@ -17,23 +16,25 @@ __lock = Lock()
|
||||
_ENABLE_UPDATE_STATS = False
|
||||
|
||||
|
||||
def __check_new_videos_sub(subscription: Subscription, yt_api: YoutubeAPI):
|
||||
def __check_new_videos_sub(subscription: Subscription, yt_api: youtube.YoutubeAPI):
|
||||
# Get list of videos
|
||||
for video in yt_api.list_playlist_videos(subscription.playlist_id):
|
||||
results = Video.objects.filter(video_id=video.getVideoId(), subscription=subscription)
|
||||
for item in yt_api.playlist_items(subscription.playlist_id):
|
||||
results = Video.objects.filter(video_id=item.resource_video_id, subscription=subscription)
|
||||
if len(results) == 0:
|
||||
log.info('New video for subscription %s: %s %s"', subscription, video.getVideoId(), video.getTitle())
|
||||
db_video = create_video(video, subscription)
|
||||
else:
|
||||
if not _ENABLE_UPDATE_STATS:
|
||||
continue
|
||||
db_video = results.first()
|
||||
log.info('New video for subscription %s: %s %s"', subscription, item.resource_video_id, item.title)
|
||||
Video.create(item, subscription)
|
||||
|
||||
# Update video stats - rating and view count
|
||||
stats = yt_api.get_single_video_stats(db_video.video_id)
|
||||
db_video.rating = stats.get_like_count() / (stats.get_like_count() + stats.get_dislike_count())
|
||||
db_video.views = stats.get_view_count()
|
||||
db_video.save()
|
||||
if _ENABLE_UPDATE_STATS:
|
||||
all_vids = Video.objects.filter(subscription=subscription)
|
||||
all_vids_ids = [video.video_id for video in all_vids]
|
||||
all_vids_dict = {v.video_id: v for v in all_vids}
|
||||
|
||||
for yt_video in yt_api.videos(all_vids_ids, part='id,statistics'):
|
||||
video = all_vids_dict.get(yt_video.id)
|
||||
if yt_video.like_count is not None and yt_video.dislike_count is not None:
|
||||
video.rating = yt_video.n_likes / (yt_video.n_likes + yt_video.n_dislikes)
|
||||
video.views = yt_video.n_views
|
||||
video.save()
|
||||
|
||||
|
||||
def __detect_deleted(subscription: Subscription):
|
||||
@ -82,11 +83,6 @@ def __fetch_thumbnails_obj(iterable, obj_type, id_attr):
|
||||
|
||||
|
||||
def __fetch_thumbnails():
|
||||
# Fetch thumbnails
|
||||
log.info("Fetching channel thumbnails... ")
|
||||
__fetch_thumbnails_obj(Channel.objects.filter(icon_default__istartswith='http'), 'channel', 'channel_id')
|
||||
__fetch_thumbnails_obj(Channel.objects.filter(icon_best__istartswith='http'), 'channel', 'channel_id')
|
||||
|
||||
log.info("Fetching subscription thumbnails... ")
|
||||
__fetch_thumbnails_obj(Subscription.objects.filter(icon_default__istartswith='http'), 'sub', 'playlist_id')
|
||||
__fetch_thumbnails_obj(Subscription.objects.filter(icon_best__istartswith='http'), 'sub', 'playlist_id')
|
||||
@ -107,7 +103,7 @@ def synchronize():
|
||||
|
||||
# Sync subscribed playlists/channels
|
||||
log.info("Sync - checking videos")
|
||||
yt_api = YoutubeAPI.build_public()
|
||||
yt_api = youtube.YoutubeAPI.build_public()
|
||||
for subscription in Subscription.objects.all():
|
||||
__check_new_videos_sub(subscription, yt_api)
|
||||
__detect_deleted(subscription)
|
||||
@ -128,7 +124,7 @@ def synchronize_subscription(subscription: Subscription):
|
||||
__lock.acquire()
|
||||
try:
|
||||
log.info("Running synchronization for single subscription %d [%s]", subscription.id, subscription.name)
|
||||
yt_api = YoutubeAPI.build_public()
|
||||
yt_api = youtube.YoutubeAPI.build_public()
|
||||
|
||||
log.info("Sync - checking videos")
|
||||
__check_new_videos_sub(subscription, yt_api)
|
||||
@ -148,12 +144,15 @@ def synchronize_subscription(subscription: Subscription):
|
||||
|
||||
def schedule_synchronize_global():
|
||||
trigger = CronTrigger.from_crontab(settings.get('global', 'SynchronizationSchedule'))
|
||||
scheduler.instance.add_job(synchronize, trigger, max_instances=1, coalesce=True)
|
||||
job = scheduler.scheduler.add_job(synchronize, trigger, max_instances=1, coalesce=True)
|
||||
log.info('Scheduled synchronize job job=%s', job.id)
|
||||
|
||||
|
||||
def schedule_synchronize_now():
|
||||
scheduler.instance.add_job(synchronize, max_instances=1, coalesce=True)
|
||||
job = scheduler.scheduler.add_job(synchronize, max_instances=1, coalesce=True)
|
||||
log.info('Scheduled synchronize now job job=%s', job.id)
|
||||
|
||||
|
||||
def schedule_synchronize_now_subscription(subscription: Subscription):
|
||||
scheduler.instance.add_job(synchronize_subscription, args=[subscription])
|
||||
job = scheduler.scheduler.add_job(synchronize_subscription, args=[subscription])
|
||||
log.info('Scheduled synchronize subscription job subscription=(%s), job=%s', subscription, job.id)
|
||||
|
@ -1,114 +0,0 @@
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
|
||||
from YtManagerApp.models import SubscriptionFolder, Subscription, Video, Channel
|
||||
from YtManagerApp.utils.youtube import YoutubeAPI, YoutubeChannelInfo
|
||||
|
||||
|
||||
class FolderManager(object):
|
||||
|
||||
@staticmethod
|
||||
def create_or_edit(fid, name, parent_id):
|
||||
# Create or edit
|
||||
if fid == '#':
|
||||
folder = SubscriptionFolder()
|
||||
else:
|
||||
folder = SubscriptionFolder.objects.get(id=int(fid))
|
||||
|
||||
# Set attributes
|
||||
folder.name = name
|
||||
if parent_id == '#':
|
||||
folder.parent = None
|
||||
else:
|
||||
folder.parent = SubscriptionFolder.objects.get(id=int(parent_id))
|
||||
|
||||
FolderManager.__validate(folder)
|
||||
folder.save()
|
||||
|
||||
@staticmethod
|
||||
def __validate(folder: SubscriptionFolder):
|
||||
# Make sure folder name is unique in the parent folder
|
||||
for dbFolder in SubscriptionFolder.objects.filter(parent_id=folder.parent_id):
|
||||
if dbFolder.id != folder.id and dbFolder.name == folder.name:
|
||||
raise ValueError('Folder name is not unique!')
|
||||
|
||||
# Prevent parenting loops
|
||||
current = folder
|
||||
visited = []
|
||||
|
||||
while not (current is None):
|
||||
if current in visited:
|
||||
raise ValueError('Parenting cycle detected!')
|
||||
visited.append(current)
|
||||
current = current.parent
|
||||
|
||||
@staticmethod
|
||||
def delete(fid: int):
|
||||
folder = SubscriptionFolder.objects.get(id=fid)
|
||||
folder.delete()
|
||||
|
||||
@staticmethod
|
||||
def list_videos(fid: int):
|
||||
folder = SubscriptionFolder.objects.get(id=fid)
|
||||
folder_list = []
|
||||
queue = [folder]
|
||||
while len(queue) > 0:
|
||||
folder = queue.pop()
|
||||
folder_list.append(folder)
|
||||
queue.extend(SubscriptionFolder.objects.filter(parent=folder))
|
||||
|
||||
return Video.objects.filter(subscription__parent_folder__in=folder_list).order_by('-publish_date')
|
||||
|
||||
|
||||
class SubscriptionManager(object):
|
||||
__scheduler = BackgroundScheduler()
|
||||
|
||||
@staticmethod
|
||||
def create_or_edit(sid, url, name, parent_id):
|
||||
# Create or edit
|
||||
if sid == '#':
|
||||
SubscriptionManager.create(url, parent_id, YoutubeAPI.build_public())
|
||||
else:
|
||||
sub = Subscription.objects.get(id=int(sid))
|
||||
sub.name = name
|
||||
|
||||
if parent_id == '#':
|
||||
sub.parent_folder = None
|
||||
else:
|
||||
sub.parent_folder = SubscriptionFolder.objects.get(id=int(parent_id))
|
||||
|
||||
sub.save()
|
||||
|
||||
@staticmethod
|
||||
def create(url, parent_id, yt_api: YoutubeAPI):
|
||||
sub = Subscription()
|
||||
# Set parent
|
||||
if parent_id == '#':
|
||||
sub.parent_folder = None
|
||||
else:
|
||||
sub.parent_folder = SubscriptionFolder.objects.get(id=int(parent_id))
|
||||
|
||||
# Pull information about the channel and playlist
|
||||
url_type, url_id = yt_api.parse_channel_url(url)
|
||||
|
||||
if url_type == 'playlist_id':
|
||||
info_playlist = yt_api.get_playlist_info(url_id)
|
||||
channel = SubscriptionManager.__get_or_create_channel('channel_id', info_playlist.getChannelId(), yt_api)
|
||||
sub.name = info_playlist.getTitle()
|
||||
sub.playlist_id = info_playlist.getId()
|
||||
sub.description = info_playlist.getDescription()
|
||||
sub.channel = channel
|
||||
sub.icon_default = info_playlist.getDefaultThumbnailUrl()
|
||||
sub.icon_best = info_playlist.getBestThumbnailUrl()
|
||||
|
||||
else:
|
||||
channel = SubscriptionManager.__get_or_create_channel(url_type, url_id, yt_api)
|
||||
# No point in getting the 'uploads' playlist info
|
||||
sub.name = channel.name
|
||||
sub.playlist_id = channel.upload_playlist_id
|
||||
sub.description = channel.description
|
||||
sub.channel = channel
|
||||
sub.icon_default = channel.icon_default
|
||||
sub.icon_best = channel.icon_best
|
||||
|
||||
sub.save()
|
||||
|
@ -1,25 +1,10 @@
|
||||
from YtManagerApp.models import Subscription, Video, SubscriptionFolder
|
||||
from YtManagerApp.utils.youtube import YoutubePlaylistItem
|
||||
from typing import Optional
|
||||
import re
|
||||
from django.db.models import Q
|
||||
from typing import Optional
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
def create_video(yt_video: YoutubePlaylistItem, subscription: Subscription):
|
||||
video = Video()
|
||||
video.video_id = yt_video.getVideoId()
|
||||
video.name = yt_video.getTitle()
|
||||
video.description = yt_video.getDescription()
|
||||
video.watched = False
|
||||
video.downloaded_path = None
|
||||
video.subscription = subscription
|
||||
video.playlist_index = yt_video.getPlaylistIndex()
|
||||
video.publish_date = yt_video.getPublishDate()
|
||||
video.icon_default = yt_video.getDefaultThumbnailUrl()
|
||||
video.icon_best = yt_video.getBestThumbnailUrl()
|
||||
video.save()
|
||||
return video
|
||||
from YtManagerApp.models import Subscription, Video, SubscriptionFolder
|
||||
|
||||
|
||||
def get_videos(user: User,
|
||||
|
32
YtManagerApp/migrations/0007_auto_20181029_1638.py
Normal file
32
YtManagerApp/migrations/0007_auto_20181029_1638.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Generated by Django 2.1.2 on 2018-10-29 16:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('YtManagerApp', '0006_auto_20181027_0256'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='subscription',
|
||||
name='channel',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='channel_id',
|
||||
field=models.CharField(default='test', max_length=128),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='channel_name',
|
||||
field=models.CharField(default='Unknown', max_length=1024),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Channel',
|
||||
),
|
||||
]
|
@ -6,7 +6,7 @@ from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.db.models.functions import Lower
|
||||
from YtManagerApp.utils.youtube import YoutubeAPI, YoutubeChannelInfo, YoutubePlaylistInfo
|
||||
from YtManagerApp.utils import youtube
|
||||
|
||||
# help_text = user shown text
|
||||
# verbose_name = user shown name
|
||||
@ -176,6 +176,9 @@ class SubscriptionFolder(models.Model):
|
||||
current = current.parent
|
||||
return s[:-3]
|
||||
|
||||
def __repr__(self):
|
||||
return f'folder {self.id}, name="{self.name}"'
|
||||
|
||||
def delete_folder(self, keep_subscriptions: bool):
|
||||
if keep_subscriptions:
|
||||
|
||||
@ -225,92 +228,13 @@ class SubscriptionFolder(models.Model):
|
||||
return data_collected
|
||||
|
||||
|
||||
class Channel(models.Model):
|
||||
channel_id = models.TextField(null=False, unique=True)
|
||||
username = models.TextField(null=True, unique=True)
|
||||
custom_url = models.TextField(null=True, unique=True)
|
||||
name = models.TextField()
|
||||
description = models.TextField()
|
||||
icon_default = models.TextField()
|
||||
icon_best = models.TextField()
|
||||
upload_playlist_id = models.TextField()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@staticmethod
|
||||
def find_by_channel_id(channel_id):
|
||||
result = Channel.objects.filter(channel_id=channel_id)
|
||||
if len(result) > 0:
|
||||
return result.first()
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def find_by_username(username):
|
||||
result = Channel.objects.filter(username=username)
|
||||
if len(result) > 0:
|
||||
return result.first()
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def find_by_custom_url(custom_url):
|
||||
result = Channel.objects.filter(custom_url=custom_url)
|
||||
if len(result) > 0:
|
||||
return result.first()
|
||||
return None
|
||||
|
||||
def fill(self, yt_channel_info: YoutubeChannelInfo):
|
||||
self.channel_id = yt_channel_info.getId()
|
||||
self.custom_url = yt_channel_info.getCustomUrl()
|
||||
self.name = yt_channel_info.getTitle()
|
||||
self.description = yt_channel_info.getDescription()
|
||||
self.icon_default = yt_channel_info.getDefaultThumbnailUrl()
|
||||
self.icon_best = yt_channel_info.getBestThumbnailUrl()
|
||||
self.upload_playlist_id = yt_channel_info.getUploadsPlaylist()
|
||||
self.save()
|
||||
|
||||
@staticmethod
|
||||
def get_or_create(url_type: str, url_id: str, yt_api: YoutubeAPI):
|
||||
channel: Channel = None
|
||||
info_channel: YoutubeChannelInfo = None
|
||||
|
||||
if url_type == 'user':
|
||||
channel = Channel.find_by_username(url_id)
|
||||
if not channel:
|
||||
info_channel = yt_api.get_channel_info_by_username(url_id)
|
||||
channel = Channel.find_by_channel_id(info_channel.getId())
|
||||
|
||||
elif url_type == 'channel_id':
|
||||
channel = Channel.find_by_channel_id(url_id)
|
||||
if not channel:
|
||||
info_channel = yt_api.get_channel_info(url_id)
|
||||
|
||||
elif url_type == 'channel_custom':
|
||||
channel = Channel.find_by_custom_url(url_id)
|
||||
if not channel:
|
||||
found_channel_id = yt_api.search_channel(url_id)
|
||||
channel = Channel.find_by_channel_id(found_channel_id)
|
||||
if not channel:
|
||||
info_channel = yt_api.get_channel_info(found_channel_id)
|
||||
|
||||
# If we downloaded information about the channel, store information
|
||||
# about the channel here.
|
||||
if info_channel:
|
||||
if not channel:
|
||||
channel = Channel()
|
||||
if url_type == 'user':
|
||||
channel.username = url_id
|
||||
channel.fill(info_channel)
|
||||
|
||||
return channel
|
||||
|
||||
|
||||
class Subscription(models.Model):
|
||||
name = models.CharField(null=False, max_length=1024)
|
||||
parent_folder = models.ForeignKey(SubscriptionFolder, on_delete=models.CASCADE, null=True, blank=True)
|
||||
playlist_id = models.CharField(null=False, max_length=128)
|
||||
description = models.TextField()
|
||||
channel = models.ForeignKey(Channel, on_delete=models.CASCADE)
|
||||
channel_id = models.CharField(max_length=128)
|
||||
channel_name = models.CharField(max_length=1024)
|
||||
icon_default = models.CharField(max_length=1024)
|
||||
icon_best = models.CharField(max_length=1024)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
@ -327,30 +251,42 @@ class Subscription(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def fill_from_playlist(self, info_playlist: YoutubePlaylistInfo):
|
||||
self.name = info_playlist.getTitle()
|
||||
self.playlist_id = info_playlist.getId()
|
||||
self.description = info_playlist.getDescription()
|
||||
self.icon_default = info_playlist.getDefaultThumbnailUrl()
|
||||
self.icon_best = info_playlist.getBestThumbnailUrl()
|
||||
def __repr__(self):
|
||||
return f'subscription {self.id}, name="{self.name}", playlist_id="{self.playlist_id}"'
|
||||
|
||||
def copy_from_channel(self):
|
||||
def fill_from_playlist(self, info_playlist: youtube.Playlist):
|
||||
self.name = info_playlist.title
|
||||
self.playlist_id = info_playlist.id
|
||||
self.description = info_playlist.description
|
||||
self.channel_id = info_playlist.channel_id
|
||||
self.channel_name = info_playlist.channel_title
|
||||
self.icon_default = youtube.default_thumbnail(info_playlist).url
|
||||
self.icon_best = youtube.best_thumbnail(info_playlist).url
|
||||
|
||||
def copy_from_channel(self, info_channel: youtube.Channel):
|
||||
# No point in storing info about the 'uploads from X' playlist
|
||||
self.name = self.channel.name
|
||||
self.playlist_id = self.channel.upload_playlist_id
|
||||
self.description = self.channel.description
|
||||
self.icon_default = self.channel.icon_default
|
||||
self.icon_best = self.channel.icon_best
|
||||
self.name = info_channel.title
|
||||
self.playlist_id = info_channel.uploads_playlist.id
|
||||
self.description = info_channel.description
|
||||
self.channel_id = info_channel.id
|
||||
self.channel_name = info_channel.title
|
||||
self.icon_default = youtube.default_thumbnail(info_channel).url
|
||||
self.icon_best = youtube.best_thumbnail(info_channel).url
|
||||
|
||||
def fetch_from_url(self, url, yt_api: youtube.YoutubeAPI):
|
||||
url_parsed = yt_api.parse_url(url)
|
||||
if 'playlist' in url_parsed:
|
||||
info_playlist = yt_api.playlist(url=url)
|
||||
if info_playlist is None:
|
||||
raise ValueError('Invalid playlist ID!')
|
||||
|
||||
def fetch_from_url(self, url, yt_api: YoutubeAPI):
|
||||
url_type, url_id = yt_api.parse_channel_url(url)
|
||||
if url_type == 'playlist_id':
|
||||
info_playlist = yt_api.get_playlist_info(url_id)
|
||||
self.channel = Channel.get_or_create('channel_id', info_playlist.getChannelId(), yt_api)
|
||||
self.fill_from_playlist(info_playlist)
|
||||
else:
|
||||
self.channel = Channel.get_or_create(url_type, url_id, yt_api)
|
||||
self.copy_from_channel()
|
||||
info_channel = yt_api.channel(url=url)
|
||||
if info_channel is None:
|
||||
raise ValueError('Cannot find channel!')
|
||||
|
||||
self.copy_from_channel(info_channel)
|
||||
|
||||
def delete_subscription(self, keep_downloaded_videos: bool):
|
||||
self.delete()
|
||||
@ -383,6 +319,22 @@ class Video(models.Model):
|
||||
views = models.IntegerField(null=False, default=0)
|
||||
rating = models.FloatField(null=False, default=0.5)
|
||||
|
||||
@staticmethod
|
||||
def create(playlist_item: youtube.PlaylistItem, subscription: Subscription):
|
||||
video = Video()
|
||||
video.video_id = playlist_item.resource_video_id
|
||||
video.name = playlist_item.title
|
||||
video.description = playlist_item.description
|
||||
video.watched = False
|
||||
video.downloaded_path = None
|
||||
video.subscription = subscription
|
||||
video.playlist_index = playlist_item.position
|
||||
video.publish_date = playlist_item.published_at
|
||||
video.icon_default = youtube.default_thumbnail(playlist_item).url
|
||||
video.icon_best = youtube.best_thumbnail(playlist_item).url
|
||||
video.save()
|
||||
return video
|
||||
|
||||
def mark_watched(self):
|
||||
self.watched = True
|
||||
self.save()
|
||||
@ -428,3 +380,6 @@ class Video(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
return f'video {self.id}, video_id="{self.video_id}"'
|
||||
|
@ -2,12 +2,12 @@ import logging
|
||||
import sys
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
|
||||
instance: BackgroundScheduler = None
|
||||
scheduler: BackgroundScheduler = None
|
||||
|
||||
|
||||
def initialize_scheduler():
|
||||
from .appconfig import settings
|
||||
global instance
|
||||
global scheduler
|
||||
|
||||
logger = logging.getLogger('scheduler')
|
||||
executors = {
|
||||
@ -17,8 +17,8 @@ def initialize_scheduler():
|
||||
}
|
||||
}
|
||||
job_defaults = {
|
||||
'misfire_grace_time': sys.maxsize
|
||||
'misfire_grace_time': 60 * 60 * 24 * 365 # 1 year
|
||||
}
|
||||
|
||||
instance = BackgroundScheduler(logger=logger, executors=executors, job_defaults=job_defaults)
|
||||
instance.start()
|
||||
scheduler = BackgroundScheduler(logger=logger, executors=executors, job_defaults=job_defaults)
|
||||
scheduler.start()
|
||||
|
@ -1,32 +0,0 @@
|
||||
import itertools
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
def first_true(*args, default=False, pred=None):
|
||||
"""Returns the first true value in the iterable.
|
||||
|
||||
If no true value is found, returns *default*
|
||||
|
||||
If *pred* is not None, returns the first item
|
||||
for which pred(item) is true.
|
||||
|
||||
"""
|
||||
# first_true([a,b,c], x) --> a or b or c or x
|
||||
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
|
||||
return next(filter(pred, args), default)
|
||||
|
||||
|
||||
def as_chunks(iterable: Iterable, chunk_size: int):
|
||||
"""
|
||||
Iterates an iterable in chunks of chunk_size elements.
|
||||
:param iterable: An iterable containing items to iterate.
|
||||
:param chunk_size: Chunk size
|
||||
:return: Returns a generator which will yield chunks of size chunk_size
|
||||
"""
|
||||
|
||||
it = iter(iterable)
|
||||
while True:
|
||||
chunk = tuple(itertools.islice(it, chunk_size))
|
||||
if not chunk:
|
||||
return
|
||||
yield chunk
|
@ -1,285 +1,48 @@
|
||||
from googleapiclient.discovery import build
|
||||
from googleapiclient.errors import Error as APIError
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from django.conf import settings
|
||||
import re
|
||||
from YtManagerApp.utils.iterutils import as_chunks
|
||||
|
||||
API_SERVICE_NAME = 'youtube'
|
||||
API_VERSION = 'v3'
|
||||
|
||||
YOUTUBE_LIST_LIMIT = 50
|
||||
from external.pytaw.pytaw.youtube import YouTube, Channel, Playlist, PlaylistItem, Thumbnail, InvalidURL, Resource, Video
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class YoutubeException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class YoutubeInvalidURLException(YoutubeException):
|
||||
pass
|
||||
|
||||
|
||||
class YoutubeChannelNotFoundException(YoutubeException):
|
||||
pass
|
||||
|
||||
|
||||
class YoutubeUserNotFoundException(YoutubeException):
|
||||
pass
|
||||
|
||||
|
||||
class YoutubePlaylistNotFoundException(YoutubeException):
|
||||
pass
|
||||
|
||||
|
||||
class YoutubeVideoNotFoundException(YoutubeException):
|
||||
pass
|
||||
|
||||
|
||||
class YoutubeChannelInfo(object):
|
||||
def __init__(self, result_dict):
|
||||
self.__id = result_dict['id']
|
||||
self.__snippet = result_dict['snippet']
|
||||
self.__contentDetails = result_dict['contentDetails']
|
||||
|
||||
def getId(self):
|
||||
return self.__id
|
||||
|
||||
def getTitle(self):
|
||||
return self.__snippet['title']
|
||||
|
||||
def getDescription(self):
|
||||
return self.__snippet['description']
|
||||
|
||||
def getCustomUrl(self):
|
||||
try:
|
||||
return self.__snippet['customUrl']
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def getDefaultThumbnailUrl(self):
|
||||
return self.__snippet['thumbnails']['default']['url']
|
||||
|
||||
def getBestThumbnailUrl(self):
|
||||
best_url = None
|
||||
best_res = 0
|
||||
for _, thumb in self.__snippet['thumbnails'].items():
|
||||
res = thumb['width'] * thumb['height']
|
||||
if res > best_res:
|
||||
best_res = res
|
||||
best_url = thumb['url']
|
||||
return best_url
|
||||
|
||||
def getUploadsPlaylist(self):
|
||||
return self.__contentDetails['relatedPlaylists']['uploads']
|
||||
|
||||
|
||||
class YoutubePlaylistInfo(object):
|
||||
def __init__(self, result_dict):
|
||||
self.__id = result_dict['id']
|
||||
self.__snippet = result_dict['snippet']
|
||||
|
||||
def getId(self):
|
||||
return self.__id
|
||||
|
||||
def getChannelId(self):
|
||||
return self.__snippet['channelId']
|
||||
|
||||
def getTitle(self):
|
||||
return self.__snippet['title']
|
||||
|
||||
def getDescription(self):
|
||||
return self.__snippet['description']
|
||||
|
||||
def getDefaultThumbnailUrl(self):
|
||||
return self.__snippet['thumbnails']['default']['url']
|
||||
|
||||
def getBestThumbnailUrl(self):
|
||||
best_url = None
|
||||
best_res = 0
|
||||
for _, thumb in self.__snippet['thumbnails'].items():
|
||||
res = thumb['width'] * thumb['height']
|
||||
if res > best_res:
|
||||
best_res = res
|
||||
best_url = thumb['url']
|
||||
return best_url
|
||||
|
||||
|
||||
class YoutubePlaylistItem(object):
|
||||
def __init__(self, result_dict):
|
||||
self.__snippet = result_dict['snippet']
|
||||
|
||||
def getVideoId(self):
|
||||
return self.__snippet['resourceId']['videoId']
|
||||
|
||||
def getPublishDate(self):
|
||||
return self.__snippet['publishedAt']
|
||||
|
||||
def getTitle(self):
|
||||
return self.__snippet['title']
|
||||
|
||||
def getDescription(self):
|
||||
return self.__snippet['description']
|
||||
|
||||
def getDefaultThumbnailUrl(self):
|
||||
return self.__snippet['thumbnails']['default']['url']
|
||||
|
||||
def getBestThumbnailUrl(self):
|
||||
best_url = None
|
||||
best_res = 0
|
||||
for _, thumb in self.__snippet['thumbnails'].items():
|
||||
res = thumb['width'] * thumb['height']
|
||||
if res > best_res:
|
||||
best_res = res
|
||||
best_url = thumb['url']
|
||||
return best_url
|
||||
|
||||
def getPlaylistIndex(self):
|
||||
return self.__snippet['position']
|
||||
|
||||
|
||||
class YoutubeVideoStatistics(object):
|
||||
def __init__(self, result_dict):
|
||||
self.id = result_dict['id']
|
||||
self.stats = result_dict['statistics']
|
||||
|
||||
def get_view_count(self):
|
||||
return int(self.stats['viewCount'])
|
||||
|
||||
def get_like_count(self):
|
||||
return int(self.stats['likeCount'])
|
||||
|
||||
def get_dislike_count(self):
|
||||
return int(self.stats['dislikeCount'])
|
||||
|
||||
def get_favorite_count(self):
|
||||
return int(self.stats['favoriteCount'])
|
||||
|
||||
def get_comment_count(self):
|
||||
return int(self.stats['commentCount'])
|
||||
|
||||
|
||||
class YoutubeAPI(object):
|
||||
def __init__(self, service):
|
||||
self.service = service
|
||||
class YoutubeAPI(YouTube):
|
||||
|
||||
@staticmethod
|
||||
def build_public() -> 'YoutubeAPI':
|
||||
service = build(API_SERVICE_NAME, API_VERSION, developerKey=settings.YOUTUBE_API_KEY)
|
||||
return YoutubeAPI(service)
|
||||
|
||||
@staticmethod
|
||||
def parse_channel_url(url):
|
||||
"""
|
||||
Parses given channel url, returns a tuple of the form (type, value), where type can be one of:
|
||||
* channel_id
|
||||
* channel_custom
|
||||
* user
|
||||
* playlist_id
|
||||
:param url: URL to parse
|
||||
:return: (type, value) tuple
|
||||
"""
|
||||
match = re.search(r'youtube\.com/.*[&?]list=([^?&/]+)', url)
|
||||
if match:
|
||||
return 'playlist_id', match.group(1)
|
||||
|
||||
match = re.search(r'youtube\.com/user/([^?&/]+)', url)
|
||||
if match:
|
||||
return 'user', match.group(1)
|
||||
|
||||
match = re.search(r'youtube\.com/channel/([^?&/]+)', url)
|
||||
if match:
|
||||
return 'channel_id', match.group(1)
|
||||
|
||||
match = re.search(r'youtube\.com/(?:c/)?([^?&/]+)', url)
|
||||
if match:
|
||||
return 'channel_custom', match.group(1)
|
||||
|
||||
raise YoutubeInvalidURLException('Unrecognized URL format!')
|
||||
|
||||
def get_playlist_info(self, list_id) -> YoutubePlaylistInfo:
|
||||
result = self.service.playlists()\
|
||||
.list(part='snippet', id=list_id)\
|
||||
.execute()
|
||||
|
||||
if len(result['items']) <= 0:
|
||||
raise YoutubePlaylistNotFoundException("Invalid playlist ID.")
|
||||
|
||||
return YoutubePlaylistInfo(result['items'][0])
|
||||
|
||||
def get_channel_info_by_username(self, user) -> YoutubeChannelInfo:
|
||||
result = self.service.channels()\
|
||||
.list(part='snippet,contentDetails', forUsername=user)\
|
||||
.execute()
|
||||
|
||||
if len(result['items']) <= 0:
|
||||
raise YoutubeUserNotFoundException('Invalid user.')
|
||||
|
||||
return YoutubeChannelInfo(result['items'][0])
|
||||
|
||||
def get_channel_info(self, channel_id) -> YoutubeChannelInfo:
|
||||
result = self.service.channels()\
|
||||
.list(part='snippet,contentDetails', id=channel_id)\
|
||||
.execute()
|
||||
|
||||
if len(result['items']) <= 0:
|
||||
raise YoutubeChannelNotFoundException('Invalid channel ID.')
|
||||
|
||||
return YoutubeChannelInfo(result['items'][0])
|
||||
|
||||
def search_channel(self, custom) -> str:
|
||||
result = self.service.search()\
|
||||
.list(part='id', q=custom, type='channel')\
|
||||
.execute()
|
||||
|
||||
if len(result['items']) <= 0:
|
||||
raise YoutubeChannelNotFoundException('Could not find channel!')
|
||||
|
||||
channel_result = result['items'][0]
|
||||
return channel_result['id']['channelId']
|
||||
|
||||
def list_playlist_videos(self, playlist_id):
|
||||
kwargs = {
|
||||
"part": "snippet",
|
||||
"maxResults": 50,
|
||||
"playlistId": playlist_id
|
||||
}
|
||||
last_page = False
|
||||
|
||||
while not last_page:
|
||||
result = self.service.playlistItems()\
|
||||
.list(**kwargs)\
|
||||
.execute()
|
||||
|
||||
for item in result['items']:
|
||||
yield YoutubePlaylistItem(item)
|
||||
|
||||
if 'nextPageToken' in result:
|
||||
kwargs['pageToken'] = result['nextPageToken']
|
||||
else:
|
||||
last_page = True
|
||||
|
||||
def get_single_video_stats(self, video_id) -> YoutubeVideoStatistics:
|
||||
result = list(self.get_video_stats([video_id]))
|
||||
if len(result) < 1:
|
||||
raise YoutubeVideoNotFoundException('Could not find video with id ' + video_id + '!')
|
||||
return result[0]
|
||||
|
||||
def get_video_stats(self, video_id_list):
|
||||
for chunk in as_chunks(video_id_list, YOUTUBE_LIST_LIMIT):
|
||||
kwargs = {
|
||||
"part": "statistics",
|
||||
"maxResults": YOUTUBE_LIST_LIMIT,
|
||||
"id": ','.join(chunk)
|
||||
}
|
||||
result = self.service.videos()\
|
||||
.list(**kwargs)\
|
||||
.execute()
|
||||
|
||||
for item in result['items']:
|
||||
yield YoutubeVideoStatistics(item)
|
||||
return YoutubeAPI(key=settings.YOUTUBE_API_KEY)
|
||||
|
||||
# @staticmethod
|
||||
# def build_oauth() -> 'YoutubeAPI':
|
||||
# flow =
|
||||
# credentials =
|
||||
# service = build(API_SERVICE_NAME, API_VERSION, credentials)
|
||||
|
||||
|
||||
def default_thumbnail(resource: Resource) -> Optional[Thumbnail]:
|
||||
"""
|
||||
Gets the default thumbnail for a resource.
|
||||
Searches in the list of thumbnails for one with the label 'default', or takes the first one.
|
||||
:param resource:
|
||||
:return:
|
||||
"""
|
||||
thumbs = getattr(resource, 'thumbnails', None)
|
||||
|
||||
if thumbs is None or len(thumbs) <= 0:
|
||||
return None
|
||||
|
||||
return next(
|
||||
(i for i in thumbs if i.id == 'default'),
|
||||
thumbs[0]
|
||||
)
|
||||
|
||||
|
||||
def best_thumbnail(resource: Resource) -> Optional[Thumbnail]:
|
||||
"""
|
||||
Gets the best thumbnail available for a resource.
|
||||
:param resource:
|
||||
:return:
|
||||
"""
|
||||
thumbs = getattr(resource, 'thumbnails', None)
|
||||
|
||||
if thumbs is None or len(thumbs) <= 0:
|
||||
return None
|
||||
|
||||
return max(thumbs, key=lambda t: t.width * t.height)
|
@ -1,4 +1,4 @@
|
||||
from crispy_forms.helper import FormHelperpython3
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout, Field, HTML
|
||||
from django import forms
|
||||
from django.contrib.auth.decorators import login_required
|
||||
@ -175,7 +175,8 @@ class SubscriptionFolderForm(forms.ModelForm):
|
||||
args_id.append(~Q(id=self.instance.id))
|
||||
|
||||
if SubscriptionFolder.objects.filter(parent=parent, name__iexact=name, *args_id).count() > 0:
|
||||
raise forms.ValidationError('A folder with the same name already exists in the given parent directory!', code='already_exists')
|
||||
raise forms.ValidationError(
|
||||
'A folder with the same name already exists in the given parent directory!', code='already_exists')
|
||||
|
||||
# Check for cycles
|
||||
if self.instance is not None:
|
||||
@ -238,6 +239,7 @@ class CreateSubscriptionForm(forms.ModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.yt_api = youtube.YoutubeAPI.build_public()
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_tag = False
|
||||
self.helper.layout = Layout(
|
||||
@ -252,11 +254,18 @@ class CreateSubscriptionForm(forms.ModelForm):
|
||||
)
|
||||
|
||||
def clean_playlist_url(self):
|
||||
playlist_url = self.cleaned_data['playlist_url']
|
||||
playlist_url: str = self.cleaned_data['playlist_url']
|
||||
try:
|
||||
youtube.YoutubeAPI.parse_channel_url(playlist_url)
|
||||
except youtube.YoutubeInvalidURLException:
|
||||
raise forms.ValidationError('Invalid playlist/channel URL, or not in a recognized format.')
|
||||
parsed_url = self.yt_api.parse_url(playlist_url)
|
||||
except youtube.InvalidURL as e:
|
||||
raise forms.ValidationError(str(e))
|
||||
|
||||
is_playlist = 'playlist' in parsed_url
|
||||
is_channel = parsed_url['type'] in ('channel', 'user', 'channel_custom')
|
||||
|
||||
if not is_channel and not is_playlist:
|
||||
raise forms.ValidationError('The given URL must link to a channel or a playlist!')
|
||||
|
||||
return playlist_url
|
||||
|
||||
|
||||
@ -269,21 +278,22 @@ class CreateSubscriptionModal(LoginRequiredMixin, ModalMixin, CreateView):
|
||||
api = youtube.YoutubeAPI.build_public()
|
||||
try:
|
||||
form.instance.fetch_from_url(form.cleaned_data['playlist_url'], api)
|
||||
except youtube.YoutubeChannelNotFoundException:
|
||||
return self.modal_response(
|
||||
form, False, 'Could not find a channel based on the given URL. Please verify that the URL is correct.')
|
||||
except youtube.YoutubeUserNotFoundException:
|
||||
return self.modal_response(
|
||||
form, False, 'Could not find an user based on the given URL. Please verify that the URL is correct.')
|
||||
except youtube.YoutubePlaylistNotFoundException:
|
||||
return self.modal_response(
|
||||
form, False, 'Could not find a playlist based on the given URL. Please verify that the URL is correct.')
|
||||
except youtube.YoutubeException as e:
|
||||
return self.modal_response(
|
||||
form, False, str(e))
|
||||
except youtube.APIError as e:
|
||||
return self.modal_response(
|
||||
form, False, 'An error occurred while communicating with the YouTube API: ' + str(e))
|
||||
except youtube.InvalidURL as e:
|
||||
return self.modal_response(form, False, str(e))
|
||||
except ValueError as e:
|
||||
return self.modal_response(form, False, str(e))
|
||||
# except youtube.YoutubeUserNotFoundException:
|
||||
# return self.modal_response(
|
||||
# form, False, 'Could not find an user based on the given URL. Please verify that the URL is correct.')
|
||||
# except youtube.YoutubePlaylistNotFoundException:
|
||||
# return self.modal_response(
|
||||
# form, False, 'Could not find a playlist based on the given URL. Please verify that the URL is correct.')
|
||||
# except youtube.YoutubeException as e:
|
||||
# return self.modal_response(
|
||||
# form, False, str(e))
|
||||
# except youtube.APIError as e:
|
||||
# return self.modal_response(
|
||||
# form, False, 'An error occurred while communicating with the YouTube API: ' + str(e))
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
@ -41,7 +41,7 @@ LogLevel=DEBUG
|
||||
;DownloadOrder=playlist
|
||||
|
||||
; Path where downloaded videos are stored
|
||||
DownloadPath=D:\\Dev\\youtube-channel-manager\\temp\\download
|
||||
DownloadPath=temp/download
|
||||
|
||||
; A pattern which describes how downloaded files are organized. Extensions are automatically appended.
|
||||
; Supported fields: channel, channel_id, playlist, playlist_id, playlist_index, title, id
|
||||
|
0
external/__init__.py
vendored
Normal file
0
external/__init__.py
vendored
Normal file
19
external/pytaw/.gitignore
vendored
Normal file
19
external/pytaw/.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
*.bak
|
||||
*.egg
|
||||
*.egg-info/
|
||||
*.eggs/
|
||||
*.pyproj
|
||||
*.sln
|
||||
*.vs/
|
||||
*~
|
||||
.DS_Store
|
||||
.cache/
|
||||
.coverage
|
||||
.idea/
|
||||
.tox/
|
||||
_build/
|
||||
build/
|
||||
dist/
|
||||
|
||||
__pycache__/
|
||||
*.ini
|
3
external/pytaw/.pytaw.conf
vendored
Normal file
3
external/pytaw/.pytaw.conf
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
; by default pytaw will look for this file (".pytaw.conf") in the user's home directory
|
||||
[youtube]
|
||||
developer_key = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
29
external/pytaw/README.md
vendored
Normal file
29
external/pytaw/README.md
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
# PYTAW: Python YouTube API Wrapper
|
||||
|
||||
###Note
|
||||
This library is copied from [https://github.com/chibicitiberiu/pytaw/tree/improvements](https://github.com/chibicitiberiu/pytaw/tree/improvements).
|
||||
|
||||
|
||||
```python
|
||||
>>> from pytaw import YouTube
|
||||
>>> youtube = YouTube(key='your_api_key')
|
||||
>>> video = youtube.video('4vuW6tQ0218')
|
||||
>>> video.title
|
||||
'Monty Python - Dead Parrot'
|
||||
>>> video.published_at
|
||||
datetime.datetime(2007, 2, 14, 13, 55, 51, tzinfo=tzutc())
|
||||
>>> channel = video.channel
|
||||
>>> channel.title
|
||||
'Chadner'
|
||||
>>> search = youtube.search(q='monty python')
|
||||
>>> search[0]
|
||||
<Channel UCGm3CO6LPcN-Y7HIuyE0Rew "Monty Python">
|
||||
>>> for r in search[:5]:
|
||||
... print(r)
|
||||
...
|
||||
Monty Python
|
||||
Chemist Sketch - Monty Python's Flying Circus
|
||||
A Selection of Sketches from "Monty Python's Flying Circus" - #4
|
||||
Monty Python - Dead Parrot
|
||||
Monty Python And the holy grail
|
||||
```
|
0
external/pytaw/__init__.py
vendored
Normal file
0
external/pytaw/__init__.py
vendored
Normal file
20
external/pytaw/docs/Makefile
vendored
Normal file
20
external/pytaw/docs/Makefile
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = pytaw
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
169
external/pytaw/docs/conf.py
vendored
Normal file
169
external/pytaw/docs/conf.py
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# pytaw documentation build configuration file, created by
|
||||
# sphinx-quickstart on Mon Nov 27 19:26:35 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'pytaw'
|
||||
copyright = '2017, 6000hulls'
|
||||
author = '6000hulls'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# This is required for the alabaster theme
|
||||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||
html_sidebars = {
|
||||
'**': [
|
||||
'relations.html', # needs 'show_related': True theme option to display
|
||||
'searchbox.html',
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'pytawdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'pytaw.tex', 'pytaw Documentation',
|
||||
'6000hulls', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'pytaw', 'pytaw Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'pytaw', 'pytaw Documentation',
|
||||
author, 'pytaw', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
18
external/pytaw/docs/index.rst
vendored
Normal file
18
external/pytaw/docs/index.rst
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
PYTAW: Python YouTube API Wrapper
|
||||
=================================
|
||||
|
||||
It's a wrapper for the YouTube python API. Written in python.
|
||||
|
||||
.. automodule:: pytaw.youtube
|
||||
:members:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
36
external/pytaw/docs/make.bat
vendored
Normal file
36
external/pytaw/docs/make.bat
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set SPHINXPROJ=pytaw
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
13
external/pytaw/main_test.py
vendored
Normal file
13
external/pytaw/main_test.py
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
import pytaw
|
||||
|
||||
yt = pytaw.YouTube(key='AIzaSyBabzE4Bup77WexdLMa9rN9z-wJidEfNX8')
|
||||
c = yt.channel('UCmmPgObSUPw1HL2lq6H4ffA')
|
||||
|
||||
uploads_playlist = c.uploads_playlist
|
||||
print(repr(uploads_playlist))
|
||||
|
||||
uploads_list = list(uploads_playlist.items)
|
||||
for item in uploads_list:
|
||||
print(item.position, '...', repr(item), ' .... ', repr(item.video))
|
||||
print(item.thumbnails)
|
||||
break
|
1
external/pytaw/pytaw/__init__.py
vendored
Normal file
1
external/pytaw/pytaw/__init__.py
vendored
Normal file
@ -0,0 +1 @@
|
||||
from .youtube import YouTube
|
92
external/pytaw/pytaw/utils.py
vendored
Normal file
92
external/pytaw/pytaw/utils.py
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
import re
|
||||
import urllib.parse
|
||||
import typing
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import dateutil.parser
|
||||
import itertools
|
||||
|
||||
def string_to_datetime(string):
|
||||
if string is None:
|
||||
return None
|
||||
else:
|
||||
return dateutil.parser.parse(string)
|
||||
|
||||
|
||||
def datetime_to_string(dt):
|
||||
if dt is None:
|
||||
return None
|
||||
if dt.tzinfo is None:
|
||||
dt = dt.astimezone(timezone.utc)
|
||||
return dt.isoformat()
|
||||
|
||||
|
||||
def youtube_url_to_id(url):
|
||||
"""Extract video id from a youtube url.
|
||||
|
||||
If parsing fails, try regex. If that fails, return None.
|
||||
|
||||
The regex is from somewhere in this thread, I think:
|
||||
https://stackoverflow.com/questions/3452546/how-do-i-get-the-youtube-video-id-from-a-url
|
||||
|
||||
"""
|
||||
url = urllib.parse.unquote(url)
|
||||
url_data = urllib.parse.urlparse(url)
|
||||
query = urllib.parse.parse_qs(url_data.query)
|
||||
try:
|
||||
# parse the url for a video query
|
||||
return query["v"][0]
|
||||
except KeyError:
|
||||
# use regex to try and extract id
|
||||
match = re.search(
|
||||
r"((?<=(v|V)/)|(?<=be/)|(?<=(\?|\&)v=)|(?<=embed/))([\w-]+)",
|
||||
url,
|
||||
)
|
||||
if match:
|
||||
return match.group()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def youtube_duration_to_seconds(value):
|
||||
"""Convert youtube (ISO 8601) duration to seconds.
|
||||
|
||||
https://en.wikipedia.org/wiki/ISO_8601#Durations
|
||||
https://regex101.com/r/ALmmSS/1
|
||||
|
||||
"""
|
||||
iso8601 = r"P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?T?(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||
match = re.match(iso8601, value)
|
||||
if match is None:
|
||||
return None
|
||||
|
||||
group_names = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds']
|
||||
d = dict()
|
||||
for name, group in zip(group_names, match.groups(default=0)):
|
||||
d[name] = int(group)
|
||||
|
||||
return int(
|
||||
d['years']*365*24*60*60 +
|
||||
d['months']*30*24*60*60 +
|
||||
d['weeks']*7*24*60*60 +
|
||||
d['days']*24*60*60 +
|
||||
d['hours']*60*60 +
|
||||
d['minutes']*60 +
|
||||
d['seconds']
|
||||
)
|
||||
|
||||
|
||||
def iterate_chunks(iterable: typing.Iterable, chunk_size: int):
|
||||
"""
|
||||
Iterates an iterable in chunks of chunk_size elements.
|
||||
:param iterable: An iterable containing items to iterate.
|
||||
:param chunk_size: Chunk size
|
||||
:return: Returns a generator which will yield chunks of size chunk_size
|
||||
"""
|
||||
|
||||
it = iter(iterable)
|
||||
while True:
|
||||
chunk = tuple(itertools.islice(it, chunk_size))
|
||||
if not chunk:
|
||||
return
|
||||
yield chunk
|
1055
external/pytaw/pytaw/youtube.py
vendored
Normal file
1055
external/pytaw/pytaw/youtube.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
external/pytaw/setup.py
vendored
Normal file
12
external/pytaw/setup.py
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='pytaw',
|
||||
version='0.0.1',
|
||||
packages=['pytaw'],
|
||||
url='https://github.com/6000hulls/pytaw',
|
||||
license='',
|
||||
author='6000hulls',
|
||||
author_email='6000hulls@gmail.com',
|
||||
description='PYTAW: Python YouTube API Wrapper'
|
||||
)
|
0
external/pytaw/tests/__init__.py
vendored
Normal file
0
external/pytaw/tests/__init__.py
vendored
Normal file
165
external/pytaw/tests/test_pytaw.py
vendored
Normal file
165
external/pytaw/tests/test_pytaw.py
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
import pytest
|
||||
import logging
|
||||
import sys
|
||||
import collections
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from googleapiclient.errors import HttpError
|
||||
|
||||
from pytaw import YouTube
|
||||
from pytaw.youtube import Resource, Video, AttributeDef
|
||||
|
||||
|
||||
logging.basicConfig(stream=sys.stdout) # show log output when run with pytest -s
|
||||
log = logging.getLogger(__name__)
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def youtube():
|
||||
"""A YouTube instance initialised with a developer key loaded from config.ini"""
|
||||
return YouTube()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def video(youtube):
|
||||
"""A Video instance for the classic video 'Me at the zoo'"""
|
||||
return youtube.video('jNQXAC9IVRw')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def channel(youtube):
|
||||
"""A Channel instance for the 'YouTube Help' channel"""
|
||||
return youtube.channel('UCMDQxm7cUx3yXkfeHa5zJIQ')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def search(youtube):
|
||||
"""A ListResponse instance corresponding to a search for the query 'python'"""
|
||||
return youtube.search()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def video_search(youtube):
|
||||
"""A ListResponse instance corresponding to a video search for the query 'python'"""
|
||||
return youtube.search(q='python', type='video')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def video_search_array(youtube):
|
||||
"""An array of video searches with a wide range of results (zero to millions)."""
|
||||
one_minute_ago = datetime.utcnow() - timedelta(minutes=1)
|
||||
five_minutes_ago = datetime.utcnow() - timedelta(minutes=5)
|
||||
return [
|
||||
#
|
||||
# no results
|
||||
youtube.search(q='minecraft', type='video', publishedBefore=datetime(2000, 1, 1)),
|
||||
#
|
||||
# less than 100 results
|
||||
youtube.search(q='minecraft', type='video', publishedBefore=datetime(2005, 7, 1)),
|
||||
#
|
||||
# over 100 results
|
||||
youtube.search(q='minecraft', type='video', publishedBefore=datetime(2006, 1, 1)),
|
||||
#
|
||||
# variable number of results (hundreds or thousands...?)
|
||||
youtube.search(q='minecraft', type='video', publishedAfter=one_minute_ago),
|
||||
youtube.search(q='minecraft', type='video', publishedAfter=five_minutes_ago),
|
||||
#
|
||||
# over a million results
|
||||
youtube.search(q='minecraft', type='video'),
|
||||
youtube.search(q='minecraft'),
|
||||
]
|
||||
|
||||
|
||||
class TestResource:
|
||||
|
||||
def test_equality(self, search):
|
||||
a = search[0]
|
||||
b = search[0]
|
||||
c = search[1]
|
||||
assert a == b
|
||||
assert a != c
|
||||
|
||||
def test_unknown_attribute(self, video):
|
||||
with pytest.raises(AttributeError):
|
||||
_ = video.attribute_name_which_definitely_will_never_exist
|
||||
|
||||
def test_unknown_part_in_attributedef(self, video):
|
||||
video.ATTRIBUTE_DEFS['x'] = AttributeDef('nonexistant_part', 'x')
|
||||
with pytest.raises(HttpError):
|
||||
_ = video.x
|
||||
|
||||
def test_unknown_attribute_name_in_attributedef(self, video):
|
||||
video.ATTRIBUTE_DEFS['x'] = AttributeDef('snippet', 'nonexistant_attribute')
|
||||
assert video.x is None
|
||||
|
||||
|
||||
class TestVideo:
|
||||
|
||||
def test_bad_video_id(self, youtube):
|
||||
video = youtube.video('not_a_valid_youtube_video_id')
|
||||
assert video is None
|
||||
|
||||
def test_title(self, video):
|
||||
assert video.title == "Me at the zoo"
|
||||
|
||||
def test_published_at(self, video):
|
||||
assert video.published_at.isoformat() == '2005-04-24T03:31:52+00:00'
|
||||
|
||||
def test_n_views(self, video):
|
||||
assert video.n_views > int(40e6)
|
||||
|
||||
def test_tags(self, video):
|
||||
assert video.tags == ['jawed', 'karim', 'elephant', 'zoo', 'youtube', 'first', 'video']
|
||||
|
||||
def test_duration(self, video):
|
||||
assert video.duration.total_seconds() == 19
|
||||
|
||||
|
||||
class TestChannel:
|
||||
|
||||
def test_title(self, channel):
|
||||
assert channel.title == "YouTube Help"
|
||||
|
||||
|
||||
class TestSearch:
|
||||
|
||||
def test_video_search_returns_a_video(self, video_search):
|
||||
assert isinstance(video_search[0], Video)
|
||||
|
||||
def test_video_search_has_many_results(self, video_search):
|
||||
# make video_search unlazy (populate pageInfo attributes)
|
||||
_ = video_search[0]
|
||||
assert video_search.total_results > 10000
|
||||
|
||||
def test_search_iteration(self, search):
|
||||
"""Simply iterate over a search, creating all resources, to check for exceptions."""
|
||||
for resource in search:
|
||||
log.debug(resource)
|
||||
|
||||
|
||||
class TestListResponse:
|
||||
|
||||
def test_if_iterable(self, search):
|
||||
assert isinstance(search, collections.Iterator)
|
||||
|
||||
def test_integer_indexing(self, search):
|
||||
assert isinstance(search[0], Resource)
|
||||
|
||||
def test_slice_indexing(self, search):
|
||||
assert isinstance(search[1:3], list)
|
||||
|
||||
def test_full_listing_iteration(self, video_search_array):
|
||||
"""Iterate over all search results to check no exceptions are raised when paging etc.
|
||||
|
||||
Even if millions of results are found, the API will never return more than 500 (by
|
||||
design), so we're okay to just bang right through the search results generator for the
|
||||
whole array of video searches.
|
||||
|
||||
"""
|
||||
for i, search in enumerate(video_search_array):
|
||||
c = 0
|
||||
for _ in search:
|
||||
c += 1
|
||||
|
||||
log.debug(f"checked first {c} results (search #{i})")
|
@ -11,3 +11,4 @@ A self-hosted tool which manages your YouTube subscriptions, and downloads files
|
||||
* google-api-python-client: `$ pip3 install google-api-python-client`
|
||||
* google_auth_oauthlib: `$ pip3 install google_auth_oauthlib`
|
||||
* apscheduler (v3.5+): `$ pip3 install apscheduler`
|
||||
* (recommended) oauth2client: `$ pip3 install oauth2client`
|
Loading…
x
Reference in New Issue
Block a user